示例#1
0
def test_blocks_ids_should_increase_one_by_one():
    chain = Blockchain()
    block_id_one = Block()
    block_id_two = Block()
    chain.add_block(block_id_one)
    chain.add_block(block_id_two)
    assert block_id_one.id == 1
    assert block_id_two.id == 2
示例#2
0
    def pick_honest_chain(self, node_chains):
        """
        Genesis block'un yaratıldığı bir ağa bağlanan node için çalıştırılır.
        Node, consensus sonucu, değiştirilmemiş block'u bulmaya çalışır.

        v0.0.1 itibari ile, sadece ilk node'dan gelen block'u doğru kabul edip almaktadır.
        İlerki versiyonlarda değiştirilecektir. Roadmap'e eklenmiş durumda.

        :param node_chains: Ağdaki tüm ağlardan alınan block'lar.
        """
        _log('info', 'Ağdaki block\'lar toplanılarak, consensus sonrası en uygun block seçildi.')
        self.chain = Blockchain(difficulty=self.difficulty)
        self.chain.blocks = node_chains[0][1]
示例#3
0
def test_any_other_node_found_correct_hash_then_mining_should_be_stopped():
    from dokuztas.blockchain import Blockchain, PendingBlock, Block
    node = NodeComponent(miner=True)
    node.chain = Blockchain(difficulty=10)

    def adds_genesis():
        genesis = Block(id=0,
                        blockhash=0,
                        previous_hash=0,
                        nonce=0,
                        merkleroot=0,
                        data=['genesis'])
        node.chain.blocks.append(genesis)

    genesis_patcher = patch('dokuztas.node.NodeComponent.create_genesis_chain',
                            side_effect=adds_genesis)
    genesis_patcher.start()
    node.create_genesis_chain()
    genesis_patcher.stop()

    pending_block = PendingBlock()
    pending_block.add_txs(['a', 'b', 'c'])
    node.pending_blocks.append(pending_block)
    node.mine()
    block_to_add = Block(id=123,
                         previous_hash=0,
                         nonce=123,
                         merkleroot=123,
                         blockhash=111,
                         data=['a', 'b', 'c'])
    node.block_added(block_to_add)
    assert node.chain.blocks[1].blockhash == 111
    assert len(node.chain.blocks) == 2
示例#4
0
def test_if_correct_hash_is_found_then_block_found_function_should_be_called_once(
):
    from dokuztas.blockchain import Blockchain, PendingBlock
    patcher = patch('dokuztas.node.NodeComponent.block_found')
    mock = patcher.start()
    node = NodeComponent(miner=True)
    node.chain = Blockchain(difficulty=4)
    node.chain._generate_genesis()
    pending_block = PendingBlock()
    pending_block.add_txs(['a', 'b', 'c'])
    node.pending_blocks.append(pending_block)

    def sync_runner():
        def always_mine():
            return False

        node.chain.mine(node.pending_blocks[0], always_mine, mock)

    mine_patcher = patch('dokuztas.node.NodeComponent.mine',
                         side_effect=sync_runner)
    mine_patcher.start()
    node.mine()
    patcher.stop()
    mine_patcher.stop()
    assert mock.called
示例#5
0
 def func_wrapper():
     from dokuztas.blockchain import Blockchain
     node = NodeComponent()
     node.chain = Blockchain(difficulty=difficulty)
     node.miner = True
     node.create_genesis_chain()
     for x in range(0, txs_count):
         node.add_transaction(str(x))
     func(node)
示例#6
0
def test_for_mining_it_should_be_start_new_thread():
    from dokuztas.blockchain import Blockchain, PendingBlock
    patcher = patch('dokuztas._internals.MiningThread.start')
    mock = patcher.start()
    node = NodeComponent(miner=True)
    node.chain = Blockchain(difficulty=1)
    node.chain._generate_genesis()
    pending_block = PendingBlock()
    pending_block.add_txs(['a', 'b', 'c'])
    node.pending_blocks.append(pending_block)
    node.mine()
    patcher.stop()
    assert mock.called
示例#7
0
        def func_wrapper():
            def always_mine():
                return False

            chain = Blockchain()
            chain._generate_genesis()
            p = PendingBlock()
            p.add_txs(['a', 'b', 'c', 'd', 'f'])
            chain.mine(p, always_mine)

            p2 = PendingBlock()
            p2.add_txs(['y', 'z'])
            chain.mine(p2, always_mine)

            func(chain)
示例#8
0
def test_each_block_s_previous_hash_property_should_be_previous_block_s_hash():
    chain = Blockchain()
    block_id_zero = Block()
    block_id_one = Block()
    block_id_two = Block()
    chain.add_block(block_id_zero)
    chain.add_block(block_id_one)
    chain.add_block(block_id_two)
    assert block_id_one.previous_hash == block_id_zero.hash
    assert block_id_two.previous_hash == block_id_one.hash
示例#9
0
def test_if_a_block_is_changed_then_chain_should_not_be_a_valid_chain():
    chain = Blockchain()
    block_id_zero = Block()
    block_id_one = Block()
    block_id_two = Block()

    chain.add_block(block_id_zero)
    chain.add_block(block_id_one)
    chain.add_block(block_id_two)

    block_id_one.data = {'hacked'}
    block_id_one.calculate_hash()

    validation = chain.validate()
    assert validation == False
示例#10
0
def test_mine_tester():
    def always_mine():
        return False

    chain = Blockchain()
    chain._generate_genesis()
    p = PendingBlock()
    p.add_txs(['a', 'b', 'c', 'd', 'f'])
    chain.mine(p, always_mine)
    assert chain.blocks[1].nonce == 13106
    assert chain.blocks[
        1].blockhash == '00005df8b04cb42e62c5be0766af2caa884dd52b51b7ff1549aab3c77e88b84d'
示例#11
0
def test_mine_tester():
    def always_mine():
        return False

    chain = Blockchain()
    chain._generate_genesis()
    p = PendingBlock()
    p.add_txs(['a', 'b', 'c', 'd', 'f'])
    chain.mine(p, always_mine)
    assert chain.blocks[1].nonce == 98346
    assert chain.blocks[
        1].blockhash == '0000ce2e9b894dcce5c482649d6f06bdb8c84215574b235cd92e616c29a02f26'
示例#12
0
def test_after_first_block_is_mined_then_pending_txs_should_be_mined():
    from dokuztas.blockchain import Blockchain
    node = NodeComponent()
    node.miner = True
    node.chain = Blockchain(difficulty=1)
    node.chain._generate_genesis()

    def sync_mine(**kwargs):
        node.chain.mine(kwargs['args'][0], kwargs['args'][1],
                        kwargs['args'][2])

    thread_patcher = patch('dokuztas.node.NodeComponent._internal_mine',
                           side_effect=sync_mine)
    thread_patcher.start()
    for x in range(0, 22):
        node.add_transaction(str(x))
    thread_patcher.stop()
    assert len(node.chain.blocks) == 3
示例#13
0
def test_any_other_node_found_correct_hash_then_mining_should_be_stopped():
    from dokuztas.blockchain import Blockchain, PendingBlock, Block
    node = NodeComponent(miner=True)
    node.chain = Blockchain(difficulty=10)
    node.chain._generate_genesis()
    pending_block = PendingBlock()
    pending_block.add_txs(['a', 'b', 'c'])
    node.pending_blocks.append(pending_block)
    node.mine()
    block_to_add = Block(id=123,
                         previous_hash=0,
                         nonce=123,
                         merkleroot=123,
                         blockhash=111,
                         data=['a', 'b', 'c'])
    node.block_added(block_to_add)
    assert node.chain.blocks[1].blockhash == 111
    assert len(node.chain.blocks) == 2
示例#14
0
def test_genesis_block_should_be_mined():
    chain = Blockchain()
    chain._generate_genesis()
    assert chain.blocks[
        0].blockhash == '00009b8539d913411b8dd95aa3a449593dc454ef0ddc87955bd48d8ff48a926a'
示例#15
0
class NodeComponent(object):
    def __init__(self, miner=False, cb_notify_nodes=None, difficulty=4):
        self.chain = None
        self.stop_mining = False
        self.difficulty = difficulty
        self.cb_notify_nodes = cb_notify_nodes
        self.miner = miner
        self.pending_txs = []
        self.pending_blocks = []

    def create_genesis_chain(self):
        """Genesis block yaratır."""
        _log('info', 'Genesis! Blockchain ilk kez oluşturuldu.')
        self.chain = Blockchain(difficulty=self.difficulty)
        self.chain._generate_genesis()

    def pick_honest_chain(self, node_chains):
        """
        Genesis block'un yaratıldığı bir ağa bağlanan node için çalıştırılır.
        Node, consensus sonucu, değiştirilmemiş block'u bulmaya çalışır.

        v0.0.1 itibari ile, sadece ilk node'dan gelen block'u doğru kabul edip almaktadır.
        İlerki versiyonlarda değiştirilecektir. Roadmap'e eklenmiş durumda.

        :param node_chains: Ağdaki tüm ağlardan alınan block'lar.
        """
        _log('info', 'Ağdaki block\'lar toplanılarak, consensus sonrası en uygun block seçildi.')
        self.chain = Blockchain(difficulty=self.difficulty)
        self.chain.blocks = node_chains[0][1]

    def load_chain(self, nodes_chains):
        """
        Ağdan gelen block'lara bakarak, genesis block mu yaratılacak, consensus sonucu en uygun chain mi seçilecek kararını verir.

        :param nodes_chains: Ağdaki tüm ağlardan alınan block'lar.
        """
        if len(nodes_chains) == 0:
            self.create_genesis_chain()
        else:
            self.pick_honest_chain(nodes_chains)

    def get_blocks(self):
        if not self.chain:
            raise ChainNotCreatedException()
        return self.chain.blocks

    def miner_check(self):
        # aktif node bir miner mı kontrolü yapar. Değilse MinerException fırlatır.
        if not self.miner:
            raise MinerException()

    def terminate_mining(self):
        """
        Blockchain.mine metoduna parametre olarak geçilir ve Blockchain.mine metodu nonce'u ararken, her iterasyonda bu metodu çağırır.
        Burada amaç, NodeComponent üzerinden, Blockchain üzerinde çalışan mining thread'ini durdurabilmektir. self.stop_mining = True olduğunda,
        Blockchain.mine duracaktır. Bu durum sadece, başka bir miner'ın problemi, aktif node'dan önce çözmesi durumunda oluşur. Aktif node'un
        üzerinde çalıştığı block'u bırakarak, yeni block'a geçmesini sağlar.
        """
        return self.stop_mining

    def add_transaction(self, tx):
        """
        Mine edilmesi için yeni bir transaction ekemek içindir.
        Her bekleyen transaction'ı, bir (1) block'a çevirir ve bu şekilde bekletir.

        Mine işlemini, tx sayısı 10'a ulaştığında bir kez tetikler. Sonrasında mine bir döngü şeklinde çalışmaya devam eder.

        :param tx: Mine edilesi için eklenen transaction.
        """
        self.miner_check()

        self.pending_txs.append(tx)

        if len(self.pending_txs) > 10:
            p_block = PendingBlock()
            p_block.add_txs(self.pending_txs)
            self.pending_blocks.append(p_block)
            self.pending_txs = []

            if len(self.pending_blocks) == 1:
                self.mine()

    def block_found(self):
        """
        Çalışan node block'u bulmuşsa, blockchain objesi tarafından bu metod çağırılır.
        """
        _log('dev', 'NodeComponent.mine.block_found')
        if len(self.pending_blocks) > 0:
            self.pending_blocks.remove(self.pending_blocks[0])
        elif len(self.pending_txs) > 0:
            self.pending_txs = []
        self.mine()

        if self.cb_notify_nodes:
            self.cb_notify_nodes(self.chain.blocks[len(self.chain.blocks) - 1])

    def _internal_mine(self, args=()):
        """
        Normal şartlar altında mine işlemi ayrı bir thread içersinde çalışmalıdır. Bu metod da bunu sağlamaktadır.
        Bu işlemin NodeComponent.mine metodu içersinde yapılmamasının tek sebebi, dışardan mock'lama ihtiyacının oluşmasıdır.
        Unit test'lerde kimi zaman senkronize mining yapılması gerekebiliyor.

        :param args: Her zaman Blockchain.mine metodu ile aynı olmalıdır.
        """
        th_mine = MiningThread(mine_target=self.chain.mine,
                               args=args)
        th_mine.start()

    def mine(self):
        """
        Mine işleminin başlatıldığı yerdir. Bu işlemin blockchain objesi tarafından yönetilmemesinin sebebi,
        ilerde node'ların, transaction fee'ye göre mine etme veya mine etmek istedikleri block'ları kendilerinin
        seçebilmesi gibi özellikleri olabilmesi ihtimalidir. Şu an için roadmap'te böyle bir özellik bulunmamaktadır.
        """
        self.miner_check()

        if len(self.pending_blocks) > 0:
            self.stop_mining = False
            self._internal_mine(args=(self.pending_blocks[0],
                                      self.terminate_mining,
                                      self.block_found))
        elif len(self.pending_txs) > 0:
            self.stop_mining = False
            temp_block = PendingBlock()
            temp_block.add_txs(self.pending_txs)
            self.pending_txs = []
            self._internal_mine(args=(temp_block,
                                      self.terminate_mining,
                                      self.block_found))

    def block_added(self, new_block):
        """
        Diğer node'lardan biri, mining sonucu block eklediğinde, aktif node'un sync kalması için çağırılır.
        Devam etmekte olan bir mine işlemi varsa, sonlandırılır.

        :param new_block: Yeni eklenen block.
        """
        _log('debug', 'node.NodeComponent.block_added')
        self.chain.blocks.append(new_block)
        if self.miner:
            self.stop_mining = True
            # Normal şartlar altında bu if bloguna ihtiyaç olmaması gerekiyor.
            # HTTP çalıştığımız için ve queue olmadığı için, diğer miner'lardan birisi
            # iki kez üst üste problemi çözerse, IndexError: list index out of range
            # oluşuyor.
            if len(self.pending_blocks) > 0:
                self.pending_blocks.remove(self.pending_blocks[0])
            self.mine()
示例#16
0
def test_genesis_block_s_previous_block_hash_should_be_assigned_zero():
    chain = Blockchain()
    genesis_block = Block()
    chain.add_block(genesis_block)
    assert chain.blocks[0].previous_hash == 0
示例#17
0
 def create_genesis_chain(self):
     """Genesis block yaratır."""
     _log('info', 'Genesis! Blockchain ilk kez oluşturuldu.')
     self.chain = Blockchain(difficulty=self.difficulty)
     self.chain._generate_genesis()
示例#18
0
def test_if_pending_tx_count_is_odd_number_then_merkle_root_should_be_one_hash(
):
    chain = Blockchain()
    pending_txs = ['a', 'b', 'c', 'd', 'f']
    root_hash = chain.calculate_merkle(pending_txs=pending_txs)
    assert root_hash == 'dab4b3dd381a192214ffe6dfb8481f741be76e171dc298db75a44eb4df6d4213'
示例#19
0
def test_if_pending_tx_count_is_even_number_then_merkle_root_should_be_one_hash(
):
    chain = Blockchain()
    pending_txs = ['a', 'b', 'c', 'd', 'f', 'g']
    root_hash = chain.calculate_merkle(pending_txs=pending_txs)
    assert root_hash == '1adc308023cbce02e0d90cc31b096ea2a847548b966adc4c18d3f2ae654bfcb3'
示例#20
0
def test_if_there_is_just_one_tx_to_mine_then_its_hash_should_be_merkle_hash():
    chain = Blockchain()
    odd_hash = chain.calculate_merkle(['I shot the sheriff'])
    assert odd_hash == 'f1254d09500291eb72966b263da07df061426fe6391873b640128bcf8abcf897'
示例#21
0
def test_if_the_blocks_are_not_changed_then_chain_should_be_a_valid_chain():
    chain = Blockchain()
    block_id_zero = Block()
    block_id_one = Block()
    block_id_two = Block()

    chain.add_block(block_id_zero)
    first_validation = chain.validate()
    assert first_validation == True

    chain.add_block(block_id_one)
    second_validation = chain.validate()
    assert second_validation == True

    chain.add_block(block_id_two)
    third_validation = chain.validate()
    assert third_validation == True
示例#22
0
def test_genesis_block_s_previous_block_hash_should_be_assigned_zero():
    chain = Blockchain()
    chain._generate_genesis()
    assert chain.blocks[0].previous_hash == 0
示例#23
0
def test_when_genesis_block_is_added_then_genesis_block_id_should_be_assigned_zero(
):
    chain = Blockchain()
    chain._generate_genesis()
    assert chain.blocks[0].id == 0
示例#24
0
def test_when_blockchain_is_created_first_time_then_first_block_should_be_genesis_block(
):
    chain = Blockchain()
    chain._generate_genesis()
    assert len(chain.blocks) == 1
    assert chain.blocks[0].id == 0