def setUp(self):
        # Faking IOThread
        registerAsIOThread()

        # Object creation and preperation
        self.dispersy = Dispersy(ManualEnpoint(0),
                                 unicode("dispersy_temporary"))
        self.api = MarketAPI(MarketDatabase(MemoryBackend()))
        self.api.db.backend.clear()

        user, _, priv = self.api.create_user()
        self.bank, _, _ = self.api.create_user()
        self.user = user
        self.private_key = priv

        self.dispersy._database.open()
        self.master_member = DummyMember(self.dispersy, 1, "a" * 20)
        self.member = self.dispersy.get_member(
            private_key=self.private_key.decode("HEX"))
        self.community = MortgageMarketCommunity.init_community(
            self.dispersy, self.master_member, self.member)

        self.community.api = self.api
        self.community.user = self.user
        self.api.community = self.community

        # Add our conversion to the community.
        self.conversion = MortgageMarketConversion(self.community)
        self.community._conversions = []
        self.community.add_conversion(self.conversion)

        self.setupModels()
示例#2
0
    def start_dispersy(self):
        from dispersy.dispersy import Dispersy
        from dispersy.endpoint import StandaloneEndpoint
        from market import Global
        from market.community.community import MortgageMarketCommunity
        from twisted.internet.task import LoopingCall

        self.dispersy = Dispersy(StandaloneEndpoint(self.port, '0.0.0.0'),
                                 unicode('.'),
                                 u'dispersy-%s.db' % self.database_prefix)
        self.dispersy.statistics.enable_debug_statistics(True)
        self.dispersy.start(autoload_discovery=True)

        my_member = self.dispersy.get_member(
            private_key=self.private_key.decode("HEX"))

        master_member = self.dispersy.get_member(public_key=Global.MASTER_KEY)
        self.community = MortgageMarketCommunity.init_community(
            self.dispersy, master_member, my_member)
        self.community.api = self.api
        self.community.user = self.user
        self.api.community = self.community

        # Run the scenario every 3 seconds
        LoopingCall(self._scenario).start(3.0)

        # Send messages from the queue every 3 seconds
        LoopingCall(self.api.outgoing_queue.process).start(3.0)
        LoopingCall(self.api.incoming_queue.process).start(3.0)
示例#3
0
    def enable_switch(self):
        self.is_switch = True

        # Start Dispersy. First, fix the bootstrap server pointer
        os.environ[BOOTSTRAP_FILE_ENVNAME] = 'bootstrap.txt'

        self.dispersy = Dispersy(StandaloneEndpoint(self.options['port'], '0.0.0.0'),
                                 os.path.join(self.cache_dir, u'dispersy'), u'dispersy.db')
        self.dispersy.statistics.enable_debug_statistics(True)
        self.dispersy.start(autoload_discovery=True)

        self.load_money_community()
class DispersyManager(object):

    def __init__(self, port, state_dir, keypair_fn):
        self.logger = logging.getLogger(self.__class__.__name__)
        self.port = port
        self.state_dir = state_dir
        self.keypair_fn = keypair_fn or os.path.join(self.state_dir, 'market.pem')

        self.session = None
        self.dispersy = None
        self.community = None

    def start_dispersy(self):
        self.logger.info('Starting Dispersy on port %d with state dir %s', self.port, self.state_dir)
        endpoint = StandaloneEndpoint(self.port)
        self.dispersy = Dispersy(endpoint, self.state_dir)
        return self.dispersy.start(autoload_discovery=True)

    def start_market(self, *args, **kwargs):

        if os.path.exists(self.keypair_fn):
            self.logger.info('Using existing keypair')
            with open(self.keypair_fn, 'rb') as keypair_fp:
                keypair_bin = keypair_fp.read()
            keypair = LibNaCLSK(binarykey=keypair_bin)
        else:
            self.logger.info('Creating new keypair')
            keypair = LibNaCLSK()
            with open(self.keypair_fn, 'wb') as keypair_fp:
                keypair_fp.write(keypair.key.sk)
                keypair_fp.write(keypair.key.seed)

        self.logger.debug('Public key %s', urlsafe_b64encode(keypair.pub().key_to_bin()))
        member = self.dispersy.get_member(private_key=keypair.key_to_bin())

        database = InternetOfMoneyDB(self.state_dir)
        manager = BankManager(database, cache_dir=self.state_dir)

        money_community = self.dispersy.define_auto_load(MoneyCommunity, member, load=True)[0]
        money_community.bank_managers = {manager.get_bank_id(): manager}

        self.logger.info('Starting MarketCommunity')
        kwargs['money_community'] = money_community
        self.community = self.dispersy.define_auto_load(MarketCommunity, member, load=True, args=args, kargs=kwargs)[0]

    def stop_dispersy(self):
        self.logger.info('Stopping Dispersy')
        return self.dispersy.stop()
示例#5
0
    def dispersy(self, callback):
        # start Dispersy
        dispersy = Dispersy.get_instance(callback, u".")
        dispersy.socket = communication.get_socket(callback, dispersy)

        # load/join discovery community
        public_key = "3081a7301006072a8648ce3d020106052b81040027038192000406b34f060c416e452fd31fb1770c2f475e928effce751f2f82565bec35c46a97fb8b375cca4ac5dc7d93df1ba594db335350297f003a423e207b53709e6163b7688c0f60a9cf6599037829098d5fbbfe786e0cb95194292f241ff6ae4d27c6414f94de7ed1aa62f0eb6ef70d2f5af97c9aade8266eb85b14296ed2004646838c056d1d9ad8a509b69f81fbc726201b57".decode("HEX")
        if True:
            # when crypto.py is disabled a public key is slightly
            # different...
            public_key = ";".join(("60", public_key[:60].encode("HEX"), ""))
        master = Member(public_key)
        try:
            self._discovery = DiscoveryCommunity.load_community(master)
        except ValueError:
            ec = ec_generate_key(u"low")
            self._my_member = Member(ec_to_public_bin(ec), ec_to_private_bin(ec))
            self._discovery = DiscoveryCommunity.join_community(master, self._my_member)
        else:
            self._my_member = self._discovery.my_member

        dispersy.define_auto_load(PreviewCommunity, (self._discovery,))
        dispersy.define_auto_load(SquareCommunity, (self._discovery,))

        # load squares
        for master in SquareCommunity.get_master_members():
            yield 1.0
            community = dispersy.get_community(master.mid)
            self._communities.append(community)
    def create_community(self, cls, *args, **kwargs):
        temp_dir = unicode(mkdtemp(suffix="_dispersy_test_session"))

        dispersy = Dispersy(StandaloneEndpoint(0), temp_dir, database_filename=u':memory:')
        dispersy.start(autoload_discovery=False)

        my_member = dispersy.get_new_member(u'curve25519')

        if self.communities:
            head_community = self.communities[0]
            master_member = dispersy.get_member(mid=head_community._master_member.mid)
            community = cls.init_community(dispersy, master_member, my_member, *args, **kwargs)
            community.candidates.clear()
            community.add_discovered_candidate(Candidate(head_community.dispersy.lan_address, False))
        else:
            community = cls.create_community(dispersy, my_member, *args, **kwargs)
            community.candidates.clear()

        self.attach_hooks(community)
        self.communities.append(community)

        return community
示例#7
0
        def run():
            crypto = ECCrypto()
            dispersy = Dispersy(
                StandaloneEndpoint(options["port"], options["ip"]),
                options["statedir"], u'dispersy.db', crypto)
            if not dispersy.start():
                raise RuntimeError("Unable to start Dispersy")
            master_member = MarketCommunity.get_master_members(dispersy)[0]
            my_member = dispersy.get_member(private_key=crypto.key_to_bin(
                crypto.generate_key(u"curve25519")))
            MarketCommunity.init_community(dispersy, master_member, my_member)

            self._stopping = False

            def signal_handler(sig, frame):
                logger.info("Received signal '%s' in %s (shutting down)" %
                            (sig, frame))
                if not self._stopping:
                    self._stopping = True
                    dispersy.stop()
                    reactor.stop()

            signal.signal(signal.SIGINT, signal_handler)
            signal.signal(signal.SIGTERM, signal_handler)
 def start_dispersy(self):
     self.logger.info('Starting Dispersy on port %d with state dir %s', self.port, self.state_dir)
     endpoint = StandaloneEndpoint(self.port)
     self.dispersy = Dispersy(endpoint, self.state_dir)
     return self.dispersy.start(autoload_discovery=True)
示例#9
0
class Session(object):
    """
    This class represents an Internet-of-Money session.
    It contains methods to start, close and manage the session.
    """
    
    def __init__(self, options):
        self.options = options
        self.managers = {}
        self.money_community = None
        self.line_receiver = None
        self.database = None
        self.dispersy = None
        self.is_switch = False
        self.cache_dir = self.options['cache']
        self.api_manager = None
        self.input_deferreds = {}
        self.is_locked = True
        self.file_handler = None
        self.stream_handler = None

        # Make sure to use our own root certificates file
        dir_path = os.path.dirname(os.path.realpath(__file__))
        os.environ['SSL_CERT_FILE'] = os.path.join(dir_path, 'data', 'root_certs.pem')

    def start(self):
        if not os.path.exists(self.cache_dir):
            os.mkdir(self.cache_dir)

        # Setup the database
        self.database = InternetOfMoneyDB(self.cache_dir)

        self.init_logging()

        if self.options["switch"]:
            self.enable_switch()

        self.initialize_managers()

        # Start the HTTP API
        self.api_manager = RESTManager(self)
        self.api_manager.start(self.options['apiport'])

        for manager in self.managers.itervalues():
            manager.input_handler = self.input_handler

    @inlineCallbacks
    def stop(self):
        yield self.api_manager.stop()
        self.database.close()
        logging.getLogger().removeHandler(self.file_handler)
        logging.getLogger().removeHandler(self.stream_handler)

        if self.dispersy:
            yield self.dispersy.stop()
        yield succeed(None)

    def unlock(self, password):
        """
        Unlock the wallet using the provided password.
        """
        if not self.is_locked:
            return  # Already unlocked

        if not os.path.exists(os.path.join(self.cache_dir, 'pwhash')):
            raise RuntimeError("No password set!")

        with open(os.path.join(self.cache_dir, 'pwhash'), 'r') as hash_file:
            file_hash = hash_file.read()
            hex_hash = hashlib.sha1(bytes(password)).hexdigest()
            if file_hash != hex_hash:
                raise WrongPasswordException()

        for manager in self.managers.itervalues():
            manager.load_storage(password)

        self.is_locked = False

    def set_password(self, password):
        """
        Set the password for this wallet.
        """
        if os.path.exists(os.path.join(self.cache_dir, 'pwhash')):
            raise PasswordAlreadySetException()

        with open(os.path.join(self.cache_dir, 'pwhash'), 'w') as hash_file:
            hex_hash = hashlib.sha1(bytes(password)).hexdigest()
            hash_file.write(hex_hash)

        for manager in self.managers.itervalues():
            manager.load_storage(password)

    def input_handler(self, required_input):
        input_deferred = Deferred()
        self.input_deferreds[required_input.name] = input_deferred
        self.api_manager.root_endpoint.events_endpoint.send_input_request(required_input)
        return input_deferred

    def on_input(self, name, user_input):
        """
        This method is invoked when there is user input available.
        :param name: the name of the input, should match the name passed to iom_input_handler
        :type name: str
        :param user_input: a dictionary with the given user input.
        :type user_input: dict
        """
        if name not in self.input_deferreds:
            raise RuntimeError("Received input that we didn't expect!")

        self.input_deferreds[name].callback(user_input)
        del self.input_deferreds[name]
        
    def init_logging(self):
        # Initialize logging
        log.startLogging(sys.stdout)
        root = logging.getLogger()
        root.setLevel(logging.DEBUG)

        # Log critical messages to stderr
        self.stream_handler = logging.StreamHandler(sys.stderr)
        formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        self.stream_handler.setFormatter(formatter)
        self.stream_handler.setLevel(logging.CRITICAL)
        root.addHandler(self.stream_handler)

        self.file_handler = logging.handlers.WatchedFileHandler(
            os.environ.get("LOGFILE", os.path.join(self.cache_dir, "iom.log")))
        self.file_handler.setFormatter(formatter)
        self.file_handler.setLevel(logging.DEBUG)
        root.addHandler(self.file_handler)

    def initialize_managers(self):
        paypal_manager = PayPalManager(self.database, cache_dir=self.cache_dir)
        rabo_manager = RaboManager(self.database, cache_dir=self.cache_dir)
        abn_manager = ABNManager(self.database, cache_dir=self.cache_dir)
        ing_manager = INGManager(self.database, cache_dir=self.cache_dir)
        self.managers = {
            paypal_manager.get_bank_id(): paypal_manager,
            rabo_manager.get_bank_id(): rabo_manager,
            abn_manager.get_bank_id(): abn_manager,
            ing_manager.get_bank_id(): ing_manager,
        }

        if self.options['dummy']:
            dummy1_manager = Dummy1Manager(self.database, cache_dir=self.cache_dir)
            dummy2_manager = Dummy2Manager(self.database, cache_dir=self.cache_dir)
            dummy3_manager = Dummy3Manager(self.database, cache_dir=self.cache_dir)

            self.managers[dummy1_manager.get_bank_id()] = dummy1_manager
            self.managers[dummy2_manager.get_bank_id()] = dummy2_manager
            self.managers[dummy3_manager.get_bank_id()] = dummy3_manager

    def load_money_community(self):
        keypair_path = os.path.join(self.cache_dir, 'ec.pem')
        if os.path.exists(keypair_path):
            keypair = permid.read_keypair(keypair_path)
        else:
            keypair = permid.generate_keypair()
            permid.save_keypair(keypair, keypair_path)
        master_member = self.dispersy.get_member(private_key=keypair.key_to_bin())
        chain_community = self.dispersy.define_auto_load(MoneyChainCommunity, master_member, load=True, kargs={})[0]
        kwargs = {
            'database': self.database,
            'money_chain_community': chain_community,
            'tx_fee': self.options['txfee']
        }
        self.money_community = self.dispersy.define_auto_load(MoneyCommunity, master_member, load=True, kargs=kwargs)[0]
        self.money_community.bank_managers = self.managers

    def enable_switch(self):
        self.is_switch = True

        # Start Dispersy. First, fix the bootstrap server pointer
        os.environ[BOOTSTRAP_FILE_ENVNAME] = 'bootstrap.txt'

        self.dispersy = Dispersy(StandaloneEndpoint(self.options['port'], '0.0.0.0'),
                                 os.path.join(self.cache_dir, u'dispersy'), u'dispersy.db')
        self.dispersy.statistics.enable_debug_statistics(True)
        self.dispersy.start(autoload_discovery=True)

        self.load_money_community()
示例#10
0
class MarketApplication(QApplication):
    """
    This class represents the main Market application.
    """
    bank_status = {}
    port = 1236
    database_prefix = 'market'

    def __init__(self, *argv):
        QApplication.__init__(self, *argv)

    def initialize(self):
        self.initialize_api()

        # Load banks
        from market import Global
        from market.models.user import User

        for bank_name in Global.BANKS:
            bank = self.api._get_user(Global.BANKS[bank_name]) or User(
                public_key=Global.BANKS[bank_name], time_added=0)
            bank.post_or_put(self.api.db)
            self.api.create_profile(bank, {'role': 3})
        #
        self.private_key = None
        self.user = None
        self.community = None

        self.identify()
        #
        signal.signal(signal.SIGINT, self.close)
        signal.signal(signal.SIGQUIT, self.close)
        self.aboutToQuit.connect(self.close)

    def run(self):
        #print reactor
        # reactor.callWhenRunning(self.start_dispersy)
        # reactor.run()
        pass

    def close(self, *_):
        from twisted.internet import reactor
        self.dispersy.stop()
        reactor.stop()
        time.sleep(2)
        os._exit(1)

    def initialize_api(self):
        from market.api.api import MarketAPI
        from market.database.backends import PersistentBackend, MemoryBackend
        from market.database.database import MarketDatabase
        self._api = MarketAPI(
            MarketDatabase(PersistentBackend('.', u'sqlite/market.db')))

    def identify(self):
        """
        Identify the user to the system.

        If it already exists in the screen go on, if not create a user.
        """
        try:
            self.private_key = self.api.db.backend.get_option('user_key_priv')
            self.user = self.api.login_user(self.private_key.encode("HEX"))
            print "Using an existing user"
        except (KeyError, IndexError):
            user, _, priv = self.api.create_user()
            self.user = user
            self.private_key = priv
            print "Using a new user"

    def start_dispersy(self):
        from dispersy.dispersy import Dispersy
        from dispersy.endpoint import StandaloneEndpoint
        from market import Global
        from market.community.community import MortgageMarketCommunity
        from twisted.internet.task import LoopingCall

        self.dispersy = Dispersy(StandaloneEndpoint(self.port, '0.0.0.0'),
                                 unicode('.'),
                                 u'dispersy-%s.db' % self.database_prefix)
        self.dispersy.statistics.enable_debug_statistics(True)
        self.dispersy.start(autoload_discovery=True)

        my_member = self.dispersy.get_member(
            private_key=self.private_key.decode("HEX"))

        master_member = self.dispersy.get_member(public_key=Global.MASTER_KEY)
        self.community = MortgageMarketCommunity.init_community(
            self.dispersy, master_member, my_member)
        self.community.api = self.api
        self.community.user = self.user
        self.api.community = self.community

        # Run the scenario every 3 seconds
        LoopingCall(self._scenario).start(3.0)

        # Send messages from the queue every 3 seconds
        LoopingCall(self.api.outgoing_queue.process).start(3.0)
        LoopingCall(self.api.incoming_queue.process).start(3.0)

    def _scenario(self):
        for bank_id in Global.BANKS:
            user = self.api._get_user(Global.BANKS[bank_id])
            if user.id in self.api.user_candidate and self.bank_status[
                    bank_id] == False:
                print bank_id, " is ONLINE"
                self.bank_status[bank_id] = True
                self.profile = True
            else:
                if not bank_id in self.bank_status:
                    self.bank_status[bank_id] = False
                    print bank_id, " is OFFLINE"

        # pass

    @property
    def api(self):
        return self._api
    def setUp(self):
        # Faking IOThread
        registerAsIOThread()

        # Object creation and preperation
        self.dispersy = Dispersy(ManualEnpoint(0),
                                 unicode("dispersy_temporary_mc1"))
        self.dispersy_bank = Dispersy(ManualEnpoint(0),
                                      unicode("dispersy_temporary_mc2"))

        self.api = MarketAPI(
            MarketDatabase(PersistentBackend('.', u'borrower.db')))
        self.api_bank = MarketAPI(
            MarketDatabase(PersistentBackend('.', u'bank.db')))

        self.api.db.backend.clear()
        self.api_bank.db.backend.clear()

        self.user, _, priv_user = self.api.create_user()
        self.bank, _, priv_bank = self.api_bank.create_user()

        self.dispersy._database.open()
        self.dispersy_bank._database.open()

        self.master_member = DummyMember(self.dispersy, 1, "a" * 20)

        self.member = self.dispersy.get_member(
            private_key=priv_user.decode("HEX"))
        self.member_bank = self.dispersy_bank.get_member(
            private_key=priv_bank.decode("HEX"))

        self.community = MortgageMarketCommunity.init_community(
            self.dispersy, self.master_member, self.member)
        self.community_bank = MortgageMarketCommunity.init_community(
            self.dispersy_bank, self.master_member, self.member_bank)

        self.community.api = self.api
        self.community.user = self.user
        self.api.community = self.community

        self.community_bank.api = self.api_bank
        self.community_bank.user = self.bank
        self.api_bank.community = self.community_bank

        self.db = self.api.db.backend
        self.bank_db = self.api_bank.db.backend

        self.community.persistence = self.db
        self.community_bank.persistence = self.bank_db

        # Models
        self.mortgage = Mortgage(UUID('b97dfa1c-e125-4ded-9b1a-5066462c529c'),
                                 UUID('b97dfa1c-e125-4ded-9b1a-5066462c520c'),
                                 'ING', 80000, 1, 2.5, 1.5, 2.5, 36, 'A', [],
                                 STATUS.ACCEPTED)
        t = int(time.time())
        self.payload = (
            self.bank.id,
            self.user.id,
            self.mortgage,
            self.mortgage,
            2,
            1,
            'prev_hash_bene',
            'prev_hash_beni',
            'sig_bene',
            'sig_beni',
            t,
        )

        self.payload2 = (
            self.bank.id,
            '',
            self.mortgage,
            None,
            2,
            1,
            'prev_hash_bene',
            '',
            'sig_bene',
            '',
            t,
        )
class BlockchainDatabaseTest(unittest.TestCase, CustomAssertions):
    """
    Tests the blockchain functionality.

    PersistentBackend is neccesary as the MemoryBackend does not implement BlockChain!
    """
    def setUp(self):
        # Faking IOThread
        registerAsIOThread()

        # Object creation and preperation
        self.dispersy = Dispersy(ManualEnpoint(0),
                                 unicode("dispersy_temporary_mc1"))
        self.dispersy_bank = Dispersy(ManualEnpoint(0),
                                      unicode("dispersy_temporary_mc2"))

        self.api = MarketAPI(
            MarketDatabase(PersistentBackend('.', u'borrower.db')))
        self.api_bank = MarketAPI(
            MarketDatabase(PersistentBackend('.', u'bank.db')))

        self.api.db.backend.clear()
        self.api_bank.db.backend.clear()

        self.user, _, priv_user = self.api.create_user()
        self.bank, _, priv_bank = self.api_bank.create_user()

        self.dispersy._database.open()
        self.dispersy_bank._database.open()

        self.master_member = DummyMember(self.dispersy, 1, "a" * 20)

        self.member = self.dispersy.get_member(
            private_key=priv_user.decode("HEX"))
        self.member_bank = self.dispersy_bank.get_member(
            private_key=priv_bank.decode("HEX"))

        self.community = MortgageMarketCommunity.init_community(
            self.dispersy, self.master_member, self.member)
        self.community_bank = MortgageMarketCommunity.init_community(
            self.dispersy_bank, self.master_member, self.member_bank)

        self.community.api = self.api
        self.community.user = self.user
        self.api.community = self.community

        self.community_bank.api = self.api_bank
        self.community_bank.user = self.bank
        self.api_bank.community = self.community_bank

        self.db = self.api.db.backend
        self.bank_db = self.api_bank.db.backend

        self.community.persistence = self.db
        self.community_bank.persistence = self.bank_db

        # Models
        self.mortgage = Mortgage(UUID('b97dfa1c-e125-4ded-9b1a-5066462c529c'),
                                 UUID('b97dfa1c-e125-4ded-9b1a-5066462c520c'),
                                 'ING', 80000, 1, 2.5, 1.5, 2.5, 36, 'A', [],
                                 STATUS.ACCEPTED)
        t = int(time.time())
        self.payload = (
            self.bank.id,
            self.user.id,
            self.mortgage,
            self.mortgage,
            2,
            1,
            'prev_hash_bene',
            'prev_hash_beni',
            'sig_bene',
            'sig_beni',
            t,
        )

        self.payload2 = (
            self.bank.id,
            '',
            self.mortgage,
            None,
            2,
            1,
            'prev_hash_bene',
            '',
            'sig_bene',
            '',
            t,
        )

    def test_from_signed_confirmed(self):
        """
        This test checks the functionality of making a block with the payload from a message.
        """
        meta = self.community.get_meta_message(u"signed_confirm")
        message = meta.impl(
            authentication=([self.member, self.member_bank], ),
            distribution=(self.community.claim_global_time(), ),
            payload=self.payload,
            destination=(LoopbackCandidate(), ))

        block = DatabaseBlock.from_signed_confirm_message(message)

        self.assertEqual(block.benefactor, message.payload.benefactor)
        self.assertEqual(block.beneficiary, message.payload.beneficiary)
        self.assertEqual(DatabaseModel.decode(block.agreement_benefactor),
                         message.payload.agreement_benefactor)
        self.assertEqual(DatabaseModel.decode(block.agreement_beneficiary),
                         message.payload.agreement_beneficiary)
        self.assertEqual(block.sequence_number_benefactor,
                         message.payload.sequence_number_benefactor)
        self.assertEqual(block.sequence_number_beneficiary,
                         message.payload.sequence_number_beneficiary)
        self.assertEqual(block.previous_hash_benefactor,
                         message.payload.previous_hash_benefactor)
        self.assertEqual(block.previous_hash_beneficiary,
                         message.payload.previous_hash_beneficiary)
        self.assertEqual(block.insert_time, message.payload.insert_time)

    def test_add_get(self):
        """
        This test checks the functionality of adding a block to the blockchain then retrieving it.
        """

        meta = self.community.get_meta_message(u"signed_confirm")
        message = meta.impl(
            authentication=([self.member, self.member_bank], ),
            distribution=(self.community.claim_global_time(), ),
            payload=self.payload,
            destination=(LoopbackCandidate(), ))

        block = DatabaseBlock.from_signed_confirm_message(message)

        # Add the block to the blockchain
        self.db.add_block(block)
        # Get the block by the hash of the block
        result = self.db.get_by_hash(block.hash_block)

        # Check whether the block was added correctly
        self.assertEqualBlocks(block, result)

    def test_add_multiple_blocks(self):
        """
        This test checks the functionality of adding two blocks to the blockchain.
        """

        meta = self.community.get_meta_message(u"signed_confirm")
        message1 = meta.impl(
            authentication=([self.member, self.member_bank], ),
            distribution=(self.community.claim_global_time(), ),
            payload=self.payload,
            destination=(LoopbackCandidate(), ))

        message2 = meta.impl(
            authentication=([self.member, self.member_bank], ),
            distribution=(self.community.claim_global_time(), ),
            payload=self.payload2,
            destination=(LoopbackCandidate(), ))

        block1 = DatabaseBlock.from_signed_confirm_message(message1)
        block2 = DatabaseBlock.from_signed_confirm_message(message2)

        # Add the blocks to the blockchain
        self.db.add_block(block1)
        self.bank_db.add_block(block1)

        self.db.add_block(block2)
        self.bank_db.add_block(block2)

        # Get the blocks by the hash of the block
        result1 = self.db.get_by_hash(block1.hash_block)
        result2 = self.db.get_by_hash(block2.hash_block)

        # Get the latest hash
        latest_hash_benefactor = self.db.get_latest_hash()
        latest_hash_beneficiary = self.bank_db.get_latest_hash()

        # Check whether the blocks were added correctly
        self.assertEqualBlocks(block1, result1)
        self.assertEqualBlocks(block2, result2)
        self.assertNotEqual(block1.hash_block, block2.hash_block)
        self.assertEqual(latest_hash_benefactor, latest_hash_beneficiary)
        self.assertEqual(latest_hash_benefactor, block2.hash_block)
        self.assertEqual(latest_hash_beneficiary, block2.hash_block)
        self.assertNotEqual(latest_hash_benefactor, block1.hash_block)
        self.assertNotEqual(latest_hash_beneficiary, block1.hash_block)

    def test_update_block_with_beneficiary(self):
        """
        This test checks the functionality of updating a block in the blockchain.
        """

        meta = self.community.get_meta_message(u"signed_confirm")
        message_no_ben = meta.impl(
            authentication=([self.member, self.member_bank], ),
            distribution=(self.community.claim_global_time(), ),
            payload=self.payload2,
            destination=(LoopbackCandidate(), ))

        message_ben = meta.impl(
            authentication=([self.member, self.member_bank], ),
            distribution=(self.community.claim_global_time(), ),
            payload=self.payload,
            destination=(LoopbackCandidate(), ))

        # Add the block with only the information from benefactor to the blockchain
        self.community.persist_signature(message_no_ben)
        # Create the block manually for assertions
        block_benefactor = DatabaseBlock.from_signed_confirm_message(
            message_no_ben)

        # Update the block with the information from the beneficiary
        self.community.update_signature(message_ben)
        # Create the block manually for assertions
        block_beneficiary = DatabaseBlock.from_signed_confirm_message(
            message_ben)

        # Get the updated block by the hash of the block
        result = self.db.get_by_hash(block_beneficiary.hash_block)

        self.assertEqualBlocks(block_beneficiary, result)
        # Get the updated block by the public key and the sequence number
        result_benefactor = self.db.get_by_public_key_and_sequence_number(
            message_ben.payload.benefactor,
            block_benefactor.sequence_number_benefactor)
        result_beneficiary = self.db.get_by_public_key_and_sequence_number(
            message_ben.payload.beneficiary,
            block_beneficiary.sequence_number_beneficiary)

        # Check whether the block was updated correctly
        self.assertEqualBlocks(result_benefactor, result_beneficiary)
        self.assertEqualBlocks(result_benefactor, result)
        self.assertEqualBlocks(result_beneficiary, result)

    def test_check_add_genesis_block(self):
        """
        This test checks the functionality of adding a block to an empty blockchain.
        """

        # Check if there are already blocks in the blockchain, if not add genesis block
        self.db.check_add_genesis_block()
        # Get the genesis block
        genesis_block = self.db.create_genesis_block()

        meta = self.community.get_meta_message(u"signed_confirm")
        message = meta.impl(
            authentication=([self.member, self.member_bank], ),
            distribution=(self.community.claim_global_time(), ),
            payload=self.payload,
            destination=(LoopbackCandidate(), ))

        block = DatabaseBlock.from_signed_confirm_message(message)

        # Add the block to the blockchain
        self.db.add_block(block)
        # Get the block by the hash of the block
        result = self.db.get_by_hash(block.hash_block)

        # Check whether the genesis block and the first block are added correctly
        self.assertEqual(result.previous_hash, genesis_block.hash_block)

    def tearDown(self):
        self.dispersy._database.close()
        self.dispersy_bank._database.close()
        self.api.db.backend.clear()
        self.api_bank.db.backend.clear()
        self.api.db.backend.close()
        self.api_bank.db.backend.close()
    def setUp(self):
        # Faking IOThread
        registerAsIOThread()

        # Object creation and preperation
        self.dispersy = Dispersy(ManualEnpoint(0),
                                 unicode("dispersy_temporary"))
        self.dispersy_bank = Dispersy(ManualEnpoint(0),
                                      unicode("dispersy_temporary2"))
        self.dispersy_investor = Dispersy(ManualEnpoint(0),
                                          unicode("dispersy_temporary3"))

        # a neutral api to generate the intial id's for loan requests and such to skip
        # having to save the loan request to the (sending) user from each test as that
        # isn't relevant.
        self.neutral_api = MarketAPI(MarketDatabase(MemoryBackend()))

        self.api = MarketAPI(MarketDatabase(MemoryBackend()))
        self.api_bank = MarketAPI(MarketDatabase(MemoryBackend()))
        self.api_investor = MarketAPI(MarketDatabase(MemoryBackend()))

        self.api.db.backend.clear()
        self.api_bank.db.backend.clear()
        self.api_investor.db.backend.clear()

        self.user, _, priv_user = self.api.create_user()
        self.bank, _, priv_bank = self.api.create_user()
        self.investor, _, priv_investor = self.api.create_user()

        # save the user to the bank and investor db
        self.user.post_or_put(self.api_bank.db)
        self.bank.post_or_put(self.api_bank.db)
        self.investor.post_or_put(self.api_bank.db)

        self.user.post_or_put(self.api_investor.db)
        self.bank.post_or_put(self.api_investor.db)
        self.investor.post_or_put(self.api_investor.db)

        self.dispersy._database.open()
        self.dispersy_bank._database.open()
        self.dispersy_investor._database.open()

        self.master_member = DummyMember(self.dispersy, 1, "a" * 20)

        self.member = self.dispersy.get_member(
            private_key=priv_user.decode("HEX"))
        self.member_bank = self.dispersy.get_member(
            private_key=priv_bank.decode("HEX"))
        self.member_investor = self.dispersy.get_member(
            private_key=priv_investor.decode("HEX"))

        self.community = MortgageMarketCommunity.init_community(
            self.dispersy, self.master_member, self.member)
        self.community_bank = MortgageMarketCommunity.init_community(
            self.dispersy_bank, self.master_member, self.member_bank)
        self.community_investor = MortgageMarketCommunity.init_community(
            self.dispersy_investor, self.master_member, self.member_investor)

        self.community.api = self.api
        self.community.user = self.user
        self.api.community = self.community

        self.community_bank.api = self.api_bank
        self.community_bank.user = self.bank
        self.api.community = self.community_bank

        self.community_investor.api = self.api_investor
        self.community_investor.user = self.investor
        self.api.community = self.community_investor

        # Add our conversion to the community.
        self.conversion = MortgageMarketConversion(self.community)

        self.dispersy_mock = Mock()
        self.dispersy_mock.store_update_forward.return_value = True

        self.setupModels()
class CommunityTestSuite(unittest.TestCase):
    """Conversion test cases."""
    def setUp(self):
        # Faking IOThread
        registerAsIOThread()

        # Object creation and preperation
        self.dispersy = Dispersy(ManualEnpoint(0),
                                 unicode("dispersy_temporary"))
        self.dispersy_bank = Dispersy(ManualEnpoint(0),
                                      unicode("dispersy_temporary2"))
        self.dispersy_investor = Dispersy(ManualEnpoint(0),
                                          unicode("dispersy_temporary3"))

        # a neutral api to generate the intial id's for loan requests and such to skip
        # having to save the loan request to the (sending) user from each test as that
        # isn't relevant.
        self.neutral_api = MarketAPI(MarketDatabase(MemoryBackend()))

        self.api = MarketAPI(MarketDatabase(MemoryBackend()))
        self.api_bank = MarketAPI(MarketDatabase(MemoryBackend()))
        self.api_investor = MarketAPI(MarketDatabase(MemoryBackend()))

        self.api.db.backend.clear()
        self.api_bank.db.backend.clear()
        self.api_investor.db.backend.clear()

        self.user, _, priv_user = self.api.create_user()
        self.bank, _, priv_bank = self.api.create_user()
        self.investor, _, priv_investor = self.api.create_user()

        # save the user to the bank and investor db
        self.user.post_or_put(self.api_bank.db)
        self.bank.post_or_put(self.api_bank.db)
        self.investor.post_or_put(self.api_bank.db)

        self.user.post_or_put(self.api_investor.db)
        self.bank.post_or_put(self.api_investor.db)
        self.investor.post_or_put(self.api_investor.db)

        self.dispersy._database.open()
        self.dispersy_bank._database.open()
        self.dispersy_investor._database.open()

        self.master_member = DummyMember(self.dispersy, 1, "a" * 20)

        self.member = self.dispersy.get_member(
            private_key=priv_user.decode("HEX"))
        self.member_bank = self.dispersy.get_member(
            private_key=priv_bank.decode("HEX"))
        self.member_investor = self.dispersy.get_member(
            private_key=priv_investor.decode("HEX"))

        self.community = MortgageMarketCommunity.init_community(
            self.dispersy, self.master_member, self.member)
        self.community_bank = MortgageMarketCommunity.init_community(
            self.dispersy_bank, self.master_member, self.member_bank)
        self.community_investor = MortgageMarketCommunity.init_community(
            self.dispersy_investor, self.master_member, self.member_investor)

        self.community.api = self.api
        self.community.user = self.user
        self.api.community = self.community

        self.community_bank.api = self.api_bank
        self.community_bank.user = self.bank
        self.api.community = self.community_bank

        self.community_investor.api = self.api_investor
        self.community_investor.user = self.investor
        self.api.community = self.community_investor

        # Add our conversion to the community.
        self.conversion = MortgageMarketConversion(self.community)

        self.dispersy_mock = Mock()
        self.dispersy_mock.store_update_forward.return_value = True

        self.setupModels()

    def setupModels(self):
        self.house = House('2500AA', '34', 'Aa Weg', 1000)
        self.house.post_or_put(self.neutral_api.db)

        self.loan_request = LoanRequest(user_key=self.user.id,
                                        house_id=self.house.id,
                                        house_link='http://www.example.com',
                                        seller_phone_number='06000000',
                                        seller_email='*****@*****.**',
                                        mortgage_type=1,
                                        banks=[self.bank.id],
                                        description=u'Unicode description',
                                        amount_wanted=10000,
                                        status={})
        self.loan_request.post_or_put(self.neutral_api.db)

        self.borrowers_profile = BorrowersProfile(
            first_name=u'Jebediah',
            last_name=u'Kerman',
            email='*****@*****.**',
            iban='sadasdas',
            phone_number='213131',
            current_postal_code='2312AA',
            current_house_number='2132',
            current_address='Damstraat 1',
            document_list=[])
        self.borrowers_profile.post_or_put(self.neutral_api.db)

        self.investors_profile = Profile(first_name=u'Jebediah',
                                         last_name=u'Kerman',
                                         email='*****@*****.**',
                                         iban='sadasdas',
                                         phone_number='213131')
        self.investors_profile.post_or_put(self.neutral_api.db)

        self.mortgage = Mortgage(request_id=self.loan_request.id,
                                 house_id=self.house.id,
                                 bank=self.bank.id,
                                 amount=10000,
                                 mortgage_type=1,
                                 interest_rate=1.0,
                                 max_invest_rate=2.0,
                                 default_rate=3.0,
                                 duration=60,
                                 risk='A',
                                 investors=[],
                                 status=STATUS.PENDING)
        self.mortgage.post_or_put(self.neutral_api.db)

        self.campaign = Campaign(mortgage_id=self.mortgage.id,
                                 amount=self.mortgage.amount,
                                 end_date=datetime.datetime.now(),
                                 completed=False)
        self.campaign.post_or_put(self.neutral_api.db)

        self.investment = Investment(investor_key=self.investor.id,
                                     amount=1000,
                                     duration=36,
                                     interest_rate=2.0,
                                     mortgage_id=self.mortgage.id,
                                     status=STATUS.PENDING)
        self.investment.post_or_put(self.neutral_api.db)

    def isModelInDB(self, api, model):
        return not api.db.get(model.type, model.id) is None

    def remove_from_db(self, model):
        self.api.db.backend.delete(model)

    def remove_payload_models_from_db(self, payload):
        for key in payload.models:
            self.remove_from_db(payload.models[key])

    def test_init(self):
        self.assertIsInstance(self.conversion, MortgageMarketConversion)
        self.assertIsInstance(self.community, MortgageMarketCommunity)
        self.assertIsInstance(self.user, User)
        self.assertIsInstance(self.member, Member)
        self.assertEqual(self.user.id, self.member.public_key.encode("HEX"))

    def test_master_member(self):
        master_member = MortgageMarketCommunity.get_master_members(
            self.dispersy)[0]
        self.assertEqual(Global.MASTER_KEY, master_member.public_key)

    def test_on_loan_request_receive(self):
        """
        Test a user sending a loan request to a bank

        user --> bank
        """
        payload = FakePayload()
        payload.request = APIMessage.LOAN_REQUEST
        payload.models = {
            self.house.type: self.house,
            self.loan_request.type: self.loan_request,
            self.user.type: self.user,
            self.borrowers_profile.type: self.borrowers_profile
        }

        # Bank doesn't have them yet
        self.assertFalse(self.isModelInDB(self.api_bank, self.loan_request))
        self.assertFalse(
            self.isModelInDB(self.api_bank, self.borrowers_profile))
        self.assertFalse(self.isModelInDB(self.api_bank, self.house))

        self.community_bank.on_loan_request_receive(payload)

        self.assertTrue(self.isModelInDB(self.api_bank, self.loan_request))
        self.assertTrue(self.isModelInDB(self.api_bank,
                                         self.borrowers_profile))
        self.assertTrue(self.isModelInDB(self.api_bank, self.house))

    def test_on_loan_request_reject(self):
        """
        Test a bank rejecting a users loan_request

        bank --> user
        """
        # Save the user-side initial data which is a pending loan request.
        self.loan_request.status[self.bank.id] = STATUS.PENDING
        self.user.loan_request_ids.append(self.loan_request.id)
        self.user.post_or_put(self.api.db)

        self.assertIn(self.loan_request.id, self.user.loan_request_ids)

        # Deep copy the loan request
        loan_request_bank = DatabaseModel.decode(self.loan_request.encode())
        loan_request_bank.status[self.bank.id] = STATUS.REJECTED

        # Make the payload
        payload = FakePayload()
        payload.request = APIMessage.LOAN_REQUEST_REJECT
        payload.models = {
            self.loan_request.type: loan_request_bank,
            self.user.type: self.bank
        }

        self.community.on_loan_request_reject(payload)

        # Now let's pull the loan request from the user database
        self.assertTrue(self.isModelInDB(self.api, loan_request_bank))
        loan_request = self.api.db.get(loan_request_bank.type,
                                       loan_request_bank.id)

        self.assertEqual(loan_request.status[self.bank.id], STATUS.REJECTED)
        self.assertNotIn(self.loan_request.id, self.user.loan_request_ids)

    def test_on_mortgage_offer(self):
        """
        Test a bank sending a mortgage offer to a user

        bank -> user
        """
        payload = FakePayload()
        payload.request = APIMessage.MORTGAGE_OFFER
        payload.models = {
            self.loan_request.type: self.loan_request,
            self.mortgage.type: self.mortgage
        }
        self.loan_request.status[self.bank.id] = STATUS.ACCEPTED
        self.mortgage.status = STATUS.ACCEPTED

        self.assertFalse(self.isModelInDB(self.api, self.loan_request))
        self.assertFalse(self.isModelInDB(self.api, self.mortgage))

        self.community.on_mortgage_offer(payload)

        # The user now has the models.
        self.assertTrue(self.isModelInDB(self.api, self.loan_request))
        self.assertTrue(self.isModelInDB(self.api, self.mortgage))

        # Check if the mortgage id is in the user
        self.user.update(self.api.db)
        self.assertIn(self.mortgage.id, self.user.mortgage_ids)
        self.assertEqual(
            self.api.db.get(self.mortgage.type, self.mortgage.id).status,
            STATUS.ACCEPTED)

    def test_on_mortgage_accept(self):
        """
        Test a user accepting a mortgage

        user -> bank
        user -> investor
        """
        payload = FakePayload()

        # Fake the signing time
        self.loan_request._time_signed = sys.maxint
        self.mortgage._time_signed = sys.maxint
        self.user._time_signed = sys.maxint
        self.campaign._time_signed = sys.maxint
        self.house._time_signed = sys.maxint

        payload.request = APIMessage.MORTGAGE_ACCEPT_UNSIGNED
        payload.models = {
            self.loan_request.type: self.loan_request,
            self.mortgage.type: self.mortgage,
            self.user.type: self.user,
            self.campaign.type: self.campaign,
            self.house.type: self.house
        }
        self.loan_request.status[self.bank.id] = STATUS.ACCEPTED
        self.mortgage.status = STATUS.ACCEPTED
        self.user.campaign_ids.append(self.campaign.id)
        self.user.mortgage_ids.append(self.mortgage.id)
        self.user.loan_request_ids.append(self.loan_request.id)

        self.community_bank.on_mortgage_accept_signed(payload)
        self.community_investor.on_mortgage_accept_unsigned(payload)

        # The bank now has the models.
        self.assertTrue(self.isModelInDB(self.api_bank, self.mortgage))
        self.assertTrue(self.isModelInDB(self.api_bank, self.campaign))
        # The loan request isn't sent to the bank
        self.assertFalse(self.isModelInDB(self.api_bank, self.loan_request))

        # The investor has the models.
        self.assertTrue(self.isModelInDB(self.api_investor, self.loan_request))
        self.assertTrue(self.isModelInDB(self.api_investor, self.mortgage))
        self.assertTrue(self.isModelInDB(self.api_investor, self.campaign))

        # And knowledge of the campaign.
        user_from_inv_db = self.api_investor.db.get(self.user.type,
                                                    self.user.id)
        self.assertIn(self.campaign.id, user_from_inv_db.campaign_ids)

        # Check of the campaign has been added to the bank
        self.bank.update(self.api_bank.db)
        self.assertIn(self.campaign.id, self.bank.campaign_ids)

    def test_on_mortgage_reject(self):
        """
        Test a user rejecting a mortgage
        user -> bank
        """

        #Pre-condition. Bank has the mortgage saved with status.PENDING
        self.mortgage.post_or_put(self.api_bank.db)
        self.bank.mortgage_ids.append(self.mortgage.id)
        self.bank.post_or_put(self.api_bank.db)

        self.mortgage._time_signed = sys.maxint
        self.user._time_signed = sys.maxint

        # Create the payload
        payload = FakePayload()
        payload.request = APIMessage.MORTGAGE_REJECT
        payload.models = {
            self.mortgage.type: self.mortgage,
            self.user.type: self.user,
        }

        self.mortgage.status = STATUS.REJECTED
        self.community_bank.on_mortgage_reject(payload)

        self.bank.update(self.api_bank.db)

        mortgage = self.api_bank.db.get(self.mortgage.type, self.mortgage.id)

        self.assertEqual(mortgage.status, STATUS.REJECTED)
        self.assertNotIn(mortgage.id, self.bank.mortgage_ids)

    def test_on_investment_offer(self):
        """
        Test an investor sending an investment offer to a borrower
        investor -> user
        """
        payload = FakePayload()
        payload.request = APIMessage.INVESTMENT_OFFER
        payload.models = {
            self.investor.type: self.investor,
            self.investment.type: self.investment,
            self.investors_profile.type: self.investors_profile
        }

        # Check if user doesn't have the investment yet
        self.assertFalse(self.isModelInDB(self.api, self.investment))

        # Send investment offer message
        self.community.on_investment_offer(payload)

        # Check if the user has the investment
        self.assertTrue(self.isModelInDB(self.api, self.investment))

    def test_on_campaign_bid_with_investment(self):
        """
        Test sending a campaign bid
        user -> user
        user -> bank
        user -> investor
        investor -> user
        investor -> bank
        investor -> investor
        """
        payload = FakePayload()
        payload.request = APIMessage.CAMPAIGN_BID
        payload.fields = [
            User.type, Investment.type, Campaign.type, LoanRequest.type,
            Mortgage.type, House.type
        ]
        payload.models = {
            self.user.type: self.user,
            self.investment.type: self.investment,
            self.campaign.type: self.campaign,
            self.loan_request.type: self.loan_request,
            self.mortgage.type: self.mortgage,
            self.house.type: self.house
        }

        # Check if user doesn't have the models yet
        self.assertFalse(self.isModelInDB(self.api, self.investment))
        self.assertFalse(self.isModelInDB(self.api_bank, self.investment))
        self.assertFalse(self.isModelInDB(self.api_investor, self.investment))
        self.assertFalse(self.isModelInDB(self.api, self.campaign))
        self.assertFalse(self.isModelInDB(self.api_bank, self.campaign))
        self.assertFalse(self.isModelInDB(self.api_investor, self.campaign))
        self.assertFalse(self.isModelInDB(self.api, self.loan_request))
        self.assertFalse(self.isModelInDB(self.api_bank, self.loan_request))
        self.assertFalse(self.isModelInDB(self.api_investor,
                                          self.loan_request))
        self.assertFalse(self.isModelInDB(self.api, self.mortgage))
        self.assertFalse(self.isModelInDB(self.api_bank, self.mortgage))
        self.assertFalse(self.isModelInDB(self.api_investor, self.mortgage))
        self.assertFalse(self.isModelInDB(self.api, self.house))
        self.assertFalse(self.isModelInDB(self.api_bank, self.house))
        self.assertFalse(self.isModelInDB(self.api_investor, self.house))

        # Send campaign bid
        self.community.on_campaign_bid(payload)
        self.community_bank.on_campaign_bid(payload)
        self.community_investor.on_campaign_bid(payload)

        # Check if the user has the models
        self.assertTrue(self.isModelInDB(self.api, self.investment))
        self.assertTrue(self.isModelInDB(self.api_bank, self.investment))
        self.assertTrue(self.isModelInDB(self.api_investor, self.investment))
        self.assertTrue(self.isModelInDB(self.api, self.campaign))
        self.assertTrue(self.isModelInDB(self.api_bank, self.campaign))
        self.assertTrue(self.isModelInDB(self.api_investor, self.campaign))
        self.assertTrue(self.isModelInDB(self.api, self.loan_request))
        self.assertTrue(self.isModelInDB(self.api_bank, self.loan_request))
        self.assertTrue(self.isModelInDB(self.api_investor, self.loan_request))
        self.assertTrue(self.isModelInDB(self.api, self.mortgage))
        self.assertTrue(self.isModelInDB(self.api_bank, self.mortgage))
        self.assertTrue(self.isModelInDB(self.api_investor, self.mortgage))
        self.assertTrue(self.isModelInDB(self.api, self.house))
        self.assertTrue(self.isModelInDB(self.api_bank, self.house))
        self.assertTrue(self.isModelInDB(self.api_investor, self.house))

    def test_on_campaign_bid_without_investment(self):
        """
        Test sending a campaign bid
        user -> user
        user -> bank
        user -> investor
        investor -> user
        investor -> bank
        investor -> investor
        """
        payload = FakePayload()
        payload.request = APIMessage.CAMPAIGN_BID
        payload.models = {
            self.user.type: self.user,
            self.investment.type: None,
            self.campaign.type: self.campaign,
            self.loan_request.type: self.loan_request,
            self.mortgage.type: self.mortgage,
            self.house.type: self.house
        }

        # Check if user doesn't have the models yet
        self.assertFalse(self.isModelInDB(self.api, self.investment))
        self.assertFalse(self.isModelInDB(self.api_bank, self.investment))
        self.assertFalse(self.isModelInDB(self.api_investor, self.investment))
        self.assertFalse(self.isModelInDB(self.api, self.campaign))
        self.assertFalse(self.isModelInDB(self.api_bank, self.campaign))
        self.assertFalse(self.isModelInDB(self.api_investor, self.campaign))
        self.assertFalse(self.isModelInDB(self.api, self.loan_request))
        self.assertFalse(self.isModelInDB(self.api_bank, self.loan_request))
        self.assertFalse(self.isModelInDB(self.api_investor,
                                          self.loan_request))
        self.assertFalse(self.isModelInDB(self.api, self.mortgage))
        self.assertFalse(self.isModelInDB(self.api_bank, self.mortgage))
        self.assertFalse(self.isModelInDB(self.api_investor, self.mortgage))
        self.assertFalse(self.isModelInDB(self.api, self.house))
        self.assertFalse(self.isModelInDB(self.api_bank, self.house))
        self.assertFalse(self.isModelInDB(self.api_investor, self.house))

        # Send campaign bid
        self.community.on_campaign_bid(payload)
        self.community_bank.on_campaign_bid(payload)
        self.community_investor.on_campaign_bid(payload)

        # Check if the user has the models
        # self.assertFalse(self.isModelInDB(self.api, None))
        # self.assertFalse(self.isModelInDB(self.api_bank, None))
        # self.assertFalse(self.isModelInDB(self.api_investor, None))
        self.assertTrue(self.isModelInDB(self.api, self.campaign))
        self.assertTrue(self.isModelInDB(self.api_bank, self.campaign))
        self.assertTrue(self.isModelInDB(self.api_investor, self.campaign))
        self.assertTrue(self.isModelInDB(self.api, self.loan_request))
        self.assertTrue(self.isModelInDB(self.api_bank, self.loan_request))
        self.assertTrue(self.isModelInDB(self.api_investor, self.loan_request))
        self.assertTrue(self.isModelInDB(self.api, self.mortgage))
        self.assertTrue(self.isModelInDB(self.api_bank, self.mortgage))
        self.assertTrue(self.isModelInDB(self.api_investor, self.mortgage))
        self.assertTrue(self.isModelInDB(self.api, self.house))
        self.assertTrue(self.isModelInDB(self.api_bank, self.house))
        self.assertTrue(self.isModelInDB(self.api_investor, self.house))

    def test_on_investment_accept(self):
        """
        Test a user rejecting an investment
        user -> investor
        """
        # Pre-condition. Investor has the investment saved with status.PENDING

        self.investment.post_or_put(self.api_investor.db)
        self.investor.investment_ids.append(self.investment.id)
        self.investor.post_or_put(self.api_investor.db)

        self.investment._time_signed = sys.maxint
        self.user._time_signed = sys.maxint
        self.borrowers_profile._time_signed = sys.maxint

        # Create the payload
        payload = FakePayload()
        payload.request = APIMessage.INVESTMENT_ACCEPT
        payload.models = {
            self.user.type: self.user,
            self.investment.type: self.investment,
            self.borrowers_profile.type: self.borrowers_profile
        }

        self.investment.status = STATUS.ACCEPTED

        self.community_investor.on_investment_accept(payload)

        self.investor.update(self.api_investor.db)

        investment = self.api_investor.db.get(self.investment.type,
                                              self.investment.id)

        self.assertEqual(investment.status, STATUS.ACCEPTED)
        self.assertIn(investment.id, self.investor.investment_ids)

    def test_on_investment_reject(self):
        """
        Test a user accepting an investment
        user -> investor
        """
        # Pre-condition. Investor has the investment saved with status.PENDING
        self.investment.post_or_put(self.api_investor.db)
        self.investor.investment_ids.append(self.investment.id)
        self.investor.post_or_put(self.api_investor.db)

        # Fake the signing time
        self.user._time_signed = sys.maxint
        self.investment._time_signed = sys.maxint

        # Create the payload
        payload = FakePayload()
        payload.request = APIMessage.INVESTMENT_REJECT
        payload.models = {
            self.user.type: self.user,
            self.investment.type: self.investment
        }

        self.investment.status = STATUS.REJECTED

        self.community_investor.on_investment_reject(payload)

        self.investor.update(self.api_investor.db)

        investment = self.api_investor.db.get(self.investment.type,
                                              self.investment.id)

        self.assertEqual(investment.status, STATUS.REJECTED)
        self.assertNotIn(investment.id, self.investor.investment_ids)

    @mock.patch('dispersy.dispersy.Dispersy.store_update_forward')
    def test_send_community_message(self, patch):
        self.assertFalse(patch.called)

        # Set them as false to override the defaults
        store = update = forward = False
        message_name = APIMessage.MORTGAGE_OFFER.value

        self.community.send_api_message_community(
            message_name, [self.loan_request.type],
            {self.loan_request.type: self.loan_request}, store, update,
            forward)

        self.assertTrue(patch.called)
        args, kwargs = patch.call_args

        self.assertEqual(type(args[0]), list)
        self.assertEqual(args[0][0].payload.request, message_name)

        self.assertEqual(args[1], store)
        self.assertEqual(args[2], update)
        self.assertEqual(args[3], forward)

    @mock.patch('dispersy.dispersy.Dispersy.store_update_forward')
    def test_send_candidate_message(self, patch):
        self.assertFalse(patch.called)

        # Set them as false to override the defaults
        store = update = forward = False
        message_name = APIMessage.MORTGAGE_OFFER.value
        candidates = (LoopbackCandidate(), )

        self.community.send_api_message_candidate(
            message_name, [self.loan_request.type],
            {self.loan_request.type: self.loan_request}, candidates, store,
            update, forward)

        self.assertTrue(patch.called)
        args, kwargs = patch.call_args
        self.assertEqual(type(args[0]), list)

        message = args[0][0]

        self.assertEqual(message.payload.request, message_name)
        self.assertEqual(args[1], store)
        self.assertEqual(args[2], update)
        self.assertEqual(args[3], forward)

    @mock.patch('dispersy.dispersy.Dispersy.store_update_forward')
    def test_send_introduce_user(self, patch):
        self.assertFalse(patch.called)

        # Set them as false to override the defaults
        store = update = forward = False
        message_name = u"introduce_user"
        candidate = LoopbackCandidate()

        self.community.send_introduce_user([self.user.type],
                                           {self.user.type: self.user},
                                           candidate, store, update, forward)

        self.assertTrue(patch.called)
        args, kwargs = patch.call_args
        self.assertEqual(type(args[0]), list)

        message = args[0][0]

        self.assertEqual(message.name, message_name)
        self.assertEqual(args[1], store)
        self.assertEqual(args[2], update)
        self.assertEqual(args[3], forward)

    @mock.patch('market.database.database.MarketDatabase.post')
    @mock.patch('dispersy.dispersy.Dispersy.store_update_forward')
    def test_on_user_introduction(self, store_patch, api_patch):
        # We'll be introducer the user to the bank. So remove user from the bank
        self.api_bank.db.delete(self.user)

        self.assertFalse(store_patch.called)

        # Set them as false to override the defaults
        store = update = forward = False
        message_name = u"introduce_user"
        candidate = LoopbackCandidate()

        self.community.send_introduce_user([self.user.type],
                                           {self.user.type: self.user},
                                           candidate)

        self.assertTrue(store_patch.called)
        args, _ = store_patch.call_args
        self.assertEqual(type(args[0]), list)
        message = args[0][0]
        self.assertEqual(message.name, message_name)

        # Receive the user as the bank and check if it is found in the database.
        self.assertIsNone(self.api_bank._get_user(self.user))

        # Send it to the bank
        self.assertFalse(api_patch.called)
        self.community_bank.on_user_introduction([message])
        self.assertTrue(api_patch.called)
        # Check if received
        args, _ = api_patch.call_args
        self.assertEqual(self.user.id, args[1].id)

    @mock.patch(
        'market.community.community.MortgageMarketCommunity.create_signature_request'
    )
    @mock.patch(
        'market.community.community.MortgageMarketCommunity._get_latest_hash')
    @mock.patch(
        'market.community.community.MortgageMarketCommunity._get_next_sequence_number'
    )
    @mock.patch(
        'market.community.community.MortgageMarketCommunity.update_signature')
    @mock.patch(
        'market.community.community.MortgageMarketCommunity.persist_signature')
    def test_signature_request_flow(self, persist, update, next_seq, next_hash,
                                    create_sig):
        persist.return_value = True
        update.return_value = True
        next_seq.return_value = 1
        next_hash.return_value = 'hasdhashdsa'
        create_sig.return_value = True

        # Attempt to sign without having a user candidate
        self.assertFalse(
            self.community_bank.publish_signed_confirm_request_message(
                self.user.id, self.mortgage))

        # Set the candidate for the user
        candidate = LoopbackCandidate()
        candidate.associate(self.member)
        self.api_bank.user_candidate[self.user.id] = candidate

        # Attempt to sign without having a user candidate
        self.assertTrue(
            self.community_bank.publish_signed_confirm_request_message(
                self.user.id, self.mortgage))
        self.assertTrue(create_sig.called)

    @mock.patch(
        'market.community.community.MortgageMarketCommunity.create_signature_request'
    )
    @mock.patch(
        'market.community.community.MortgageMarketCommunity._get_latest_hash')
    @mock.patch(
        'market.community.community.MortgageMarketCommunity._get_next_sequence_number'
    )
    @mock.patch(
        'market.community.community.MortgageMarketCommunity.update_signature')
    @mock.patch(
        'market.community.community.MortgageMarketCommunity.persist_signature')
    def test_create_signed_confirm_request_message(self, persist, update,
                                                   next_seq, next_hash,
                                                   create_sig):
        persist.return_value = True
        update.return_value = True
        next_seq.return_value = 1
        next_hash.return_value = 'hasdhashdsa'
        create_sig.return_value = True

        # Save the agreement for the user
        self.mortgage.post_or_put(self.api.db)
        self.loan_request.post_or_put(self.api_bank.db)

        # Set the candidate for the user
        candidate = LoopbackCandidate()
        candidate.associate(self.member)
        self.api_bank.user_candidate[self.user.id] = candidate

        # Attempt to sign without having a user candidate
        message = self.community_bank.create_signed_confirm_request_message(
            candidate, self.mortgage)

        self.assertEqual(message.name, u"signed_confirm")
        self.assertEqual(message.payload.agreement_benefactor, self.mortgage)
        self.assertEqual(message.payload.benefactor, self.bank.id)
        self.assertTrue(next_hash.called)
        self.assertTrue(next_seq.called)
        self.assertTrue(persist.called)
        self.assertFalse(update.called)

        persist.reset_mock()
        next_hash.reset_mock()
        next_seq.reset_mock()

        message2 = self.community.allow_signed_confirm_request(message)

        self.assertTrue(next_hash.called)
        self.assertTrue(next_seq.called)
        self.assertTrue(persist.called)
        self.assertFalse(update.called)

        self.assertEqual(message.name, message2.name)
        self.assertEqual(message.payload.benefactor,
                         message2.payload.benefactor)
        self.assertNotEqual(message.payload.beneficiary,
                            message2.payload.beneficiary)

        # Finally check if the update call works
        persist.reset_mock()
        next_hash.reset_mock()
        next_seq.reset_mock()

        self.assertTrue(
            self.community_bank.allow_signed_confirm_response(
                message, message2, True))
        self.assertFalse(next_hash.called)
        self.assertFalse(next_seq.called)
        self.assertFalse(persist.called)
        self.assertFalse(update.called)

        self.community_bank.received_signed_confirm_response([message2])

        self.assertTrue(update.called)

    def tearDown(self):
        # Closing and unlocking dispersy database for other tests in test suite
        self.dispersy._database.close()
        self.dispersy_bank._database.close()
        self.dispersy_investor._database.close()
class ConversionTestCase(unittest.TestCase):
    def setUp(self):
        # Faking IOThread
        registerAsIOThread()

        # Object creation and preperation
        self.dispersy = Dispersy(ManualEnpoint(0),
                                 unicode("dispersy_temporary"))
        self.api = MarketAPI(MarketDatabase(MemoryBackend()))
        self.api.db.backend.clear()

        user, _, priv = self.api.create_user()
        self.bank, _, _ = self.api.create_user()
        self.user = user
        self.private_key = priv

        self.dispersy._database.open()
        self.master_member = DummyMember(self.dispersy, 1, "a" * 20)
        self.member = self.dispersy.get_member(
            private_key=self.private_key.decode("HEX"))
        self.community = MortgageMarketCommunity.init_community(
            self.dispersy, self.master_member, self.member)

        self.community.api = self.api
        self.community.user = self.user
        self.api.community = self.community

        # Add our conversion to the community.
        self.conversion = MortgageMarketConversion(self.community)
        self.community._conversions = []
        self.community.add_conversion(self.conversion)

        self.setupModels()

    def setupModels(self):
        self.house = House('2500AA', '34', 'Aa Weg', 1000)
        self.house.post_or_put(self.api.db)

        self.loan_request = LoanRequest(user_key=self.user.id,
                                        house_id=self.house.id,
                                        house_link='http://www.example.com',
                                        seller_phone_number='06000000',
                                        seller_email='*****@*****.**',
                                        mortgage_type=1,
                                        banks=[self.bank.id],
                                        description=u'Unicode description',
                                        amount_wanted=10000,
                                        status={})
        self.loan_request.post_or_put(self.api.db)
        self.profile = BorrowersProfile(first_name=u'Jebediah',
                                        last_name=u'Kerman',
                                        email='*****@*****.**',
                                        iban='sadasdas',
                                        phone_number='213131',
                                        current_postal_code='2312AA',
                                        current_house_number='2132',
                                        current_address='Damstraat 1',
                                        document_list=[])
        self.profile.post_or_put(self.api.db)

    def test_encode_introduce_user(self):
        meta = self.community.get_meta_message(u"introduce_user")
        message = meta.impl(
            authentication=(self.member, ),
            distribution=(self.community.claim_global_time(), ),
            payload=([self.user.type], {
                self.user.type: self.user
            }),
            destination=(LoopbackCandidate(), ))

        encoded_message = self.conversion._encode_model(message)[0]
        decoded_payload = self.conversion._decode_model(
            message, 0, encoded_message)[1]

        self.assertEqual(message.payload.fields, decoded_payload.fields)
        self.assertEqual(message.payload.models, decoded_payload.models)

    def test_encode_api_request_community(self):
        meta = self.community.get_meta_message(u"api_message_community")
        message = meta.impl(
            authentication=(self.member, ),
            distribution=(self.community.claim_global_time(), ),
            payload=(
                APIMessage.MORTGAGE_OFFER.value,
                [self.user.type],
                {
                    self.user.type: self.user
                },
            ),
            destination=(LoopbackCandidate(), ))

        encoded_message = self.conversion._encode_api_message(message)[0]
        decoded_payload = self.conversion._decode_api_message(
            message, 0, encoded_message)[1]

        self.assertEqual(message.payload.models, decoded_payload.models)

    def test_encode_api_request_candidate(self):
        meta = self.community.get_meta_message(u"api_message_candidate")
        message = meta.impl(
            authentication=(self.member, ),
            distribution=(self.community.claim_global_time(), ),
            payload=(
                APIMessage.MORTGAGE_OFFER.value,
                [self.user.type],
                {
                    self.user.type: self.user
                },
            ),
            destination=(LoopbackCandidate(), ))

        encoded_message = self.conversion._encode_api_message(message)[0]
        decoded_payload = self.conversion._decode_api_message(
            message, 0, encoded_message)[1]

        self.assertEqual(message.payload.models, decoded_payload.models)

    def test_encode_signed_confirm(self):
        payload_list = []
        for k in range(1, 12):
            payload_list.append(None)

        payload_list[0] = self.user.id  # benefactor, 0
        payload_list[1] = self.bank.id  # beneficiary, 1
        payload_list[2] = self.loan_request
        payload_list[3] = None  # agreement beneficiary
        payload_list[4] = 0
        payload_list[5] = 0  # sequence number beneficiary
        payload_list[6] = 'hashsas'
        payload_list[7] = 'asdasdas'  # previous hash beneficiary
        payload_list[8] = 'sig1'  # Signature benefactor
        payload_list[9] = 'sig2'  # Signature beneficiary
        payload_list[10] = 324325252

        meta = self.community.get_meta_message(u"signed_confirm")
        loop = LoopbackCandidate()
        message = meta.impl(
            authentication=([self.member, self.member], ),
            distribution=(self.community.claim_global_time(), ),
            payload=tuple(payload_list))

        encoded_message = self.conversion._encode_signed_confirm(message)[0]
        decoded_payload = self.conversion._decode_signed_confirm(
            message, 0, encoded_message)[1]

        p1 = message.payload
        p2 = decoded_payload

        assert isinstance(p1, SignedConfirmPayload.Implementation)
        assert isinstance(p2, SignedConfirmPayload.Implementation)

        self.assertEqual(p1.agreement_benefactor, p2.agreement_benefactor)
        self.assertEqual(p1.agreement_beneficiary, p2.agreement_beneficiary)
        self.assertEqual(p1.benefactor, p2.benefactor)
        self.assertEqual(p1.beneficiary, p2.beneficiary)
        self.assertEqual(p1.previous_hash_benefactor,
                         p2.previous_hash_benefactor)
        self.assertEqual(p1.previous_hash_beneficiary,
                         p2.previous_hash_beneficiary)
        self.assertEqual(p1.sequence_number_benefactor,
                         p2.sequence_number_benefactor)
        self.assertEqual(p1.sequence_number_beneficiary,
                         p2.sequence_number_beneficiary)
        self.assertEqual(p1.signature_beneficiary, p2.signature_beneficiary)
        self.assertEqual(p1.signature_benefactor, p1.signature_benefactor)
        self.assertEqual(p1.insert_time, p2.insert_time)