Beispiel #1
0
    def on_register(self):
        self.db = self.engine.db
        self.blockchain = self.engine.blockchain
        self.account = self.engine.account

        if not self.db.exists('node_id'):
            self.db.put('node_id', str(uuid.uuid4()))

        self.node_id = self.db.get('node_id')

        try:
            self.s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            self.s.settimeout(2)
            self.s.bind(('0.0.0.0', self.engine.config['port']['peers']))
            self.s.listen(10)
            print("Started Peer Listen on 0.0.0.0:{}".format(
                self.engine.config['port']['peers']))
            return True
        except Exception as e:
            tools.log("Could not start Peer Receive socket!")
            tools.log(e)
            sys.stderr.write(str(e) + '\n')
            return False
 def listen(self):
     try:
         client_sock, address = self.s.accept()
         response, leftover = ntwrk.receive(client_sock)
         if response.getFlag():
             message = Message.from_yaml(response.getData())
             request = message.get_body()
             try:
                 if hasattr(self, request['action']) \
                         and request['version'] == custom.version \
                         and message.get_header("node_id") != self.node_id:
                     kwargs = copy.deepcopy(request)
                     if request['action'] == 'greetings':
                         kwargs['__remote_ip__'] = client_sock.getpeername()
                     elif request['action'] == 'push_block':
                         kwargs['node_id'] = message.get_header("node_id")
                     del kwargs['action']
                     del kwargs['version']
                     result = getattr(self, request['action'])(**kwargs)
                 else:
                     result = 'Received action is not valid'
             except:
                 result = 'Something went wrong while evaluating.\n'
                 tools.log(sys.exc_info())
             response = Message(headers={'ack': message.get_header('id'),
                                         'node_id': self.node_id},
                                body=result)
             ntwrk.send(response, client_sock)
             client_sock.close()
     except Exception as e:
         import time
         time.sleep(0.1)
Beispiel #3
0
    def on_register(self):
        # TODO: Add authentication support for redis
        try:
            redis_instance = redis.StrictRedis(
                host=os.environ.get('REDIS_URL', 'localhost'),
                db=self.engine.config['database']['index'])
            redis_instance.ping()
            self.DB = RedisStore(redis_instance)
        except Exception as e:
            tools.log(e)
            sys.stderr.write(
                'Redis connection cannot be established!\nFalling to SQLAlchemy'
            )
            return False

        try:
            self.salt = self.DB.get('salt').decode()
            if self.salt is None:
                raise Exception
        except Exception as e:
            self.salt = ''.join(
                random.choice(string.ascii_uppercase + string.digits)
                for _ in range(5))
            self.DB.put('salt', self.salt.encode())
        return True
Beispiel #4
0
 def target(_candidate_block, queue):
     # Miner registered but no work is sent yet.
     import copy
     candidate_block = copy.deepcopy(_candidate_block)
     try:
         if candidate_block is None:
             return
         if 'nonce' in candidate_block:
             candidate_block.pop('nonce')
         halfHash = tools.det_hash(candidate_block)
         candidate_block['nonce'] = random.randint(
             0, 10000000000000000000000000000000000000000)
         current_hash = tools.det_hash({
             'nonce': candidate_block['nonce'],
             'halfHash': halfHash
         })
         while current_hash > candidate_block['target']:
             candidate_block['nonce'] += 1
             current_hash = tools.det_hash({
                 'nonce': candidate_block['nonce'],
                 'halfHash': halfHash
             })
         if current_hash <= candidate_block['target']:
             queue.put(candidate_block)
     except Exception as e:
         tools.log('miner f****d up' + str(e))
         pass
Beispiel #5
0
    def block_integrity_check(block):
        if not isinstance(block, dict):
            tools.log('Block is not a dict')
            return False

        if 'error' in block:
            tools.log('Errors in block')
            return False

        if not ('length' in block and isinstance(block['length'], int)):
            return False

        if 'version' not in block or block['version'] != custom.version:
            return False

        if 'target' not in block:
            tools.log('no target in block')
            return False

        if 'time' not in block:
            tools.log('no time')
            return False

        if block['time'] > time.time() + 60 * 6:
            tools.log('Received block is coming from future. Call the feds')
            return False

        return True
Beispiel #6
0
 def rollback(self):
     if not self.simulating:
         tools.log('There isn\'t any ongoing simulation')
         return False
     self.log = dict()
     self.simulating = False
     return True
 def insider(*args, **kwargs):
     while self.__threads[func.__name__]["running"]:
         try:
             func(*args, **kwargs)
         except Exception as e:
             tools.log('Exception occurred at thread {}\n{}'.format(
                 func.__name__, traceback.format_exc()))
     return 0
    def register(self):
        def service_target(service):
            service.set_state(Service.RUNNING)
            while service.get_state() == Service.RUNNING:
                try:
                    order = service.into_service_queue.get(timeout=1)
                    if isinstance(order, Order):
                        result = Service.execute_order(service, order)
                        self.service_responses[order.id] = result
                        self.signals[order.id].set()
                    service.into_service_queue.task_done()
                except TypeError:
                    service.set_state(Service.STOPPED)
                    self.service_responses[order.id] = True
                    self.signals[order.id].set()
                except queue.Empty:
                    pass

        def threaded_wrapper(func):
            def insider(*args, **kwargs):
                while self.__threads[func.__name__]["running"]:
                    try:
                        func(*args, **kwargs)
                    except Exception as e:
                        tools.log('Exception occurred at thread {}\n{}'.format(
                            func.__name__, traceback.format_exc()))
                return 0

            return insider

        cont = self.on_register()
        if not cont:
            tools.log("Service is not going to continue with registering!")
            return False

        # Start event loop
        self.event_thread = threading.Thread(target=service_target,
                                             args=(self, ),
                                             name=self.name)
        self.event_thread.start()

        # Start all side-threads
        for clsMember in self.__class__.__dict__.values():
            if hasattr(
                    clsMember,
                    "decorator") and clsMember.decorator == threaded.__name__:
                new_thread = threading.Thread(
                    target=threaded_wrapper(clsMember._original),
                    args=(self, ),
                    name=clsMember._original.__name__)
                self.__threads[clsMember._original.__name__] = {
                    "running": True,
                    "thread": new_thread
                }
                new_thread.start()

        return True
 def __init__(self, engine):
     self.engine = engine
     self.DB = None
     self.blockchain = None
     try:
         db_location = os.path.join(self.engine.working_dir, 'client.db')
         DB = plyvel.DB(db_location, create_if_missing=True)
         self.DB = DB.prefixed_db(custom.version.encode())
     except Exception as e:
         tools.log(e)
         sys.stderr.write('Database connection cannot be established!\n')
Beispiel #10
0
 def set_state(self, state):  # (INIT|RUNNING|STOPPED|TERMINATED) -> ()
     """
     Set the current state of the service.
     This should never be used outside of the service.
     Treat as private method.
     :param state: New state
     :return: None
     """
     if state == Service.STOPPED or state == Service.TERMINATED:
         tools.log('{} got stopped'.format(self.__class__.__name__))
         for thread_name in self.__threads.keys():
             self.__threads[thread_name]["running"] = False
     self.__state = state
Beispiel #11
0
 def commit(self):
     """
     Commit simply erases the earlier snapshot.
     :return:
     """
     if not self.simulating:
         tools.log('There isn\'t any ongoing simulation')
         return False
     for key, value in self.log.items():
         self.DB.put(str(key).encode(), pickle.dumps(value))
     self.log = dict()
     self.simulating = False
     return True
Beispiel #12
0
    def on_register(self):
        print('Starting halocoin')
        if not self.db.register():
            return False

        print("Firing up the Database")
        time.sleep(0.1)

        if not test_database(self.db):
            tools.log("Database service is not working.")
            return False

        b = self.db.get('init')
        if not b:
            print("Initializing records")
            self.db.put('init', True)
            self.db.put('length', -1)
            self.db.put('txs', [])
            self.db.put('peer_list', [])
            self.db.put('targets', {})
            self.db.put('times', {})
            self.db.put('mine', False)
            self.db.put('diffLength', '0')
            self.db.put('accounts', {})
            self.db.put('known_length', -1)
        self.db.put('stop', False)

        if not self.account.register():
            sys.stderr.write("Account service has failed. Exiting!\n")
            self.unregister_sub_services()
            return False

        if not self.blockchain.register():
            sys.stderr.write("Blockchain service has failed. Exiting!\n")
            self.unregister_sub_services()
            return False

        if not self.peer_receive.register():
            sys.stderr.write("Peer Receive service has failed. Exiting!\n")
            self.unregister_sub_services()
            return False

        if not self.peers_check.register():
            sys.stderr.write("Peers Check service has failed. Exiting!\n")
            self.unregister_sub_services()
            return False

        api.run()

        return True
Beispiel #13
0
 def set_default_wallet(self, wallet_name, password):
     try:
         from halocoin.model.wallet import Wallet
         encrypted_wallet_content = self.get_wallet(wallet_name)
         wallet = Wallet.from_string(
             tools.decrypt(password, encrypted_wallet_content))
         if wallet.name == wallet_name:
             self.db.put('default_wallet', {
                 "wallet_name": wallet_name,
                 "password": password
             })
             return True
         else:
             return False
     except Exception as e:
         tools.log(e)
         return False
Beispiel #14
0
 def __init__(self, engine, dbname):
     self.engine = engine
     self.dbname = dbname
     self.DB = None
     self.iterator = None
     self.simulating = False
     self.simulation_owner = ''
     self.salt = None
     self.req_count = 0
     self.log = dict()
     try:
         db_location = os.path.join(self.engine.working_dir, self.dbname)
         DB = plyvel.DB(db_location, create_if_missing=True)
         self.DB = DB.prefixed_db(custom.version.encode())
         self.iterator = self.DB.iterator
     except Exception as e:
         tools.log(e)
         sys.stderr.write('Database connection cannot be established!\n')
Beispiel #15
0
    def simulate(self):
        """
        Database simulations are thread based batch transactions.
        When a simulation is started by a thread, any get or put operation
        is executed on the simulated database.

        Other threads
        :return:
        """
        if self.simulating:
            tools.log('There is already an ongoing simulation! {}'.format(
                threading.current_thread().getName()))
            return False
        try:
            self.simulating = True
            self.simulation_owner = threading.current_thread().getName()
            return True
        except:
            return False
Beispiel #16
0
    def worker(self):
        if not self.blockchain.tx_queue.empty() or not self.blockchain.blocks_queue.empty() or \
                self.blockchain.get_chain_state() != BlockchainService.IDLE:
            time.sleep(0.1)
            return

        candidate_block = self.get_candidate_block()
        self.start_workers(candidate_block)

        possible_blocks = []
        while self.threaded_running() and (self.db.get('length') +
                                           1) == candidate_block['length']:
            api.miner_status()
            while not self.queue.empty():
                possible_blocks.append(self.queue.get(timeout=0.01))
            time.sleep(0.1)
            if len(possible_blocks) > 0:
                tools.log('Mined block')
                tools.log(possible_blocks[:1])
                self.blockchain.blocks_queue.put(
                    (possible_blocks[:1], 'miner'))
                break
Beispiel #17
0
 def execute_order(service, order):
     """
     Directly executes the order on service instance.
     Makes no thread checks, no synchronization attempts.
     :param service: Service instance
     :param order: Order object
     :return: result of the execution
     """
     result = False
     if order.action == '__close_threaded__':
         result = True
         service.__threads[order.args[0]]["running"] = False
     elif order.action == '__shutdown_service__':
         result = True
         service.set_state(Service.STOPPED)
     elif hasattr(service, order.action):
         try:
             result = getattr(service, order.action)._original(service, *order.args, **order.kwargs)
         except:
             result = None
             tools.log(sys.exc_info())
     return result
    def execute(self, action, expect_result, args, kwargs):
        """
        Execute an order that is triggered by annotated methods.
        This method should be treated as private.
        :param action: Action name
        :param expect_result: Whether to wait for result of action
        :param args: Argument list for method
        :param kwargs: Keyword argument list for method
        :return: result of action or None
        """
        if self.get_state() != Service.RUNNING:
            return None

        result = None
        new_order = Order(action, args, kwargs)

        # This is already event thread and someone called a synced function.
        # We can run it now.
        if threading.current_thread().name == self.event_thread.name:
            result = Service.execute_order(self, new_order)
            return result

        self.signals[new_order.id] = threading.Event()
        self.into_service_queue.put(new_order)
        if expect_result:
            try:
                if self.signals[new_order.id].wait():
                    response = self.service_responses[new_order.id]
                    del self.signals[new_order.id]
                    del self.service_responses[new_order.id]
                    result = response
                else:
                    tools.log('Service wait timed out',
                              self.__class__.__name__)
            except:
                tools.log(sys.exc_info())
                pass
        return result
Beispiel #19
0
    def tx_signature_check(tx):
        tx_copy = copy.deepcopy(tx)
        if 'signatures' not in tx or not isinstance(tx['signatures'],
                                                    (list, )):
            tools.log('no signatures')
            return False
        if 'pubkeys' not in tx or not isinstance(tx['pubkeys'], (list, )):
            tools.log('no pubkeys')
            return False
        if len(tx['pubkeys']) == 0:
            tools.log('pubkey error')
            return False
        if len(tx['signatures']) > len(tx['pubkeys']):
            tools.log('there are more signatures than required')
            return False

        tx_copy.pop('signatures')
        msg = tools.det_hash(tx_copy)
        if not BlockchainService.sigs_match(copy.deepcopy(tx['signatures']),
                                            copy.deepcopy(tx['pubkeys']), msg):
            tools.log('sigs do not match')
            return False
        return True
Beispiel #20
0
    def worker(self):
        if self.blockchain.get_chain_state(
        ) == blockchain.BlockchainService.SYNCING:
            time.sleep(0.1)
            return

        candidate_block = self.get_candidate_block()
        tx_pool = self.blockchain.tx_pool()
        self.start_workers(candidate_block)

        possible_blocks = []
        while not MinerService.is_everyone_dead(
                self.pool) and self.threaded_running():
            if self.db.get('length') + 1 != candidate_block[
                    'length'] or self.blockchain.tx_pool() != tx_pool:
                candidate_block = self.get_candidate_block()
                tx_pool = self.blockchain.tx_pool()
                self.start_workers(candidate_block)
            try:
                while not self.queue.empty():
                    possible_blocks.append(self.queue.get(timeout=0.01))
            except queue.Empty:
                pass
            if len(possible_blocks) > 0:
                break

        # This may seem weird. It is needed when workers finish so fast, while loop ends prematurely.
        try:
            while not self.queue.empty():
                possible_blocks.append(self.queue.get(timeout=0.01))
        except queue.Empty:
            pass

        if len(possible_blocks) > 0:
            tools.log('Mined block')
            tools.log(possible_blocks)
            self.blockchain.blocks_queue.put(possible_blocks)
    def add_block(self, block):
        """Attempts adding a new block to the blockchain.
         Median is good for weeding out liars, so long as the liars don't have 51%
         hashpower. """

        length = self.db.get('length')
        block_at_length = self.get_block(length)

        if int(block['length']) < int(length) + 1:
            return 1
        elif int(block['length']) > int(length) + 1:
            return 2

        tools.echo('add block: ' + str(block['length']))

        if (length >= 0 and block['diffLength'] != tools.hex_sum(block_at_length['diffLength'],
                                                                 tools.hex_invert(block['target']))) \
                or (length < 0 and block['diffLength'] != tools.hex_invert(block['target'])):
            tools.log(block['diffLength'])
            tools.log(
                tools.hex_sum(self.db.get('diffLength'),
                              tools.hex_invert(block['target'])))
            tools.log(block['length'])
            tools.log('difflength is wrong')
            return 3

        if length >= 0 and tools.det_hash(
                block_at_length) != block['prevHash']:
            tools.log('prevhash different')
            return 3

        nonce_and_hash = tools.hash_without_nonce(block)
        if tools.det_hash(nonce_and_hash) > block['target']:
            tools.log('hash value does not match the target')
            return 3

        if block['target'] != self.target(block['length']):
            tools.log('block: ' + str(block))
            tools.log('target: ' + str(self.target(block['length'])))
            tools.log('wrong target')
            return 3
        """
        recent_time_values = self.recent_blockthings('times', custom.median_block_time_limit)
        median_block = tools.median(recent_time_values)
        if block['time'] < median_block:
            tools.log('Received block is generated earlier than median.')
            return 3
        """

        # Check that block includes exactly one mint transaction
        if 'txs' not in block:
            tools.log(
                'Received block does not include txs. At least a coinbase tx must be present'
            )
            return 3

        # Sum of all mint type transactions must be one
        mint_present = sum(
            [0 if tx['type'] != 'mint' else 1 for tx in block['txs']])
        if mint_present != 1:
            tools.log('Received block includes wrong amount of mint txs')
            return 3

        for tx in block['txs']:
            if not BlockchainService.tx_integrity_check(tx).getFlag():
                tools.log('Received block failed special txs check.')
                return 3

        if not self.statedb.update_database_with_block(block):
            return 3

        self.put_block(block['length'], block)
        self.db.put('length', block['length'])
        self.db.put('diffLength', block['diffLength'])

        orphans = self.tx_pool_pop_all()

        for orphan in sorted(orphans,
                             key=lambda x: x['count'] if 'count' in x else -1):
            self.tx_queue.put(orphan)

        tools.techo('add block: ' + str(block['length']))
        return 0
Beispiel #22
0
    def add_block(self, block):
        """Attempts adding a new block to the blockchain.
         Median is good for weeding out liars, so long as the liars don't have 51%
         hashpower. """

        length = self.db.get('length')

        if int(block['length']) < int(length) + 1:
            return 1
        elif int(block['length']) > int(length) + 1:
            return 2

        if (length >= 0 and block['diffLength'] != tools.hex_sum(self.db.get('diffLength'), tools.hex_invert(block['target']))) \
                or (length < 0 and block['diffLength'] != tools.hex_invert(block['target'])):
            tools.log(block['diffLength'])
            tools.log(
                tools.hex_sum(self.db.get('diffLength'),
                              tools.hex_invert(block['target'])))
            tools.log(block['length'])
            tools.log('difflength is wrong')
            return 3

        if length >= 0 and tools.det_hash(
                self.db.get(length)) != block['prevHash']:
            tools.log('prevhash different')
            return 3

        nonce_and_hash = tools.hash_without_nonce(block)
        if tools.det_hash(nonce_and_hash) > block['target']:
            tools.log('hash value does not match the target')
            return 3

        if block['target'] != self.target(block['length']):
            tools.log('block: ' + str(block))
            tools.log('target: ' + str(self.target(block['length'])))
            tools.log('wrong target')
            return 3

        recent_time_values = self.recent_blockthings(
            'times', custom.median_block_time_limit, self.db.get('length'))
        median_block = tools.median(recent_time_values)
        if block['time'] < median_block:
            tools.log('Received block is generated earlier than median.')
            return 3

        if not self.account.update_accounts_with_block(
                block, add_flag=True, simulate=True):
            tools.log('Received block failed transactions check.')
            return 3

        self.db.put(block['length'], block)
        self.db.put('length', block['length'])
        self.db.put('diffLength', block['diffLength'])

        orphans = self.tx_pool_pop_all()

        self.account.update_accounts_with_block(block, add_flag=True)

        for orphan in sorted(orphans, key=lambda x: x['count']):
            self.add_tx(orphan)

        return 0
    def blockchain_process(self):
        """
        In this thread we check blocks queue for possible additions to blockchain.
        Following type is expected to come out of the queue. Any other type will be rejected.
        ([candidate_blocks in order], peer_node_id)
        Only 3 services actually put stuff in queue: peer_listen, peer_check, miner
        PeerListen and PeerCheck obeys the expected style.
        Miner instead puts one block in candidate block list, node id is 'miner'
        :return:
        """
        try:
            candidate = self.blocks_queue.get(timeout=0.1)
            self.set_chain_state(BlockchainService.SYNCING)
            try:
                if isinstance(candidate, tuple):
                    blocks = candidate[0]
                    node_id = candidate[1]
                    total_number_of_blocks_added = 0

                    for block in blocks:
                        if not BlockchainService.block_integrity_check(
                                block) and node_id != 'miner':
                            self.peer_reported_false_blocks(node_id)
                            return

                    self.db.simulate()
                    try:
                        length = self.db.get('length')
                        for i in range(20):
                            block = self.get_block(length)
                            if self.fork_check(blocks, length, block):
                                self.delete_block()
                                length -= 1
                            else:
                                break

                        for block in blocks:
                            add_block_result = self.add_block(block)
                            if add_block_result == 2:  # A block that is ahead of us could not be added. No need to proceed.
                                break
                            elif add_block_result == 0:
                                total_number_of_blocks_added += 1
                                api.new_block()
                    except Exception as e:
                        print("halo")

                    if total_number_of_blocks_added == 0 or self.db.get(
                            'length') != blocks[-1]['length']:
                        # All received blocks failed. Punish the peer by lowering rank.
                        self.db.rollback()
                        if node_id != 'miner':
                            self.peer_reported_false_blocks(node_id)
                    else:
                        self.db.commit()
            except Exception as e:
                tools.log(e)
            self.blocks_queue.task_done()
        except:
            pass

        try:
            candidate_tx = self.tx_queue.get(timeout=0.1)
            result = self.add_tx(candidate_tx)
            self.tx_queue.task_done()
        except:
            pass

        self.set_chain_state(BlockchainService.IDLE)