Something went wrong on our end
'''
Created on 1 févr. 2014
@author: inso
'''
from ucoinpy.api import bma
from ucoinpy import PROTOCOL_VERSION
from ucoinpy.documents.peer import Peer, Endpoint, BMAEndpoint
from ucoinpy.documents.block import Block
from ..tools.exceptions import NoPeerAvailable
import logging
import inspect
import hashlib
import re
from requests.exceptions import RequestException, Timeout
class Cache():
def __init__(self, community):
self.latest_block = 0
self.community = community
self.data = {}
def refresh(self):
self.latest_block = self.community.current_blockid()['number']
self.data = {}
def request(self, request, req_args={}, get_args={}):
cache_key = (hash(request),
hash(tuple(frozenset(sorted(req_args.keys())))),
hash(tuple(frozenset(sorted(req_args.items())))),
hash(tuple(frozenset(sorted(get_args.keys())))),
hash(tuple(frozenset(sorted(get_args.items())))))
if cache_key not in self.data.keys():
result = self.community.request(request, req_args, get_args,
cached=False)
# Do not cache block 0
if self.latest_block == 0:
return result
else:
self.data[cache_key] = result
return self.data[cache_key]
else:
return self.data[cache_key]
class Community(object):
'''
classdocs
'''
def __init__(self, currency, peers):
'''
A community is a group of nodes using the same currency.
'''
self.currency = currency
self.peers = [p for p in peers if p.currency == currency]
self._cache = Cache(self)
self._cache.refresh()
@classmethod
def create(cls, currency, peer):
community = cls(currency, [peer])
logging.debug("Creating community")
community.peers = community.peering()
logging.debug("{0} peers found".format(len(community.peers)))
logging.debug([peer.pubkey for peer in community.peers])
return community
@classmethod
def load(cls, json_data):
peers = []
currency = json_data['currency']
for data in json_data['peers']:
for e in data['endpoints']:
if Endpoint.from_inline(e) is not None:
endpoint = Endpoint.from_inline(e)
try:
peering = bma.network.Peering(endpoint.conn_handler())
peer_data = peering.get()
peer = Peer.from_signed_raw("{0}{1}\n".format(peer_data['raw'],
peer_data['signature']))
peers.append(peer)
break
except:
pass
community = cls(currency, peers)
logging.debug("Creating community")
community.peers = community.peering()
logging.debug("{0} peers found".format(len(community.peers)))
logging.debug([peer.pubkey for peer in community.peers])
return community
@classmethod
def without_network(cls, json_data):
peers = []
currency = json_data['currency']
for data in json_data['peers']:
endpoints = []
for e in data['endpoints']:
endpoints.append(Endpoint.from_inline(e))
peer = Peer(PROTOCOL_VERSION, currency, data['pubkey'],
"0-DA39A3EE5E6B4B0D3255BFEF95601890AFD80709",
endpoints, None)
peers.append(peer)
community = cls(currency, peers)
return community
def name(self):
return self.currency
def __eq__(self, other):
return (other.currency == self.currency)
@property
def short_currency(self):
words = re.split('[_\W]+', self.currency)
shortened = ""
if len(words) > 1:
shortened = ''.join([w[0] for w in words])
else:
vowels = ('a', 'e', 'i', 'o', 'u', 'y')
shortened = self.currency
shortened = ''.join([c for c in shortened if c not in vowels])
return shortened
@property
def currency_symbol(self):
letter = self.currency[0]
u = ord('\u24B6') + ord(letter) - ord('A')
return chr(u)
def dividend(self):
ud = self.request(bma.blockchain.UD)
if len(ud['result']['blocks']) > 0:
block_number = ud['result']['blocks'][-1]
block = self.request(bma.blockchain.Block,
req_args={'number': block_number})
return block['dividend']
else:
return 1
@property
def monetary_mass(self):
try:
block = self.request(bma.blockchain.Current)
return block['monetaryMass']
except ValueError as e:
if '404' in e:
return 0
@property
def nb_members(self):
try:
block = self.request(bma.blockchain.Current)
return block['membersCount']
except ValueError as e:
if '404' in e:
return 0
def _peering_traversal(self, peer, found_peers, traversed_pubkeys):
logging.debug("Read {0} peering".format(peer.pubkey))
traversed_pubkeys.append(peer.pubkey)
if peer.currency == self.currency and \
peer.pubkey not in [p.pubkey for p in found_peers]:
found_peers.append(peer)
try:
e = next(e for e in peer.endpoints if type(e) is BMAEndpoint)
next_peers = bma.network.peering.Peers(e.conn_handler()).get()
for p in next_peers:
next_peer = Peer.from_signed_raw("{0}{1}\n".format(p['value']['raw'],
p['value']['signature']))
logging.debug(traversed_pubkeys)
logging.debug("Traversing : next to read : {0} : {1}".format(next_peer.pubkey,
(next_peer.pubkey not in traversed_pubkeys)))
if next_peer.pubkey not in traversed_pubkeys:
self._peering_traversal(next_peer, found_peers, traversed_pubkeys)
except Timeout:
pass
except ConnectionError:
pass
except ValueError:
pass
except RequestException as e:
pass
def peering(self):
peers = []
traversed_pubkeys = []
for p in self.peers:
logging.debug(traversed_pubkeys)
logging.debug("Peering : next to read : {0} : {1}".format(p.pubkey,
(p.pubkey not in traversed_pubkeys)))
if p.pubkey not in traversed_pubkeys:
self._peering_traversal(p, peers, traversed_pubkeys)
logging.debug("Peers found : {0}".format(peers))
return peers
def get_block(self, number=None):
if number is None:
data = self.request(bma.blockchain.Current)
else:
data = self.request(bma.blockchain.Block,
req_args={'number': number})
return Block.from_signed_raw("{0}{1}\n".format(data['raw'],
data['signature']))
def current_blockid(self):
try:
block = self.request(bma.blockchain.Current, cached=False)
signed_raw = "{0}{1}\n".format(block['raw'], block['signature'])
block_hash = hashlib.sha1(signed_raw.encode("ascii")).hexdigest().upper()
block_number = block['number']
except ValueError as e:
if '404' in str(e):
block_number = 0
block_hash = "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709"
else:
raise
return {'number': block_number, 'hash': block_hash}
def members_pubkeys(self):
'''
Listing members pubkeys of a community
'''
memberships = self.request(bma.wot.Members)
members = []
logging.debug(memberships)
for m in memberships["results"]:
members.append(m['pubkey'])
return members
def refresh_cache(self):
self._cache.refresh()
def request(self, request, req_args={}, get_args={}, cached=True):
if cached:
return self._cache.request(request, req_args, get_args)
else:
for peer in self.peers.copy():
e = next(e for e in peer.endpoints if type(e) is BMAEndpoint)
try:
req = request(e.conn_handler(), **req_args)
data = req.get(**get_args)
if inspect.isgenerator(data):
generated = []
for d in data:
generated.append(d)
return generated
else:
return data
except ValueError as e:
if '502' in str(e):
continue
else:
raise
except Timeout:
# Move the timeout peer to the end
self.peers.remove(peer)
self.peers.append(peer)
continue
raise NoPeerAvailable(self.currency, len(self.peers))
def post(self, request, req_args={}, post_args={}):
for peer in self.peers:
e = next(e for e in peer.endpoints if type(e) is BMAEndpoint)
logging.debug("Trying to connect to : " + peer.pubkey)
req = request(e.conn_handler(), **req_args)
try:
req.post(**post_args)
return
except ValueError as e:
raise
except Timeout:
# Move the timeout peer to the end
self.peers.remove(peer)
self.peers.append(peer)
continue
except:
raise
raise NoPeerAvailable(self.currency, len(self.peers))
def broadcast(self, request, req_args={}, post_args={}):
tries = 0
ok = False
value_error = None
for peer in self.peers:
e = next(e for e in peer.endpoints if type(e) is BMAEndpoint)
logging.debug("Trying to connect to : " + peer.pubkey)
req = request(e.conn_handler(), **req_args)
try:
req.post(**post_args)
ok = True
except ValueError as e:
value_error = e
continue
except Timeout:
tries = tries + 1
# Move the timeout peer to the end
self.peers.remove(peer)
self.peers.append(peer)
continue
except:
raise
if not ok:
raise value_error
if tries == len(self.peers):
raise NoPeerAvailable(self.currency, len(self.peers))
def jsonify_peers_list(self):
data = []
for peer in self.peers:
endpoints_data = []
for e in peer.endpoints:
endpoints_data.append(e.inline())
data.append({'endpoints': endpoints_data,
'pubkey': peer.pubkey})
return data
def jsonify(self):
data = {'currency': self.currency,
'peers': self.jsonify_peers_list()}
return data
def get_parameters(self):
return self.request(bma.blockchain.Parameters)