def insert_processed_transaction(self, ev_hash_list, round, self_pub_key): """ Inserts processed tx (the one, that was included in final tx order) by event hash Should be used only in order.py :param ev_hash_list: list of events for which final order was found :type ev_hash_list: list :param round: round of all events, used later to generate state and clean db :type round: int :param private_key_seed: node private key seed needed to create and afterwards to check verify key :type private_key_seed: str :return: """ self.logger.debug( "insert_processed_transaction input ev_hash_list = %s, round = %s, pub_key = %s", str(ev_hash_list), str(round), str(self_pub_key)) tx_list = [] for event_hash in ev_hash_list: self.logger.debug( "insert_processed_transaction for ev with hash %s", str(event_hash)) event = Prisma().db.get_event(event_hash) Prisma().db.set_round_handled({event_hash: round}) self.logger.debug("insert_transaction_by_ev_hash event %s", str(event)) if not event: self.logger.error( "Could not insert tx, event there is no event !") return False if len(event.d) > 0: if event.c != self_pub_key.decode("utf-8"): for tx_hex in event.d: tx = self.parse_transaction_hex(tx_hex) # Money transfer if (tx and 'type' in tx and int(tx['type']) == TYPE_MONEY_TRANSFER and 'amount' in tx and 'senderId' in tx and 'recipientId' in tx): tx['tx_dict_hex'] = tx_hex tx['ev_hash'] = event_hash tx['round'] = round tx_list.append(tx) self.logger.debug("Insert money transfer tx %s", str(tx)) # State signature elif tx and 'type' in tx and int( tx['type']) == TYPE_SIGNED_STATE: # Handle new signatures that was crated by remote node self.logger.debug("Handle remote sign %s", str(tx)) Prisma().state_manager.handle_new_sign(tx) # Error else: self.logger.error( "Skipping malformed transaction data for event hash: %s", str(tx)) else: Prisma().db.set_transaction_round(event_hash, round) return Prisma().db.insert_transactions(tx_list)
def local_cryptograph_response(self, signed_event_response): """ Based on the get_event_response and the data generated in signed_event_response() on the remote peer we calculate a subset of events that the asking node does not know about. The two parents and their height are used below. Returns a dict; see crypto.py :param signed_event_response: signed remote events and last remote event time :type signed_event_response: dict :returns: signed local events or False if error :rtype: dict or bool """ head = Prisma().db.get_head() self.logger.debug("signed_event_response %s", str(signed_event_response)) self.logger.debug("head %s", str(head)) # cg is a list of event tuples, it should be a dict of tuples if head: cs = json.loads((self.crypto.verify_concatenated(signed_event_response)).decode('utf-8')) # cs are a dict subset = {h: Prisma().db.get_event(h) for h in self._cgc.bfs((head,), lambda u: (p for p in Prisma().db.get_event(u, clear_parent=True).p if Prisma().db.get_event(p).c not in cs or Prisma().db.get_height(p) > cs[Prisma().db.get_event(p).c]))} response = json.dumps((head, subset)) local_cryptograph_response_res = self.crypto.sign_data(response, self.keystore['privateKeySeed']) self.logger.debug("local_cryptograph_response_res %s", str(local_cryptograph_response_res)) return local_cryptograph_response_res return False
def create_transaction(to_address, amount, from_address=None, password=None): """ Created a transaction and saves it. :param to_address: :param amount: :param from_address: :param password: :return: the transaction stored """ if from_address: """ keystore = Prisma().wallet.decrypt_keystore(from_address, password) if not keystore: raise Exception('The wallet provided is invalid or is not stored in the system.') """ raise Exception( 'It\'s not possible to send from another wallet different than the main wallet atm. Sorry!' ) else: keystore = Prisma().graph.keystore transaction = Prisma().wallet.transaction.form_funds_tx( keystore, to_address, amount) Prisma().wallet.transaction_buffer_add(transaction) return { 'transaction_stored': Prisma().wallet.transaction.unhexlify_transaction(transaction) }
def add_event(self, blake2hash, ev): """ Save event to database :param blake2hash: event hash :type blake2hash: str :param ev: Event = named tuple containing : d p c t s :type ev: named tuple :return: True if successfully added and False otherwise :rtype: bool """ try: Prisma().graph.tbd.add(blake2hash) if ev.p == (): if not Prisma().db.insert_height({blake2hash: 0}): self.logger.error( "Could not add root event with blake2b hash %s.", str(blake2hash)) return False else: height_list = [] for p in ev.p: height_list.append(Prisma().db.get_height(p)) if not Prisma().db.insert_height( {blake2hash: max(height_list) + 1}): self.logger.debug( "Could not add new event with blake2b hash %s", str(blake2hash)) return False Prisma().db.insert_event({blake2hash: ev}) except Exception as e: self.logger.error("Could not add new event. Reason:", e) return False return True
def get_clean_remote_cg(self, remote_cg): """ Removes events from remote cg we have already signed or know. :param: remote cryptograph with events that we might know :type remote_cg: tuple :returns: remote cg without events that we have already sign or know :rtype: dict """ remote_cg = self._event.restore(remote_cg) for event_hash in list(remote_cg): event_round = Prisma().db.get_round(event_hash) if event_round == -1: self.logger.error("Could not clean remote cg: get event round ERROR !") return False if (event_round and event_round <= self.last_signed_state) \ or Prisma().db.get_event(event_hash, as_tuple=False): self.logger.debug("CLEANING DELETE hash = %s", str(event_hash)) del remote_cg[event_hash] self.logger.debug("Clean remote HG %s", str(remote_cg)) return remote_cg
def sync_genesis(self): """ Insert genesis state if it is not exist """ if not Prisma().db.get_state(-1): gen_state = Common().read_genesis_state() self.logger.debug("genesis_state: %s", str(gen_state)) Prisma().db.insert_state(gen_state['state'], gen_state['hash'], gen_state['signed'])
def get_my_balance(): """ Get the balance in the current node keystore :return: my balance """ address = Prisma().graph.keystore['address'] my_balance = Prisma().db.get_account_balance(address) return {'my_address': address, 'my_balance': my_balance}
def init_graph(self): self.last_signed_state = Prisma().db.get_consensus_last_signed() self.logger.debug("INIT last_signed_state: %s", str(self.last_signed_state)) is_cg_empty = self.init_events() self.restore_invariants(is_cg_empty) if is_cg_empty: self.sync_genesis() self.unsent_count = len(Prisma().db.get_consensus_greater_than( Prisma().db.get_consensus_last_created_sign()))
def send_get_peers(protocol): """ Prepare and send get_peers. :param protocol: """ request_data = { 'method': 'get_peers', '_id': Prisma().network.node_id, 'port': Prisma().network.listen_port, 'latest_event': Prisma().db.get_latest_event_time() } protocol.send_data(request_data)
def higher(a, b): """ Check if height of a bigger than height of b :param a: first event hash :type a: str :param b: second event hash :type b: str :return: is a higher :rtype: bool """ return a is not None and (b is None or Prisma().db.get_height(a) >= Prisma().db.get_height(b))
def list_transactions(): """ Returns a list of buffered transactions. :return: the transaction buffer unhexified. """ i = 0 data = {} if len(Prisma().wallet.transaction_buffer) == 0: return {'message': 'The buffer is empty.'} for transaction in Prisma().wallet.transaction_buffer: data[i] = Prisma().wallet.transaction.unhexlify_transaction( transaction) i += 1 return data
def create_transaction_and_send(to_address, amount): """ Creates a transaction and inserts it to the pool. :param to_address: address :param amount: int """ transaction = Prisma().wallet.transaction.form_funds_tx( Prisma().graph.keystore, to_address, amount) Prisma().wallet.transaction.insert_transactions_into_pool( [transaction]) return { 'transaction': Prisma().wallet.transaction.unhexlify_transaction(transaction) }
def last_event_time(): """ Returns the latest event time. :return: last event """ return {'latest_event_time': Prisma().db.get_latest_event_time()}
def __init__(self): self.logger = logging.getLogger('Protocol') self.validate = Validator() self.callLater = Prisma().callLater self.peer = None self.host = None self.d = None
def handle_get_peers_response(protocol, peers): """ Add peers from the response to the database. Then close connection. :param protocol: :param peers: """ for peer in peers: if Prisma().network.node_id != peer[ '_id'] and protocol.validate.is_valid_node_ip( peer['host']): Prisma().db.insert_peer(peer) # everything ok, so do the callback and close connection protocol.d.callback(None) protocol.close_connection()
def list_wallets(): """ Returns a list of wallet addresses stored. :return: a list of wallet addresses """ return {'addresses': Prisma().graph.wallet.list_wallets()}
def peer_list(): """ Returns a list with all the peers in our database. :return: a list of peers """ return {'peer_list': Prisma().db.get_peers_many()}
def peer_count(): """ Returns the number of connected peers. :return: a list of peers """ return {'peer_count': Prisma().db.count_peers()}
def is_client(self): """ If our port is not our listen port then we are the client. :return: bool """ return self.host.port != Prisma().network.listen_port
def __init__(self): # prepare websocket client self.info_peer_count = 0 self.info_last_event_time = False self.info_my_balance = False self.info_my_address = False self.block = False self.client_factory = ClientFactory(url='ws://127.0.0.1:{}'.format(Prisma().api.port)) self.client_factory.prompt = self self.client_connection = connectWS(self.client_factory) # prompt style self.print_style_green = style_from_dict({ Token.Text: '#00cc00', }) self.print_style_red = style_from_dict({ Token.Text: '#cc0000', }) self.prompt_style = style_from_dict({ Token.Pound: '#ffff00', Token.Toolbar: '#00ee00 bg:#333333', }) # prompt autocompletion words = [] for command in self.command_list: words.append(command['command']) self.completer = WordCompleter(words, ignore_case=True, sentence=True) # prompt memory self.history = FileHistory(expanduser('~/.prisma/history.txt'))
def insert_transactions_into_pool(self, tx_list): """ Prepares and inserts transactions into tx pool. They will be inserted into event as soon as it will be created. :param tx_list: list of finalized transactions :type tx_list: list :return: insertion result :rtype: bool """ if tx_list: prepared_tx_list = [] for tx_hex in tx_list: tx = self.parse_transaction_hex(tx_hex) if tx: tx['tx_dict_hex'] = tx_hex self.logger.debug("Prepared for pool tx %s", str(tx)) prepared_tx_list.append(tx) else: self.logger.error( "Skipping inserting malformed transaction %s", str(tx)) continue return Prisma().db.insert_transactions(prepared_tx_list) return False
def stop(self): """ Closes the client connection and stops the manager. """ self.print('Exiting, please wait...') self.client_connection.transport.protocol.sendClose() sleep(0.1) # this has to be replaced to a clean way to wait until sendClose() is finished. Prisma().stop()
def decrypt_wallet(address, password): decrypted_wallet = Prisma().graph.wallet.decrypt_keystore( address, password) return { 'address': decrypted_wallet['address'], 'public_key': decrypted_wallet['publicKey'].decode('utf-8'), 'private_key': decrypted_wallet['privateKeySeed'].decode('utf-8') }
def restore_invariants(self, is_cg_empty): """ Initializes cryptograph if it is empty :param is_cg_empty: is cryptograph empty (this arg func get from init_events) :return: None """ if is_cg_empty: h, ev = self._event.new_event([], ()) self._event.add_event(h, ev) Prisma().db.insert_round({h: 0}) Prisma().db.insert_witness({0: {ev.c: h}}) Prisma().db.insert_can_see({h: {ev.c: h}}) Prisma().db.insert_head(h) else: self.logger.debug("Reconnect")
def connection_error(reason): # in case of error remove the peer from the database addr = random_peer['host'] + ':' + str(random_peer['port']) self.logger.debug('Error while connecting to {0}: {1}'.format( addr, reason.getErrorMessage())) Prisma().db.delete_peer(random_peer['_id']) # then call later again self.callLater(0, lambda: self.download_state_from_random_peer())
def download_state_from_random_peer(self): """ This will get the state of a random peer. """ random_peer = Prisma().db.get_random_peer() # if list is empty if not random_peer: self.logger.info( 'No peers to connect to. Wait to some peer to connect with you or restart with a peer.' ) self.callLater(2, lambda: self.download_state_from_random_peer()) return random_peer = random_peer.pop() host = random_peer['host'] port = random_peer['port'] client = TCP4ClientEndpoint(self.reactor, host, port, self.timeout) d = client.connect(self.factory) # in case of connection ok, add the callbacks for get_state def connection_ok(protocol): protocol.d = defer.Deferred() def get_state_ok(_): self.logger.info( 'Successfully got the state from {0}:{1}'.format( host, port)) self.status = STATUS_READY protocol.d.addCallback(get_state_ok) def get_state_error(reason): error_message = reason.getErrorMessage() self.logger.error( 'Error when getting state from {0}:{1}: {2}'.format( host, port, error_message)) protocol.close_connection() self.callLater(0, lambda: self.download_state_from_random_peer()) protocol.d.addErrback(get_state_error) protocol.send_get_state() d.addCallback(connection_ok) # in case of connection error show in debug and try again def connection_error(reason): # in case of error remove the peer from the database addr = random_peer['host'] + ':' + str(random_peer['port']) self.logger.debug('Error while connecting to {0}: {1}'.format( addr, reason.getErrorMessage())) Prisma().db.delete_peer(random_peer['_id']) # then call later again self.callLater(0, lambda: self.download_state_from_random_peer()) d.addErrback(connection_error)
def strongly_see(self, h, r): """ Get nodes that given event can strongly see :param h: event hash :type h: str :param r: round :type r: int :return: nodes that can strongly see that event :rtype: set """ self.logger.debug("strongly_see start h = %s, r = %s", str(h), str(r)) self.logger.debug("Witneeses on round r %s", str(Prisma().db.get_witness(r))) hits = defaultdict(int) for c, k in Prisma().db.get_can_see(h).items(): self.logger.debug("strongly_see k = %s ", str(k)) self.logger.debug("strongly_see k (round) = %s", str(Prisma().db.get_round(k))) if Prisma().db.get_round(k) == r: for c_, k_ in Prisma().db.get_can_see(k).items(): self.logger.debug("strongly_see k______ = %s ", str(k_)) self.logger.debug("strongly_see k______(round) = %s ", str(Prisma().db.get_round(k_))) if Prisma().db.get_round(k_) == r: # TODO change it to node stake hits[c_] += 1 self.logger.debug("strongly_see hits = %s", str(hits)) res = set() for c, n in hits.items(): if n >= self.graph.min_s: res.add(c) self.logger.debug("strongly_see res %s", str(res)) return res
def get_peers_from_random_peer(self): """ Gets a random a peer from the database and connects to it and asks for peers. """ # get a random peer from database random_peer = Prisma().db.get_random_peer() # if list is empty if not random_peer: self.logger.info( 'No peers to connect to. Wait to some peer to connect with you or restart with a peer.' ) return random_peer = random_peer.pop() host = random_peer['host'] port = random_peer['port'] client = TCP4ClientEndpoint(self.reactor, host, port, self.timeout) d = client.connect(self.factory) # in case of connection ok, add the callbacks for get_peers and call send_get_peers def connection_ok(protocol): protocol.d = defer.Deferred() def get_peers_ok(_): self.logger.info('Successfully got peers from {0}:{1}'.format( host, port)) protocol.d.addCallback(get_peers_ok) def get_peers_error(reason): error_message = reason.getErrorMessage() self.logger.error( 'Error when getting peers from {0}:{1}: {2}'.format( host, port, error_message)) protocol.close_connection() self.get_peers_lc.reset() self.get_peers_from_random_peer() protocol.d.addErrback(get_peers_error) protocol.send_get_peers() d.addCallback(connection_ok) # in case of connection error show in debug and try again def connection_error(reason): # in case of error remove the peer from the database addr = random_peer['host'] + ':' + str(random_peer['port']) self.logger.debug('Error while connecting to {0}: {1}'.format( addr, reason.getErrorMessage())) Prisma().db.delete_peer(random_peer['_id']) # then restart timer and try again get_peers_from_random_peer self.get_peers_lc.reset() self.get_peers_from_random_peer() d.addErrback(connection_error)
def connection_error(reason): # in case of error remove the peer from the database addr = random_peer['host'] + ':' + str(random_peer['port']) self.logger.debug('Error while connecting to {0}: {1}'.format( addr, reason.getErrorMessage())) Prisma().db.delete_peer(random_peer['_id']) # then restart timer and try again get_peers_from_random_peer self.get_events_lc.reset() self.get_events_from_random_peer()
def get_address_balance(address): """ Returns the balance in an address. :param address: :return: balance """ balance = Prisma().db.get_account_balance(address) return {'address': address, 'balance': balance}