Ejemplo n.º 1
0
    def setUp(self):
        self.database_of_record = dict_database.DictDatabase()
        self.context_manager = context_manager.ContextManager(
            self.database_of_record)
        self.first_state_hash = self.context_manager.get_first_root()

        # used for replicating state hash through direct merkle tree updates
        self.database_results = dict_database.DictDatabase()
Ejemplo n.º 2
0
 def setUp(self):
     self.context_manager = ContextManager(dict_database.DictDatabase())
     squash_handler = self.context_manager.get_squash_handler()
     self.first_state_root = self.context_manager.get_first_root()
     self.scheduler = ParallelScheduler(squash_handler,
                                        self.first_state_root,
                                        always_persist=False)
Ejemplo n.º 3
0
    def test_set_status(self):
        """Tests that set_status() has the correct behavior.

        Basically:
            1. Adds a batch which has two transactions.
            2. Calls next_transaction() to get the first Transaction.
            3. Calls next_transaction() to verify that it returns None.
            4. Calls set_status() to mark the first transaction applied.
            5. Calls next_transaction() to  get the second Transaction.

        Step 3 returns None because the first transaction hasn't been marked
        as applied, and the SerialScheduler will only return one
        not-applied Transaction at a time.

        Step 5 is expected to return the second Transaction, not None,
        since the first Transaction was marked as applied in the previous
        step.
        """
        private_key = bitcoin.random_key()
        public_key = bitcoin.encode_pubkey(
            bitcoin.privkey_to_pubkey(private_key), "hex")

        context_manager = ContextManager(dict_database.DictDatabase())
        squash_handler = context_manager.get_squash_handler()
        first_state_root = context_manager.get_first_root()
        scheduler = SerialScheduler(squash_handler, first_state_root)

        txns = []

        for name in ['a', 'b']:
            txn = create_transaction(
                name=name,
                private_key=private_key,
                public_key=public_key)

            txns.append(txn)

        batch = create_batch(
            transactions=txns,
            private_key=private_key,
            public_key=public_key)

        scheduler.add_batch(batch)

        scheduled_txn_info = scheduler.next_transaction()
        self.assertIsNotNone(scheduled_txn_info)
        self.assertEquals('a', scheduled_txn_info.txn.payload.decode())

        self.assertIsNone(scheduler.next_transaction())

        scheduler.set_transaction_execution_result(
            scheduled_txn_info.txn.header_signature,
            is_valid=False,
            context_id=None)

        scheduled_txn_info = scheduler.next_transaction()
        self.assertIsNotNone(scheduled_txn_info)
        self.assertEquals('b', scheduled_txn_info.txn.payload.decode())
Ejemplo n.º 4
0
    def setUp(self):
        self.context_manager = ContextManager(dict_database.DictDatabase())
        squash_handler = self.context_manager.get_squash_handler()
        self.first_state_root = self.context_manager.get_first_root()
        self.scheduler = ParallelScheduler(squash_handler,
                                           self.first_state_root,
                                           always_persist=False)

        self._context = create_context('secp256k1')
        self._crypto_factory = CryptoFactory(self._context)
Ejemplo n.º 5
0
    def test_transaction_order(self):
        """Tests the that transactions are returned in order added.

        Adds three batches with varying number of transactions, then tests
        that they are returned in the appropriate order when using an iterator.

        This test also creates a second iterator and verifies that both
        iterators return the same transactions.

        This test also finalizes the scheduler and verifies that StopIteration
        is thrown by the iterator.
        """
        private_key = bitcoin.random_key()
        public_key = bitcoin.encode_pubkey(
            bitcoin.privkey_to_pubkey(private_key), "hex")
        context_manager = ContextManager(dict_database.DictDatabase())
        squash_handler = context_manager.get_squash_handler()
        first_state_root = context_manager.get_first_root()
        scheduler = SerialScheduler(squash_handler, first_state_root)

        txns = []

        for names in [['a', 'b', 'c'], ['d', 'e'], ['f', 'g', 'h', 'i']]:
            batch_txns = []
            for name in names:
                txn = create_transaction(
                    name=name,
                    private_key=private_key,
                    public_key=public_key)

                batch_txns.append(txn)
                txns.append(txn)

            batch = create_batch(
                transactions=batch_txns,
                private_key=private_key,
                public_key=public_key)

            scheduler.add_batch(batch)

        scheduler.finalize()

        iterable1 = iter(scheduler)
        iterable2 = iter(scheduler)
        for txn in txns:
            scheduled_txn_info = next(iterable1)
            self.assertEqual(scheduled_txn_info, next(iterable2))
            self.assertIsNotNone(scheduled_txn_info)
            self.assertEquals(txn.payload, scheduled_txn_info.txn.payload)
            scheduler.set_transaction_execution_result(
                txn.header_signature, False, None)

        with self.assertRaises(StopIteration):
            next(iterable1)
Ejemplo n.º 6
0
    def test_completion_on_finalize(self):
        """Tests that iteration will stop when finalized is called on an
        otherwise complete scheduler.

        Adds one batch and transaction, then verifies the iterable returns
        that transaction.  Sets the execution result and then calls finalize.
        Since the the scheduler is complete (all transactions have had
        results set, and it's been finalized), we should get a StopIteration.

        This check is useful in making sure the finalize() can occur after
        all set_transaction_execution_result()s have been performed, because
        in a normal situation, finalize will probably occur prior to those
        calls.
        """
        private_key = signing.generate_privkey()
        public_key = signing.generate_pubkey(private_key)

        context_manager = ContextManager(dict_database.DictDatabase())
        squash_handler = context_manager.get_squash_handler()
        first_state_root = context_manager.get_first_root()
        scheduler = SerialScheduler(squash_handler, first_state_root)

        txn = create_transaction(name='a',
                                 private_key=private_key,
                                 public_key=public_key)

        batch = create_batch(transactions=[txn],
                             private_key=private_key,
                             public_key=public_key)

        iterable = iter(scheduler)

        scheduler.add_batch(batch)

        scheduled_txn_info = next(iterable)
        self.assertIsNotNone(scheduled_txn_info)
        self.assertEquals(txn.payload, scheduled_txn_info.txn.payload)
        scheduler.set_transaction_execution_result(txn.header_signature, False,
                                                   None)

        scheduler.finalize()

        with self.assertRaises(StopIteration):
            next(iterable)
Ejemplo n.º 7
0
    def _add_valid_batch_invalid_batch(self, scheduler, context_manager):
        """Tests the squash function. That the correct state hash is found
        at the end of valid and invalid batches, similar to block publishing.

         Basically:
            1. Adds two batches, one where all the txns are valid,
               and one where one of the txns is invalid.
            2. Run through the scheduler executor interaction
               as txns are processed.
            3. Verify that the state root obtained through the squash function
               is the same as directly updating the merkle tree.
            4. Verify that correct batch statuses are set

        This test should work for both a serial and parallel scheduler.
        """
        private_key = signing.generate_privkey()
        public_key = signing.generate_pubkey(private_key)

        # 1)
        batch_signatures = []
        for names in [['a', 'b'], ['invalid', 'c'], ['d', 'e']]:
            batch_txns = []
            for name in names:
                txn, _ = create_transaction(payload=name.encode(),
                                            private_key=private_key,
                                            public_key=public_key)

                batch_txns.append(txn)

            batch = create_batch(transactions=batch_txns,
                                 private_key=private_key,
                                 public_key=public_key)

            batch_signatures.append(batch.header_signature)
            scheduler.add_batch(batch)
        scheduler.finalize()
        # 2)
        sched1 = iter(scheduler)
        invalid_payload = hashlib.sha512('invalid'.encode()).hexdigest()
        while not scheduler.complete(block=False):
            txn_info = next(sched1)
            txn_header = transaction_pb2.TransactionHeader()
            txn_header.ParseFromString(txn_info.txn.header)
            inputs_or_outputs = list(txn_header.inputs)
            c_id = context_manager.create_context(
                state_hash=txn_info.state_hash,
                inputs=inputs_or_outputs,
                outputs=inputs_or_outputs,
                base_contexts=txn_info.base_context_ids)
            if txn_header.payload_sha512 == invalid_payload:
                scheduler.set_transaction_execution_result(
                    txn_info.txn.header_signature, False, None)
            else:
                context_manager.set(c_id, [{inputs_or_outputs[0]: b"1"}])
                scheduler.set_transaction_execution_result(
                    txn_info.txn.header_signature, True, c_id)

        sched2 = iter(scheduler)
        # 3)
        txn_info_a = next(sched2)
        txn_a_header = transaction_pb2.TransactionHeader()
        txn_a_header.ParseFromString(txn_info_a.txn.header)
        inputs_or_outputs = list(txn_a_header.inputs)
        address_a = inputs_or_outputs[0]

        txn_info_b = next(sched2)
        address_b = _get_address_from_txn(txn_info_b)

        txn_infoInvalid = next(sched2)
        txn_info_c = next(sched2)

        txn_info_d = next(sched2)
        address_d = _get_address_from_txn(txn_info_d)

        txn_info_e = next(sched2)
        address_e = _get_address_from_txn(txn_info_e)

        merkle_database = MerkleDatabase(dict_database.DictDatabase())
        state_root_end = merkle_database.update(
            {
                address_a: b"1",
                address_b: b"1",
                address_d: b"1",
                address_e: b"1"
            },
            virtual=False)

        # 4)
        batch1_result = scheduler.get_batch_execution_result(
            batch_signatures[0])
        self.assertTrue(batch1_result.is_valid)

        batch2_result = scheduler.get_batch_execution_result(
            batch_signatures[1])
        self.assertFalse(batch2_result.is_valid)

        batch3_result = scheduler.get_batch_execution_result(
            batch_signatures[2])
        self.assertTrue(batch3_result.is_valid)
        self.assertEqual(batch3_result.state_hash, state_root_end)
Ejemplo n.º 8
0
 def setUp(self):
     self._context_manager = ContextManager(dict_database.DictDatabase(),
                                            state_delta_store=Mock())
Ejemplo n.º 9
0
 def setUp(self):
     self.context_manager = ContextManager(dict_database.DictDatabase())
     squash_handler = self.context_manager.get_squash_handler()
     self.first_state_root = self.context_manager.get_first_root()
     self.scheduler = SerialScheduler(squash_handler, self.first_state_root)
Ejemplo n.º 10
0
    def test_valid_batch_invalid_batch(self):
        """Tests the squash function. That the correct hash is being used
        for each txn and that the batch ending state hash is being set.

         Basically:
            1. Adds two batches, one where all the txns are valid,
               and one where one of the txns is invalid.
            2. Run through the scheduler executor interaction
               as txns are processed.
            3. Verify that the valid state root is obtained
               through the squash function.
            4. Verify that correct batch statuses are set
        """
        private_key = bitcoin.random_key()
        public_key = bitcoin.encode_pubkey(
            bitcoin.privkey_to_pubkey(private_key), "hex")

        context_manager = ContextManager(dict_database.DictDatabase())
        squash_handler = context_manager.get_squash_handler()
        first_state_root = context_manager.get_first_root()
        scheduler = SerialScheduler(squash_handler, first_state_root)
        # 1)
        batch_signatures = []
        for names in [['a', 'b'], ['invalid', 'c']]:
            batch_txns = []
            for name in names:
                txn = create_transaction(
                    name=name,
                    private_key=private_key,
                    public_key=public_key)

                batch_txns.append(txn)

            batch = create_batch(
                transactions=batch_txns,
                private_key=private_key,
                public_key=public_key)

            batch_signatures.append(batch.header_signature)
            scheduler.add_batch(batch)
        scheduler.finalize()
        # 2)
        sched1 = iter(scheduler)
        invalid_payload = hashlib.sha512('invalid'.encode()).hexdigest()
        while not scheduler.complete(block=False):
            txn_info = next(sched1)
            txn_header = transaction_pb2.TransactionHeader()
            txn_header.ParseFromString(txn_info.txn.header)
            inputs_or_outputs = list(txn_header.inputs)
            c_id = context_manager.create_context(txn_info.state_hash,
                                                  inputs_or_outputs,
                                                  inputs_or_outputs)
            if txn_header.payload_sha512 == invalid_payload:
                scheduler.set_transaction_execution_result(
                    txn_info.txn.header_signature, False, c_id)
            else:
                context_manager.set(c_id, [{inputs_or_outputs[0]: 1}])
                scheduler.set_transaction_execution_result(
                    txn_info.txn.header_signature, True, c_id)

        sched2 = iter(scheduler)
        # 3)
        txn_info_a = next(sched2)
        self.assertEquals(first_state_root, txn_info_a.state_hash)

        txn_a_header = transaction_pb2.TransactionHeader()
        txn_a_header.ParseFromString(txn_info_a.txn.header)
        inputs_or_outputs = list(txn_a_header.inputs)
        address_a = inputs_or_outputs[0]
        c_id_a = context_manager.create_context(first_state_root,
                                                inputs_or_outputs,
                                                inputs_or_outputs)
        context_manager.set(c_id_a, [{address_a: 1}])
        state_root2 = context_manager.commit_context([c_id_a], virtual=False)
        txn_info_b = next(sched2)

        self.assertEquals(txn_info_b.state_hash, state_root2)

        txn_b_header = transaction_pb2.TransactionHeader()
        txn_b_header.ParseFromString(txn_info_b.txn.header)
        inputs_or_outputs = list(txn_b_header.inputs)
        address_b = inputs_or_outputs[0]
        c_id_b = context_manager.create_context(state_root2,
                                                inputs_or_outputs,
                                                inputs_or_outputs)
        context_manager.set(c_id_b, [{address_b: 1}])
        state_root3 = context_manager.commit_context([c_id_b], virtual=False)
        txn_infoInvalid = next(sched2)

        self.assertEquals(txn_infoInvalid.state_hash, state_root3)

        txn_info_c = next(sched2)
        self.assertEquals(txn_info_c.state_hash, state_root3)
        # 4)
        batch1_result = scheduler.get_batch_execution_result(
            batch_signatures[0])
        self.assertTrue(batch1_result.is_valid)
        self.assertEquals(batch1_result.state_hash, state_root3)

        batch2_result = scheduler.get_batch_execution_result(
            batch_signatures[1])
        self.assertFalse(batch2_result.is_valid)
        self.assertIsNone(batch2_result.state_hash)
Ejemplo n.º 11
0
 def setUp(self):
     self._context_manager = ContextManager(dict_database.DictDatabase())
Ejemplo n.º 12
0
    def setUp(self):
        self._context_manager = ContextManager(dict_database.DictDatabase())

        self._context = create_context('secp256k1')
        self._crypto_factory = CryptoFactory(self._context)
Ejemplo n.º 13
0
    def test_add_batch_after_empty_iteration(self):
        """Tests that iterations will continue as result of add_batch().

        This test calls next() on a scheduler iterator in a separate thread
        called the IteratorThread.  The test waits until the IteratorThread
        is waiting in next(); internal to the scheduler, it will be waiting on
        a condition variable as there are no transactions to return and the
        scheduler is not finalized.  Then, the test continues by running
        add_batch(), which should cause the next() running in the
        IterableThread to return a transaction.

        This demonstrates the scheduler's ability to wait on an empty iterator
        but continue as transactions become available via add_batch.
        """
        private_key = signing.generate_privkey()
        public_key = signing.generate_pubkey(private_key)

        context_manager = ContextManager(dict_database.DictDatabase())
        squash_handler = context_manager.get_squash_handler()
        first_state_root = context_manager.get_first_root()
        scheduler = SerialScheduler(squash_handler, first_state_root)

        # Create a basic transaction and batch.
        txn = create_transaction(name='a',
                                 private_key=private_key,
                                 public_key=public_key)
        batch = create_batch(transactions=[txn],
                             private_key=private_key,
                             public_key=public_key)

        # This class is used to run the scheduler's iterator.
        class IteratorThread(threading.Thread):
            def __init__(self, iterable):
                threading.Thread.__init__(self)
                self._iterable = iterable
                self.ready = False
                self.condition = threading.Condition()
                self.txn_info = None

            def run(self):
                # Even with this lock here, there is a race condition between
                # exit of the lock and entry into the iterable.  That is solved
                # by sleep later in the test.
                with self.condition:
                    self.ready = True
                    self.condition.notify()
                txn_info = next(self._iterable)
                with self.condition:
                    self.txn_info = txn_info
                    self.condition.notify()

        # This is the iterable we are testing, which we will use in the
        # IteratorThread.  We also use it in this thread below to test
        # for StopIteration.
        iterable = iter(scheduler)

        # Create and startup thread.
        thread = IteratorThread(iterable=iterable)
        thread.start()

        # Pause here to make sure the thread is absolutely as far along as
        # possible; in other words, right before we call next() in it's run()
        # method.  When this returns, there should be very little time until
        # the iterator is blocked on a condition variable.
        with thread.condition:
            while not thread.ready:
                thread.condition.wait()

        # May the daemons stay away during this dark time, and may we be
        # forgiven upon our return.
        time.sleep(1)

        # At this point, the IteratorThread should be waiting next(), so we go
        # ahead and give it a batch.
        scheduler.add_batch(batch)

        # If all goes well, thread.txn_info will get set to the result of the
        # next() call.  If not, it will timeout and thread.txn_info will be
        # empty.
        with thread.condition:
            if thread.txn_info is None:
                thread.condition.wait(5)

        # If thread.txn_info is empty, the test failed as iteration did not
        # continue after add_batch().
        self.assertIsNotNone(thread.txn_info, "iterable failed to return txn")
        self.assertEquals(txn.payload, thread.txn_info.txn.payload)

        # Continue with normal shutdown/cleanup.
        scheduler.finalize()
        scheduler.set_transaction_execution_result(txn.header_signature, False,
                                                   None)
        with self.assertRaises(StopIteration):
            next(iterable)