class bitcoinNode(cryptoNode): ############################################################################ def __init__(self, symbol, rpc_address, rpc_user, rpc_pass): super().__init__(symbol, rpc_address, rpc_user, rpc_pass) self.proxy = Proxy('http://%s:%s@%s' % (rpc_user, rpc_pass, rpc_address)) ############################################################################ def __getattr__(self, method): return getattr(self.proxy, method) ############################################################################ def initialise(self): try: info = self.proxy.getnetworkinfo() except ValueError: raise cryptoNodeException( 'Failed to connect - error in rpcuser or rpcpassword for {} daemon' .format(self.symbol)) except pycurl.error: raise cryptoNodeException( 'Failed to connect - check that {} daemon is running'.format( self.symbol)) except exc.RpcInWarmUp: raise cryptoNodeException( 'Failed to connect - {} daemon is starting but not ready - try again after 60 seconds' .format(self.symbol)) except exc.RpcMethodNotFound: raise cryptoNodeException( 'RPC getnetworkinfo unavailable for {} daemon'.format( self.symbol)) try: zmqnotifications = self.proxy.getzmqnotifications() except pycurl.error: raise cryptoNodeException( 'Blockchain node for {} not available or incorrectly configured' .format(self.symbol)) except (exc.RpcMethodNotFound, ValueError): zmqnotifications = [] for zmqnotification in zmqnotifications: if zmqnotification['type'] == 'pubhashblock': self.zmqAddress = zmqnotification['address'] ############################################################################ def refresh(self): self.blocks = self.proxy.getblockcount() self.peers = self.proxy.getconnectioncount() ############################################################################ def get_balance(self): try: result = self.proxy.getbalance() except exc.RpcException as error: raise cryptoNodeException('{} daemon returned error: {}'.format( self.symbol, str(error))) else: return result ############################################################################ def get_unlocked_balance(self): try: result = self.proxy.getbalance() except exc.RpcException as error: raise cryptoNodeException('{} daemon returned error: {}'.format( self.symbol, str(error))) else: return result ############################################################################ def get_unconfirmed_balance(self): try: result = self.proxy.getunconfirmedbalance() except exc.RpcException as error: raise cryptoNodeException('{} daemon returned error: {}'.format( self.symbol, str(error))) else: return result ############################################################################ def get_new_address(self): return self.proxy.getnewaddress() ############################################################################ def wallet_locked(self, cache_prior_state=False): info = self.proxy.getwalletinfo() if 'unlocked_until' in info: return info['unlocked_until'] == 0 return False ############################################################################ def unlock_wallet(self, passphrase, seconds, staking=False): try: self.proxy.walletpassphrase(passphrase, seconds) except exc.RpcWalletPassphraseIncorrect: return False else: return True ############################################################################ def revert_wallet_lock(self): pass ############################################################################ def send_to_address(self, address, amount, comment): try: txid = self.proxy.sendtoaddress(address, amount, comment) except exc.RpcWalletUnlockNeeded: raise cryptoNodeException('Wallet locked - please unlock') except exc.RpcWalletInsufficientFunds: raise cryptoNodeException('Insufficient funds in wallet') except exc.RpcTypeError: raise cryptoNodeException('Invalid amount') except exc.RpcWalletError: raise cryptoNodeException('Amount too small') else: return txid ############################################################################ def shutdown(self): pass
class eccoinNode(cryptoNode): version_min = 30000 version_max = 99999 win_zmq_bug = 30300 version_fPacketSig = 30300 serviceIdx = count(start=1) respondIdx = count(start=1) ############################################################################ def __init__(self, symbol, rpc_address, rpc_user, rpc_pass, service_id=1, respond_id=None): super().__init__(symbol, rpc_address, rpc_user, rpc_pass) self.proxy = Proxy( 'http://%s:%s@%s' % (rpc_user, rpc_pass, rpc_address)) # Not thread safe ... self.prox2 = Proxy( 'http://%s:%s@%s' % (rpc_user, rpc_pass, rpc_address)) # ... hence needing 2 self.serviceId = service_id self.respondId = respond_id self.serviceKey = '' self.respondKey = '' self.routingTag = '' self.ecresolve_tags = [] # ECC feature flags (based on version number) self.fPacketSig = False # Caching prior unlock state for post transaction reversion self.cached_prior_walletinfo = {} ############################################################################ def __getattr__(self, method): return getattr(self.proxy, method) ############################################################################ def initialise(self): try: info = self.proxy.getnetworkinfo() except ValueError: raise cryptoNodeException( 'Failed to connect - error in rpcuser or rpcpassword for eccoin' ) except pycurl.error: raise cryptoNodeException( 'Failed to connect - check that eccoin daemon is running') except exc.RpcInWarmUp: raise cryptoNodeException( 'Failed to connect - eccoin daemon is starting but not ready - try again after 60 seconds' ) except exc.RpcMethodNotFound: raise cryptoNodeException( 'RPC getnetworkinfo unavailable for {} daemon'.format( self.symbol)) # ECC daemon version checking and feature enablement if not self.version_min <= info['version'] <= self.version_max: raise cryptoNodeException( 'eccoind version {} not supported - please run a version in the range {}-{}' .format(info['version'], self.version_min, self.version_max)) if (info['version'] == self.win_zmq_bug) and (sys.platform in ['win32', 'cygwin']): raise cryptoNodeException( 'eccoind version {} not supported on Windows - please upgrade'. format(info['version'])) self.fPacketSig = info['version'] >= self.version_fPacketSig # ECC messaging buffer setup try: self.routingTag = self.proxy.getroutingpubkey() if self.serviceId: self.serviceKey = self.proxy.registerbuffer(self.serviceId) if self.respondId: self.respondKey = self.proxy.registerbuffer(self.respondId) except exc.RpcInternalError: raise cryptoNodeException( 'API Buffer was not correctly unregistered or another instance running - try again after 60 seconds' ) # ZMQ detection and configuration loading try: zmqnotifications = self.proxy.getzmqnotifications() except pycurl.error: raise cryptoNodeException( 'Blockchain node for {} not available or incorrectly configured' .format(self.symbol)) except (exc.RpcMethodNotFound, ValueError): zmqnotifications = [] for zmqnotification in zmqnotifications: if zmqnotification['type'] == 'pubhashblock': self.zmqAddress = zmqnotification['address'] # Load ecresolve routing tags and setup routes for subsequent ecc network name resolution self.ecresolve_tags = self.get_ecresolve_tags() route = [] for tag in self.ecresolve_tags: try: self.proxy.findroute(tag) route.append(self.proxy.haveroute(tag)) except exc.RpcInvalidAddressOrKey: raise cryptoNodeException( 'Routing tag for ecresolve has invalid base64 encoding : {}' .format(tag)) if not any(route): raise cryptoNodeException( 'No route available to ecresolve across all {} configured routing tags' .format(len(self.ecresolve_tags))) ############################################################################ def refresh(self): self.blocks = self.proxy.getblockcount() self.peers = self.proxy.getconnectioncount() ############################################################################ def get_balance(self): return self.proxy.getbalance() ############################################################################ def get_unlocked_balance(self): return self.proxy.getbalance() ############################################################################ def get_unconfirmed_balance(self): return self.proxy.getunconfirmedbalance() ############################################################################ def get_new_address(self): return self.proxy.getnewaddress() ############################################################################ def wallet_locked(self, cache_prior_state=False): info = self.proxy.getwalletinfo() if cache_prior_state and info.keys() >= { 'unlocked_until', 'staking_only_unlock' }: self.cached_prior_walletinfo = info.copy() if 'unlocked_until' in info: if 'staking_only_unlock' in info: return info['staking_only_unlock'] or (info['unlocked_until'] == 0) else: return info['unlocked_until'] == 0 return False # unencrypted wallet ############################################################################ def unlock_wallet(self, passphrase, seconds, staking=False): try: self.proxy.walletpassphrase(passphrase, seconds, staking) except exc.RpcWalletPassphraseIncorrect: return False else: # Cache passphrase for later call to revert_wallet_lock if self.cached_prior_walletinfo: self.cached_prior_walletinfo['passphrase'] = passphrase return True ############################################################################ def revert_wallet_lock(self): if self.cached_prior_walletinfo and 'passphrase' in self.cached_prior_walletinfo: if self.cached_prior_walletinfo['unlocked_until'] > 0: seconds = self.cached_prior_walletinfo['unlocked_until'] - int( time.time()) self.unlock_wallet( self.cached_prior_walletinfo['passphrase'], seconds, self.cached_prior_walletinfo['staking_only_unlock']) self.cached_prior_walletinfo.clear() ############################################################################ def send_to_address(self, address, amount, comment): try: txid = self.proxy.sendtoaddress(address, amount, comment) except exc.RpcWalletUnlockNeeded: raise cryptoNodeException('Wallet locked - please unlock') except exc.RpcWalletInsufficientFunds: raise cryptoNodeException('Insufficient funds in wallet') except exc.RpcTypeError: raise cryptoNodeException('Invalid amount') except exc.RpcWalletError: raise cryptoNodeException('Amount too small') else: return txid ############################################################################ def reset_service_buffer_timeout(self): if self.serviceKey: try: bufferSig = self.prox2.buffersignmessage( self.serviceKey, 'ResetBufferTimeout') self.prox2.resetbuffertimeout(self.serviceId, bufferSig) except pycurl.error: self.serviceKey = '' raise cryptoNodeException( 'Failed to connect - check that eccoin daemon is running') return True return False ############################################################################ def reset_respond_buffer_timeout(self): if self.respondKey: try: bufferSig = self.prox2.buffersignmessage( self.respondKey, 'ResetBufferTimeout') self.prox2.resetbuffertimeout(self.respondId, bufferSig) except pycurl.error: self.serviceKey = '' raise cryptoNodeException( 'Failed to connect - check that eccoin daemon is running') return True return False ############################################################################ def reset_buffer_timeouts(self): serviceResult = self.reset_service_buffer_timeout() respondResult = self.reset_respond_buffer_timeout() return serviceResult or respondResult # OR semantics because the return value is used to gate timer setup ############################################################################ def get_ecresolve_tags(self): domain = 'ecchat.io' # TODO - Make this daemon version dependent ref new RPC tags = [] try: resolved = dns.resolver.resolve(domain, 'TXT') except: raise cryptoNodeException( 'Error while resolving ecresolve routing tags from {} TXT record' .format(domain)) for entry in resolved: decoded_entry = entry.to_text()[1:-1].split('=', 1) if (len(decoded_entry) == 2) and (decoded_entry[0] == 'ecresolve'): tags.append(decoded_entry[1]) return tags ############################################################################ def setup_route(self, targetRoute): try: self.proxy.findroute(targetRoute) isRoute = self.proxy.haveroute(targetRoute) except exc.RpcInvalidAddressOrKey: raise cryptoNodeException( 'Routing tag has invalid base64 encoding : {}'.format( targetRoute)) if not isRoute: raise cryptoNodeException( 'No route available to : {}'.format(targetRoute)) ############################################################################ def send_packet(self, dest_key, protocol_id, data): if self.fPacketSig: signature = self.proxy.tagsignmessage(data) self.proxy.sendpacket(dest_key, protocol_id, data, self.routingTag, signature) else: self.proxy.sendpacket(dest_key, protocol_id, data) ############################################################################ def get_service_buffer(self): if self.serviceKey: bufferCmd = 'GetBufferRequest:' + str(self.serviceId) + str( next(self.serviceIdx)) bufferSig = self.proxy.buffersignmessage(self.serviceKey, bufferCmd) eccbuffer = self.proxy.getbuffer(self.serviceId, bufferSig) return eccbuffer else: return None ############################################################################ def get_respond_buffer(self): if self.respondKey: bufferCmd = 'GetBufferRequest:' + str(self.respondId) + str( next(self.respondIdx)) bufferSig = self.proxy.buffersignmessage(self.respondKey, bufferCmd) eccbuffer = self.proxy.getbuffer(self.respondId, bufferSig) return eccbuffer else: return None ############################################################################ def get_buffer(self, protocol_id=1): if protocol_id == self.serviceId: return self.get_service_buffer() if protocol_id == self.respondId: return self.get_respond_buffer() return None ############################################################################ def shutdown(self): if self.serviceKey: bufferSig = self.proxy.buffersignmessage(self.serviceKey, 'ReleaseBufferRequest') self.proxy.releasebuffer(self.serviceId, bufferSig) self.serviceKey = '' if self.respondKey: bufferSig = self.proxy.buffersignmessage(self.respondKey, 'ReleaseBufferRequest') self.proxy.releasebuffer(self.respondId, bufferSig) self.respondKey = ''