コード例 #1
0
ファイル: test_block.py プロジェクト: CeON/pmpi
 def test_wrong_limit(self):
     block = Block.from_operations_list(BlockRev(), int(time.time()), self.operations)
     block.operations_limit = Block.MAX_OPERATIONS + 1
     block.mine()
     sign_object(self.public_key, self.private_key, block)
     with self.assertRaisesRegex(Block.VerifyError, "operations_limit out of range"):
         block.verify()
コード例 #2
0
ファイル: test_block.py プロジェクト: CeON/pmpi
    def test_no_database(self):
        with self.assertRaisesRegex(pmpi.database.Database.InitialisationError, "initialise database first"):
            Block.get_ids_list()

        with self.assertRaisesRegex(pmpi.database.Database.InitialisationError, "initialise database first"):
            Block.get(sha256(b'something').digest())

        private_key = SigningKey.generate()
        public_key = PublicKey.from_signing_key(private_key)

        operations = [
            Operation(OperationRev(), 'http://example0.com/', [public_key]),
            Operation(OperationRev(), 'http://example1.com/', [public_key])
        ]

        for op in operations:
            sign_object(public_key, private_key, op)

        block = Block.from_operations_list(BlockRev(), int(time.time()), operations)
        block.mine()
        sign_object(public_key, private_key, block)

        with self.assertRaisesRegex(pmpi.database.Database.InitialisationError, "initialise database first"):
            block.put()

        with self.assertRaisesRegex(pmpi.database.Database.InitialisationError, "initialise database first"):
            block.remove()
コード例 #3
0
ファイル: test_block.py プロジェクト: CeON/pmpi
 def test_too_much(self):
     block = Block.from_operations_list(BlockRev(), int(time.time()), self.operations)
     block.operations_limit = Block.MIN_OPERATIONS
     block.mine()
     sign_object(self.public_key, self.private_key, block)
     with self.assertRaisesRegex(Block.VerifyError, "number of operations doesn't satisfy limitations"):
         block.verify()
コード例 #4
0
ファイル: test_operation.py プロジェクト: CeON/pmpi
    def setUp(self):
        self.private_key = SigningKey.generate(curve=NIST256p)
        self.public_key = PublicKey.from_signing_key(self.private_key)

        self.operation = Operation(OperationRev(), 'http://example.com/', [self.public_key])

        sign_object(self.public_key, self.private_key, self.operation)
コード例 #5
0
ファイル: user.py プロジェクト: CeON/pmpi
    def sign_object(self, obj):
        """
        Sign given operation by this user.

        :param obj: operation to sign
        """
        sign_object(self._public_key, self._private_key, obj)
コード例 #6
0
ファイル: test_blockchain.py プロジェクト: CeON/pmpi
    def test_wrong_operations(self):
        operations = self.add_operations()
        blocks = self.add_blocks(operations)
        bc = get_blockchain()

        with patch.object(BlockChain, '_get_new_blocks', return_value=blocks):
            bc.update_blocks()

        with self.assertRaisesRegex(Block.VerifyError, "some of the new operations have been added to the block already"):
            blocks[5].extend_operations([operations[9]])

        illegal_operation = Operation(operations[7].get_rev(), 'illegal address', [])
        sign_object(self.public_keys[2], self.private_keys[2], illegal_operation)

        blocks[5].extend_operations([illegal_operation])
        blocks[5].mine()

        with self.assertRaisesRegex(Block.ChainError, "operations are creating tree inside the block"):
            blocks[5].put()

        blocks[2].extend_operations([operations[6]])
        blocks[2].mine()
        with self.assertRaisesRegex(Block.ChainError, "operation's previous_operation_rev is not pointing at "
                                                      "the last operation on current blockchain"):
            blocks[2].put()
コード例 #7
0
ファイル: test_operation.py プロジェクト: CeON/pmpi
    def test_3_get_and_remove(self):
        sign_object(self.public_key, self.private_key, self.operations[0])

        self.operations[1] = Operation(OperationRev.from_obj(self.operations[0]),
                                       self.operations[1].address,
                                       self.operations[1].owners)

        for op in self.operations:
            sign_object(self.public_key, self.private_key, op)
            op.put()

        for op in self.operations:
            new_op = Operation.get(op.id)
            self.assertEqual(new_op.id, op.id)

        self.assertCountEqual(Operation.get_ids_list(), [op.id for op in self.operations])

        self.operations[1].remove()
        self.operations[0].remove()

        self.assertCountEqual(Operation.get_ids_list(), [self.operations[2].id])

        self.operations[2].remove()

        for op in self.operations:
            with self.assertRaises(Operation.DoesNotExist):
                op.remove()

        self.assertEqual(Operation.get_ids_list(), [])
コード例 #8
0
ファイル: test_identifier.py プロジェクト: CeON/pmpi
 def setUp(self):
     self.private_key = SigningKey.generate()
     self.public_keys = tuple(PublicKey.from_signing_key(SigningKey.generate()) for _ in range(2))
     self.operation = Operation(OperationRev(), 'http://example.com', self.public_keys)
     sign_object(PublicKey.from_signing_key(self.private_key), self.private_key, self.operation)
     self.uuid = self.operation.uuid
     self.identifier = Identifier.from_operation(self.operation)
コード例 #9
0
ファイル: test_block.py プロジェクト: CeON/pmpi
    def test_4_wrong_previous_block(self):
        self.blocks[0].previous_block_rev = BlockRev.from_id(double_sha(b'something'))
        self.blocks[0].mine()
        sign_object(self.public_key, self.private_key, self.blocks[0])

        with self.assertRaisesRegex(Block.ChainError, "previous_block_rev does not exist"):
            self.blocks[0].verify()
コード例 #10
0
ファイル: test_block.py プロジェクト: CeON/pmpi
    def setUp(self):
        self.private_key = SigningKey.generate()
        self.public_key = PublicKey.from_signing_key(self.private_key)

        self.operations = [Operation(
            OperationRev(), 'http://example.com/', [self.public_key]) for _ in range(Block.MIN_OPERATIONS + 1)]
        for op in self.operations:
            sign_object(self.public_key, self.private_key, op)
コード例 #11
0
ファイル: test_operation.py プロジェクト: CeON/pmpi
    def test_put_operation0_and_copy(self):
        sign_object(self.public_keys[0], self.private_keys[0], self.operation[0])
        self.operation[0].put()

        copied_op = Operation(self.operation[0].previous_operation_rev,
                              self.operation[0].address,
                              self.operation[0].owners)
        sign_object(self.public_keys[1], self.private_keys[1], copied_op)

        with self.assertRaisesRegex(Operation.VerifyError, "trying to create a minting operation for an existing uuid"):
            copied_op.put()
コード例 #12
0
ファイル: test_operation.py プロジェクト: CeON/pmpi
    def test_0_operation(self):
        with self.assertRaisesRegex(Operation.VerifyError, "object is not signed"):
            self.operation[0].raw()  # attempt to call .raw() on unsigned operation

        with self.assertRaisesRegex(Operation.VerifyError, "object is not signed"):
            self.operation[0].verify()  # attempt to verify operation without signing

        with self.assertRaisesRegex(Operation.VerifyError, "wrong signature"):
            # wrong because of incompatibility between public (self.public_key[1]) and private (self.private_key[0])
            sign_object(self.public_keys[1], self.private_keys[0], self.operation[0])
            self.operation[0].verify()

        sign_object(self.public_keys[1], self.private_keys[1], self.operation[0])
        self.operation[0].verify()
コード例 #13
0
ファイル: test_operation.py プロジェクト: CeON/pmpi
    def test_put_operation1(self):
        sign_object(self.public_keys[0], self.private_keys[0], self.operation[0])
        self.operation[0].put()

        self.operation[1] = Operation._construct_with_uuid(OperationRev(),
                                                           self.operation[0].uuid,
                                                           self.operation[1].address,
                                                           self.operation[1].owners)
        sign_object(self.public_keys[0], self.private_keys[0], self.operation[1])

        with self.assertRaisesRegex(Operation.UUIDError,
                                    "UUID of the minting operation does not fulfill the requirements"):
            self.operation[1].put()

        self.operation[1] = Operation(OperationRev.from_obj(self.operation[0]),
                                      self.operation[1].address,
                                      self.operation[1].owners)

        sign_object(self.public_keys[0], self.private_keys[0], self.operation[1])

        with self.assertRaises(Operation.OwnershipError):
            self.operation[1].put()

        sign_object(self.public_keys[1], self.private_keys[1], self.operation[1])

        self.operation[1].put()
コード例 #14
0
ファイル: test_operation.py プロジェクト: CeON/pmpi
    def test_owners(self):
        self.operation = Operation(self.operation.previous_operation_rev,
                                   self.operation.address,
                                   [self.public_key, self.public_key])
        sign_object(self.public_key, self.private_key, self.operation)

        with self.assertRaisesRegex(Operation.VerifyError, "duplicated owners"):
            self.operation.verify()

        self.operation = Operation(self.operation.previous_operation_rev,
                                   self.operation.address,
                                   [])
        sign_object(self.public_key, self.private_key, self.operation)

        self.assertTrue(self.operation.verify())
コード例 #15
0
ファイル: test_operation.py プロジェクト: CeON/pmpi
    def test_no_database(self):
        with self.assertRaisesRegex(pmpi.database.Database.InitialisationError, "initialise database first"):
            Operation.get_ids_list()

        with self.assertRaisesRegex(pmpi.database.Database.InitialisationError, "initialise database first"):
            Operation.get(sha256(b'something').digest())

        operation = Operation(OperationRev(), 'http://example.com/', [])
        sk = SigningKey.generate()
        sign_object(PublicKey.from_signing_key(sk), sk, operation)

        with self.assertRaisesRegex(pmpi.database.Database.InitialisationError, "initialise database first"):
            operation.put(BlockRev())

        with self.assertRaisesRegex(pmpi.database.Database.InitialisationError, "initialise database first"):
            operation.remove(BlockRev())
コード例 #16
0
ファイル: test_blockchain.py プロジェクト: CeON/pmpi
    def test_only_one_genesis_block(self):
        ops = self.add_operations()
        blocks = [
            Block.from_operations_list(BlockRev(), 42, [ops[1], ops[3], ops[4]]),
            Block.from_operations_list(BlockRev(), 43, [ops[5], ops[7], ops[9]])
        ]

        for block in blocks:
            block.mine()
            sign_object(self.public_keys[0], self.private_keys[0], block)

        blocks[0].put()

        for block in blocks:
            with self.assertRaisesRegex(Block.GenesisBlockDuplicationError, "trying to create multiple genesis blocks"):
                block.put()
コード例 #17
0
ファイル: test_identifier.py プロジェクト: CeON/pmpi
    def test_no_database(self):
        with self.assertRaisesRegex(pmpi.database.Database.InitialisationError, "initialise database first"):
            Identifier.get_uuid_list()

        with self.assertRaisesRegex(pmpi.database.Database.InitialisationError, "initialise database first"):
            Identifier.get(uuid4())

        operation = Operation(OperationRev(), 'http://example.com/', [])
        sk = SigningKey.generate()
        sign_object(PublicKey.from_signing_key(sk), sk, operation)
        identifier = Identifier.from_operation(operation)

        with self.assertRaisesRegex(pmpi.database.Database.InitialisationError, "initialise database first"):
            identifier.put()

        with self.assertRaisesRegex(pmpi.database.Database.InitialisationError, "initialise database first"):
            identifier.remove()
コード例 #18
0
ファイル: test_block.py プロジェクト: CeON/pmpi
    def setUp(self):
        self.private_key = SigningKey.generate()
        self.public_key = PublicKey.from_signing_key(self.private_key)
        self.timestamp = int(time.time())

        self.operations = (
            Operation(OperationRev(), 'http://example1.com/', [self.public_key]),
            Operation(OperationRev(), 'http://example2.com/', [self.public_key])
        )

        for op in self.operations:
            sign_object(self.public_key, self.private_key, op)

        self.block = Block.from_operations_list(BlockRev(), self.timestamp, self.operations)

        self.block.mine()
        sign_object(self.public_key, self.private_key, self.block)
コード例 #19
0
ファイル: test_identifier.py プロジェクト: CeON/pmpi
    def setUp(self):
        initialise_database('test_database_file')

        self.operations = [
            Operation(OperationRev(), 'http://example.com/' + url, [PublicKey.from_signing_key(SigningKey.generate())])
            for url in ('first/', 'second/')]

        for op in self.operations:
            sk = SigningKey.generate()
            sign_object(PublicKey.from_signing_key(sk), sk, op)

        block = Block.from_operations_list(BlockRev(), 42, self.operations)
        block.mine()
        sk = SigningKey.generate()
        sign_object(PublicKey.from_signing_key(sk), sk, block)
        block.put()

        self.identifiers = [Identifier.from_operation(op) for op in self.operations]
コード例 #20
0
ファイル: test_operation.py プロジェクト: CeON/pmpi
    def test_put_operation_2(self):
        with self.assertRaisesRegex(Operation.ChainError, "previous_operation_rev does not exist"):
            self.operation[2] = Operation(OperationRev.from_id(
                sha256(b'wrong hash').digest()), self.operation[2].address, self.operation[2].owners)

        sign_object(self.public_keys[0], self.private_keys[0], self.operation[1])

        self.operation[2] = Operation._construct_with_uuid(OperationRev.from_obj(self.operation[1]),
                                                           self.operation[2].uuid,
                                                           self.operation[2].address,
                                                           self.operation[2].owners)

        sign_object(self.public_keys[1], self.private_keys[1], self.operation[2])

        with self.assertRaisesRegex(Operation.UUIDError, "UUID mismatch"):
            self.operation[2].put()

        self.operation[2] = Operation._construct_with_uuid(OperationRev(),
                                                           self.operation[2].uuid,
                                                           self.operation[2].address,
                                                           self.operation[2].owners)

        sign_object(self.public_keys[1], self.private_keys[1], self.operation[2])
        self.operation[2].put()

        with self.assertRaisesRegex(Operation.DuplicationError, "object id already in the database"):
            self.operation[2].put()
コード例 #21
0
ファイル: test_operation.py プロジェクト: CeON/pmpi
    def test_2_operation(self):
        sign_object(self.public_keys[1], self.private_keys[1], self.operation[0])
        self.operation[1] = Operation(OperationRev.from_obj(self.operation[0]),
                                      'http://new.example.com/', [self.public_keys[2]])
        sign_object(self.public_keys[2], self.private_keys[2], self.operation[1])
        self.operation[2] = Operation._construct_with_uuid(OperationRev.from_obj(self.operation[1]), uuid4(),
                                                           'http://new2.example.com', [])

        with self.assertRaisesRegex(Operation.UUIDError, "UUID mismatch"):
            sign_object(self.public_keys[2], self.private_keys[2], self.operation[2])
            self.operation[2].verify()

        self.operation[2] = Operation(OperationRev.from_obj(self.operation[1]), 'http://new2.example.com', [])
        sign_object(self.public_keys[2], self.private_keys[2], self.operation[2])
        self.operation[2].verify()
コード例 #22
0
ファイル: test_operation.py プロジェクト: CeON/pmpi
    def test_1_operation(self):
        sign_object(self.public_keys[0], self.private_keys[0], self.operation[0])
        self.operation[1] = Operation(OperationRev.from_obj(self.operation[0]),
                                      'http://illegal.example.com/', [self.public_keys[2]])

        with self.assertRaises(Operation.OwnershipError):
            sign_object(self.public_keys[0], self.private_keys[0], self.operation[1])
            self.operation[1].verify()

        self.operation[1] = Operation(OperationRev.from_obj(self.operation[0]),
                                      'http://new.example.com/', [self.public_keys[2]])
        sign_object(self.public_keys[2], self.private_keys[2], self.operation[1])

        self.assertTrue(self.operation[1].verify())
コード例 #23
0
ファイル: test_operation.py プロジェクト: CeON/pmpi
    def test_2_put(self):

        sign_object(self.public_key, self.private_key, self.operations[0])
        self.operations[0].put()

        with self.assertRaisesRegex(Operation.DuplicationError, "object id already in the database"):
            self.operations[0].put()

        self.operations[1] = Operation(OperationRev.from_obj(self.operations[0]),
                                       self.operations[1].address,
                                       self.operations[1].owners)
        sign_object(self.public_key, self.private_key, self.operations[1])

        self.operations[1].put()

        sign_object(self.public_key, self.private_key, self.operations[2])
        self.operations[2].put()

        revision_id_list = Operation.get_ids_list()

        self.assertEqual(len(revision_id_list), 3)
        self.assertCountEqual(revision_id_list, [op.id for op in self.operations])
コード例 #24
0
ファイル: test_blockchain.py プロジェクト: CeON/pmpi
    def add_operations(self):
        """
        create operations:
        for uuid[0]: op[0] -> op[2] -> op[6] -> op[8];
        for uuid[1]: op[1] -> op[3] -> op[4];
        for uuid[2]: op[5] -> op[7] -> op[9]
        """
        ops = [
            Operation(OperationRev(), 'http://example1.com/', [self.public_keys[0]]),
            Operation(OperationRev(), 'http://example2.com/', [self.public_keys[1]]),
        ]

        sign_object(self.public_keys[0], self.private_keys[0], ops[0])
        sign_object(self.public_keys[0], self.private_keys[0], ops[1])

        ops.extend([
            Operation(OperationRev.from_obj(ops[0]), 'http://example1.com/v2/', [self.public_keys[0]]),
            Operation(OperationRev.from_obj(ops[1]), 'http://example2.com/v2/', [self.public_keys[1]])
        ])

        sign_object(self.public_keys[0], self.private_keys[0], ops[2])
        sign_object(self.public_keys[1], self.private_keys[1], ops[3])

        ops.append(
            Operation(OperationRev.from_obj(ops[3]), 'http://example2.com/v3/', [self.public_keys[1]])
        )

        sign_object(self.public_keys[1], self.private_keys[1], ops[4])

        ops.extend([
            Operation(OperationRev(), 'http://example3.com/', [self.public_keys[1], self.public_keys[2]]),
            Operation(OperationRev.from_obj(ops[2]), 'http://example1.com/v3/', [self.public_keys[0], self.public_keys[2]])
        ])

        sign_object(self.public_keys[2], self.private_keys[2], ops[5])
        sign_object(self.public_keys[0], self.private_keys[0], ops[6])

        ops.extend([
            Operation(OperationRev.from_obj(ops[5]), 'http://example3.com/v2/', [self.public_keys[2]]),
            Operation(OperationRev.from_obj(ops[6]), 'http://example1.com/v4/', [self.public_keys[2]])
        ])

        sign_object(self.public_keys[1], self.private_keys[1], ops[7])
        sign_object(self.public_keys[2], self.private_keys[2], ops[8])

        ops.append(
            Operation(OperationRev.from_obj(ops[7]), 'http://example3.com/v3/', [self.public_keys[2]])
        )

        sign_object(self.public_keys[2], self.private_keys[2], ops[9])

        return ops
コード例 #25
0
ファイル: test_block.py プロジェクト: CeON/pmpi
    def setUp(self):
        initialise_database('test_database_file')

        self.private_key = SigningKey.generate()
        self.public_key = PublicKey.from_signing_key(self.private_key)
        # self.uuids = [uuid4() for _ in range(3)]

        self.operations = [[
            Operation(OperationRev(),'http://example0.com/v0/', [self.public_key]),
            Operation(OperationRev(),'http://example1.com/v0/', [self.public_key])
        ]]

        for op in self.operations[0]:
            sign_object(self.public_key, self.private_key, op)

        self.operations.append([
            Operation(OperationRev.from_obj(self.operations[0][0]), 'http://example0.com/v1/', [self.public_key]),
            Operation(OperationRev.from_obj(self.operations[0][1]), 'http://example1.com/v1/', [self.public_key]),
            Operation(OperationRev(), 'http://example2.com/v0/', [self.public_key])
        ])

        for op in self.operations[1]:
            sign_object(self.public_key, self.private_key, op)

        self.operations.append([
            Operation(OperationRev.from_obj(self.operations[1][0]),'http://example0.com/v2/', [self.public_key]),
            Operation(OperationRev.from_obj(self.operations[1][1]), 'http://example1.com/v2/', [self.public_key])
        ])

        for op in self.operations[2]:
            sign_object(self.public_key, self.private_key, op)

        self.operations.append([
            Operation(OperationRev.from_obj(self.operations[1][1]), 'http://alternative1.com/', [self.public_key]),
            Operation(OperationRev.from_obj(self.operations[1][2]), 'http://alternative2.com/', [self.public_key])
        ])

        for op in self.operations[3]:
            sign_object(self.public_key, self.private_key, op)

        timestamp = int(time.time()) - 100

        self.blocks = [Block.from_operations_list(BlockRev(), timestamp, self.operations[0])]
        self.blocks[0].mine()
        sign_object(self.public_key, self.private_key, self.blocks[0])
        self.blocks.append(
            Block.from_operations_list(BlockRev.from_obj(self.blocks[0]), timestamp + 20, self.operations[1]))
        self.blocks[1].mine()
        sign_object(self.public_key, self.private_key, self.blocks[1])
        self.blocks.append(
            Block.from_operations_list(BlockRev.from_obj(self.blocks[1]), timestamp + 40, self.operations[2]))
        self.blocks[2].mine()
        sign_object(self.public_key, self.private_key, self.blocks[2])
        self.blocks.append(
            Block.from_operations_list(BlockRev.from_obj(self.blocks[1]), timestamp + 60, self.operations[3]))
        self.blocks[3].mine()
        sign_object(self.public_key, self.private_key, self.blocks[3])
コード例 #26
0
ファイル: test_operation.py プロジェクト: CeON/pmpi
    def test_put_operation0(self):
        with self.assertRaisesRegex(Operation.VerifyError, "object is not signed"):
            self.operation[0].put()

        sign_object(self.public_keys[0], self.private_keys[0], self.operation[0])
        self.operation[0].put()
コード例 #27
0
ファイル: test_operation.py プロジェクト: CeON/pmpi
 def test_1_get_from_empty(self):
     with self.assertRaises(Operation.DoesNotExist):
         sign_object(self.public_key, self.private_key, self.operations[0])
         Operation.get(self.operations[0].id)
コード例 #28
0
ファイル: test_blockchain.py プロジェクト: CeON/pmpi
    def add_blocks(self, ops):
        """
        create blocks:
        blocks[0] -> blocks[1] -> blocks[2] -> blocks[3] -> blocks[5]
                                           \-> blocks[4] -> blocks[6]
        """
        start_time = 42
        blocks = [Block.from_operations_list(BlockRev(), start_time, [ops[0], ops[1]])]
        blocks[0].mine()
        sign_object(self.public_keys[0], self.private_keys[0], blocks[0])
        blocks.append(Block.from_operations_list(BlockRev.from_obj(blocks[0]), start_time + 10, [ops[2], ops[6]]))
        blocks[1].mine()
        sign_object(self.public_keys[0], self.private_keys[0], blocks[1])
        blocks.append(Block.from_operations_list(BlockRev.from_obj(blocks[1]), start_time + 20, [ops[3], ops[5]]))
        blocks[2].mine()
        sign_object(self.public_keys[0], self.private_keys[0], blocks[2])
        blocks.append(Block.from_operations_list(BlockRev.from_obj(blocks[2]), start_time + 30, [ops[8], ops[4]]))
        blocks[3].mine()
        sign_object(self.public_keys[0], self.private_keys[0], blocks[3])
        blocks.append(Block.from_operations_list(BlockRev.from_obj(blocks[2]), start_time + 40, [ops[4], ops[7]]))
        blocks[4].mine()
        sign_object(self.public_keys[0], self.private_keys[0], blocks[4])
        blocks.append(Block.from_operations_list(BlockRev.from_obj(blocks[3]), start_time + 50, [ops[7], ops[9]]))
        blocks[5].mine()
        sign_object(self.public_keys[0], self.private_keys[0], blocks[5])
        blocks.append(Block.from_operations_list(BlockRev.from_obj(blocks[4]), start_time + 60, [ops[8], ops[9]]))
        blocks[6].mine()
        sign_object(self.public_keys[0], self.private_keys[0], blocks[6])

        return blocks
コード例 #29
0
ファイル: profiling_1.py プロジェクト: CeON/pmpi
def test():
    try:
        os.remove('test_database_file')
    except OSError:
        pass
    initialise_database('test_database_file')

    obj_private_key = SigningKey.generate()
    obj_public_key = PublicKey.from_signing_key(obj_private_key)
    obj_uuids = [uuid4() for _ in range(3)]

    obj_operations = [[
        Operation(OperationRev(), 'http://example0.com/v0/', [obj_public_key]),
        Operation(OperationRev(), 'http://example1.com/v0/', [obj_public_key])
    ]]

    for op in obj_operations[0]:
        sign_object(obj_public_key, obj_private_key, op)

    obj_operations.append([
        Operation(OperationRev.from_obj(obj_operations[0][0]), 'http://example0.com/v1/', [obj_public_key]),
        Operation(OperationRev.from_obj(obj_operations[0][1]), 'http://example1.com/v1/', [obj_public_key]),
        Operation(OperationRev(), 'http://example2.com/v0/', [obj_public_key])
    ])

    for op in obj_operations[1]:
        sign_object(obj_public_key, obj_private_key, op)

    obj_operations.append([
        Operation(OperationRev.from_obj(obj_operations[1][0]), 'http://example0.com/v2/', [obj_public_key]),
        Operation(OperationRev.from_obj(obj_operations[1][1]), 'http://example1.com/v2/', [obj_public_key])
    ])

    for op in obj_operations[2]:
        sign_object(obj_public_key, obj_private_key, op)

    obj_operations.append([
        Operation(OperationRev.from_obj(obj_operations[1][1]), 'http://alternative1.com/', [obj_public_key]),
        Operation(OperationRev.from_obj(obj_operations[1][2]), 'http://alternative2.com/', [obj_public_key])
    ])

    for op in obj_operations[3]:
        sign_object(obj_public_key, obj_private_key, op)

    timestamp = int(time.time()) - 100

    obj_blocks = [Block.from_operations_list(BlockRev(), timestamp, obj_operations[0])]
    obj_blocks[0].mine()
    sign_object(obj_public_key, obj_private_key, obj_blocks[0])
    obj_blocks.append(
        Block.from_operations_list(BlockRev.from_obj(obj_blocks[0]), timestamp + 20, obj_operations[1]))
    obj_blocks[1].mine()
    sign_object(obj_public_key, obj_private_key, obj_blocks[1])
    obj_blocks.append(
        Block.from_operations_list(BlockRev.from_obj(obj_blocks[1]), timestamp + 40, obj_operations[2]))
    obj_blocks[2].mine()
    sign_object(obj_public_key, obj_private_key, obj_blocks[2])
    obj_blocks.append(
        Block.from_operations_list(BlockRev.from_obj(obj_blocks[1]), timestamp + 60, obj_operations[3]))
    obj_blocks[3].mine()
    sign_object(obj_public_key, obj_private_key, obj_blocks[3])

    for block in obj_blocks:
        block.put()

    for block in obj_blocks:
        new_block = Block.get(block.id)
        assert new_block.id == block.id

    for block in obj_blocks[:2]:
        try:
            block.remove()
            raise AssertionError
        except Block.ChainOperationBlockedError:
            pass

    assert sorted(Block.get_ids_list()) == sorted([block.id for block in obj_blocks])

    obj_blocks[2].remove()

    for block in obj_blocks[:2]:
        try:
            block.remove()
            raise AssertionError
        except Block.ChainOperationBlockedError:
            pass

    obj_blocks[3].remove()

    try:
        obj_blocks[0].remove()
        raise AssertionError
    except Block.ChainOperationBlockedError:
        pass

    obj_blocks[1].remove()
    obj_blocks[0].remove()

    for block in obj_blocks:
        try:
            block.remove()
            raise AssertionError
        except Block.DoesNotExist:
            pass

    assert Block.get_ids_list() == []

    close_database()
    os.remove('test_database_file')