def initiate_meta_messages(self):
     """
     Overwrite
     """
     self._small_file_distribution = FullSyncDistribution(DISTRIBUTION_DIRECTION, DISTRIBUTION_PRIORITY, True)
     self._file_hash_distribution = FullSyncDistribution(DISTRIBUTION_DIRECTION, DISTRIBUTION_PRIORITY, True)
     self._addresses_distribution = DirectDistribution()
     self._addresses_request_distribution = DirectDistribution()
     self._puncture_distribution = DirectDistribution()
     self._puncture_response_distribution = DirectDistribution()
     self._api_message_distribution = DirectDistribution()
     return [Message(self, SMALL_FILE_MESSAGE_NAME, MemberAuthentication(encoding="sha1"), PublicResolution(), 
                     self._small_file_distribution, CommunityDestination(NUMBER_OF_PEERS_TO_SYNC), SmallFilePayload(), 
                     self.small_file_message_check, self.small_file_message_handle),
             Message(self, FILE_HASH_MESSAGE_NAME, MemberAuthentication(encoding="sha1"), PublicResolution(), 
                     self._file_hash_distribution, CommunityDestination(NUMBER_OF_PEERS_TO_SYNC), FileHashPayload(), 
                      self.file_hash_check, self.file_hash_handle),
             Message(self, ADDRESSES_MESSAGE_NAME, MemberAuthentication(encoding="sha1"), PublicResolution(), 
                     self._addresses_distribution, CandidateDestination(), AddressesPayload(), 
                     self.addresses_message_check, self.addresses_message_handle),
             Message(self, ADDRESSES_REQUEST_MESSAGE_NAME, MemberAuthentication(encoding="sha1"), PublicResolution(), 
                     self._addresses_request_distribution, CandidateDestination(), AddressesRequestPayload(), 
                     self.addresses_request_message_check, self.addresses_request_message_handle),
             Message(self, PUNCTURE_MESSAGE_NAME, MemberAuthentication(encoding="sha1"), PublicResolution(), 
                     self._puncture_distribution, CandidateDestination(), PuncturePayload(), 
                     self.puncture_check, self.puncture_handle),
             Message(self, PUNCTURE_RESPONSE_MESSAGE_NAME, MemberAuthentication(encoding="sha1"), PublicResolution(), 
                     self._puncture_response_distribution, CandidateDestination(), PunctureResponsePayload(), 
                     self.puncture_response_check, self.puncture_response_handle),
             Message(self, API_MESSAGE_NAME, MemberAuthentication(encoding="sha1"), PublicResolution(), 
                     self._api_message_distribution, CandidateDestination(), APIMessagePayload(), 
                     self.api_message_check, self.api_message_handle)]
 def initiate_meta_messages(self):
     """>EXTEND< the current meta messages with our custom Flood type.
     """
     messages = super(FloodCommunity, self).initiate_meta_messages()
     ourmessages = [
         Message(
             self,
             u"flood",
             # Unique identifier
             MemberAuthentication(encoding="sha1"),
             # Member identifier hash type
             PublicResolution(),
             # All members can add messages
             FullSyncDistribution(enable_sequence_number=False,
                                  synchronization_direction=u"ASC",
                                  priority=255),
             # Synchronize without sequence number, delivering messages with the lowest
             # (Lamport) global time first and the highest priority
             CommunityDestination(node_count=10),
             # Push to >AT MOST< 10 other nodes initially
             FloodPayload(),
             # The object to actually carry our payload
             self.check_flood,
             # Callback to validate a received message
             self.on_flood,
             # Callback to actually handle a validated
             # message
             batch=BatchConfiguration(0.0))
     ]  # Amount of time (seconds) to save up messages before handling them
     messages.extend(ourmessages)
     return messages
Example #3
0
 def initiate_meta_messages(self):
     return super(
         MortgageMarketCommunity, self).initiate_meta_messages() + [
             Message(self, u"introduce_user", MemberAuthentication(),
                     PublicResolution(), DirectDistribution(),
                     CandidateDestination(), DatabaseModelPayload(),
                     self.check_message, self.on_user_introduction),
             Message(
                 self, u"api_message_community", MemberAuthentication(),
                 PublicResolution(),
                 FullSyncDistribution(synchronization_direction=u"DESC",
                                      priority=200,
                                      enable_sequence_number=False),
                 CommunityDestination(node_count=50), APIMessagePayload(),
                 self.check_message, self.on_api_message),
             Message(self, u"api_message_candidate", MemberAuthentication(),
                     PublicResolution(), DirectDistribution(),
                     CandidateDestination(), APIMessagePayload(),
                     self.check_message, self.on_api_message),
             Message(
                 self, u"signed_confirm",
                 DoubleMemberAuthentication(allow_signature_func=self.
                                            allow_signed_confirm_request),
                 PublicResolution(), DirectDistribution(),
                 CandidateDestination(), SignedConfirmPayload(),
                 self._generic_timeline_check,
                 self.received_signed_confirm_response),
         ]
 def initiate_meta_messages(self):
     return [
         Message(
             self, u"text", MemberAuthentication(encoding="bin"),
             PublicResolution(),
             FullSyncDistribution(enable_sequence_number=False,
                                  synchronization_direction=u"RANDOM",
                                  priority=128),
             CommunityDestination(node_count=0), TextPayload(),
             self.check_text, self.on_text)
     ]
Example #5
0
 def initiate_meta_messages(self):
     return super(MarketCommunity, self).initiate_meta_messages() + [
         Message(
             self, u"create-ask", MemberAuthentication(encoding="sha1"),
             PublicResolution(),
             FullSyncDistribution(enable_sequence_number=False,
                                  synchronization_direction=u"ASC",
                                  priority=128),
             CommunityDestination(node_count=0), AskPayload(),
             self.check_message, self.on_ask)
     ]
Example #6
0
    def initiate_meta_messages(self):
        '''
        Create the packaging for your message payloads,
        in this case we have one message type that is distributed to all peers
        '''

        return super(ExampleCommunity, self).initiate_meta_messages() + [
            Message(self,
                    u"text",
                    MemberAuthentication(encoding="sha1"),
                    PublicResolution(),
                    FullSyncDistribution(enable_sequence_number=False,
                                         synchronization_direction=u"ASC",
                                         priority=128),
                    CommunityDestination(node_count=10),
                    TextPayload(),
                    self.check_text,
                    self.on_text,
                    batch=BatchConfiguration(max_window=5.0))
        ]
class MyCommunity(Community):
    '''
    For the most part this Community is designed around the messages that are allowed to be used by its members.
    The SmallFile message is a container for a filename and the file's data. It has to be small enough to fit in a single packet.
    The FileHash message is designed to convey the information necessary for Swift to create a swarm for the file 
    you want to disseminate.
    The Addresses message allows you to send information about your endpoints (the sockets your have available)
    The AddressesRequest message is merely a request for an Addresses message.
    The Puncture message is used to ensure that contact between two endpoints is feasible. 
    Additionally it conveys the address it thinks it's talking for NAT purposes.
    The PunctureResponse message is not currently in use and is very much like the Puncture message.
    The API message is simply a message limited by packet size.
    
    Moreover this Community also instantiates the SwiftCommunity which is an abstraction of this community 
    with respect to Swift.
    On top of this the Community has the option of not using the bootstrappers and the walker,
    but instead use a more aggressive form of keeping in contact with peers by periodically sending 
    introduction requests to each of them.  
    '''

    def __init__(self, dispersy, master_member, enable=False, api_callback=None):
        '''
        @param enable: Enable the candidate walker
        @param api_callback: Callback function
        '''
        logger.info("I will %swalk today", "" if enable else "not ")
        self._enable_candidate_walker = enable # Needs to be set before call to Community
        super(MyCommunity, self).__init__(dispersy, master_member)
        self._dest_dir = None
        self._update_bloomfilter = -1
        self._intro_request_updates = {}
        self._api_callback = api_callback
        self._looper = Looper(sleep=0.1, name="MyCommunity_looper")
        self._looper.start()
        self._lock = Lock()
        self.swift_community = SwiftCommunity(self, self.dispersy.endpoint, api_callback=api_callback)
        
    def initiate_conversions(self):
        """
        Overwrite
        """
        return [DefaultConversion(self), SmallFileConversion(self), FileHashConversion(self), 
                AddressesConversion(self), AddressesRequestConversion(self), PunctureConversion(self), 
                PunctureResponseConversion(self), APIMessageConversion(self)]
    
    def initiate_meta_messages(self):
        """
        Overwrite
        """
        self._small_file_distribution = FullSyncDistribution(DISTRIBUTION_DIRECTION, DISTRIBUTION_PRIORITY, True)
        self._file_hash_distribution = FullSyncDistribution(DISTRIBUTION_DIRECTION, DISTRIBUTION_PRIORITY, True)
        self._addresses_distribution = DirectDistribution()
        self._addresses_request_distribution = DirectDistribution()
        self._puncture_distribution = DirectDistribution()
        self._puncture_response_distribution = DirectDistribution()
        self._api_message_distribution = DirectDistribution()
        return [Message(self, SMALL_FILE_MESSAGE_NAME, MemberAuthentication(encoding="sha1"), PublicResolution(), 
                        self._small_file_distribution, CommunityDestination(NUMBER_OF_PEERS_TO_SYNC), SmallFilePayload(), 
                        self.small_file_message_check, self.small_file_message_handle),
                Message(self, FILE_HASH_MESSAGE_NAME, MemberAuthentication(encoding="sha1"), PublicResolution(), 
                        self._file_hash_distribution, CommunityDestination(NUMBER_OF_PEERS_TO_SYNC), FileHashPayload(), 
                         self.file_hash_check, self.file_hash_handle),
                Message(self, ADDRESSES_MESSAGE_NAME, MemberAuthentication(encoding="sha1"), PublicResolution(), 
                        self._addresses_distribution, CandidateDestination(), AddressesPayload(), 
                        self.addresses_message_check, self.addresses_message_handle),
                Message(self, ADDRESSES_REQUEST_MESSAGE_NAME, MemberAuthentication(encoding="sha1"), PublicResolution(), 
                        self._addresses_request_distribution, CandidateDestination(), AddressesRequestPayload(), 
                        self.addresses_request_message_check, self.addresses_request_message_handle),
                Message(self, PUNCTURE_MESSAGE_NAME, MemberAuthentication(encoding="sha1"), PublicResolution(), 
                        self._puncture_distribution, CandidateDestination(), PuncturePayload(), 
                        self.puncture_check, self.puncture_handle),
                Message(self, PUNCTURE_RESPONSE_MESSAGE_NAME, MemberAuthentication(encoding="sha1"), PublicResolution(), 
                        self._puncture_response_distribution, CandidateDestination(), PunctureResponsePayload(), 
                        self.puncture_response_check, self.puncture_response_handle),
                Message(self, API_MESSAGE_NAME, MemberAuthentication(encoding="sha1"), PublicResolution(), 
                        self._api_message_distribution, CandidateDestination(), APIMessagePayload(), 
                        self.api_message_check, self.api_message_handle)]
        
    def small_file_message_check(self, messages):
        for x in messages:
            yield x
    
    def small_file_message_handle(self, messages):
        for x in messages:
            self.swift_community.file_received(x.payload.filename, x.payload.data)
            
    def file_hash_check(self, messages):
        for x in messages:
            yield x
    
    def file_hash_handle(self, messages):
        for x in messages:
            self.swift_community.filehash_received(x.payload.filename, x.payload.directories, x.payload.roothash, 
                                                   x.payload.size, x.payload.timestamp, x.destination)
    
    def addresses_message_check(self, messages):
        for x in messages:
            yield x
    
    def addresses_message_handle(self, messages):
        for x in messages:
            self.dispersy.endpoint.peer_endpoints_received(self, x.authentication.member, x.payload.addresses, 
                                                           x.payload.wan_addresses, x.payload.ids)
            
    def addresses_request_message_check(self, messages):
        for x in messages:
            yield x
            
    def addresses_request_message_handle(self, messages):
        for x in messages:
            self.dispersy.endpoint.addresses_requested(self, x.authentication.member, x.payload.sender_lan, 
                                                       x.payload.sender_wan, x.payload.endpoint_id, x.payload.wan_address)
            
    def puncture_check(self, messages):
        for x in messages:
            yield x
            
    def puncture_handle(self, messages):
        for x in messages:
            self.dispersy.endpoint.incoming_puncture_message(self, x.authentication.member, x.payload.sender_lan, 
                                                             x.payload.sender_wan, x.payload.sender_id, 
                                                             x.payload.address_vote, x.payload.endpoint_id)
            
    def puncture_response_check(self, messages):
        for x in messages:
            yield x
            
    def puncture_response_handle(self, messages):
        for x in messages:
            self.dispersy.endpoint.incoming_puncture_response_message(x.authentication.member, x.payload.sender_lan, 
                                                                      x.payload.sender_wan,
                                                                      x.payload.address_vote, x.payload.endpoint_id)
        
    def api_message_check(self, messages):
        for x in messages:
            yield x
    
    def api_message_handle(self, messages):
        for x in messages:
            if self._api_callback:
                self._api_callback(MESSAGE_KEY_API_MESSAGE, x.payload.message)
    
    def _active_sockets(self):
        return [s.address for s in self.dispersy.endpoint.swift_endpoints if s.socket_running]
     
    def create_small_file_messages(self, count, simple_message=None, store=True, update=True, forward=True):
        """
        @param count: Number of messages
        @type simple_message: SimpleFileCarrier
        """
        meta = self.get_meta_message(SMALL_FILE_MESSAGE_NAME)
        messages = [meta.impl(authentication=(self.my_member,), 
                              distribution=(self.claim_global_time(), self._small_file_distribution.claim_sequence_number()), 
                              payload=(simple_message.filename, simple_message.data)) for _ in xrange(count)]
        self.dispersy.store_update_forward(messages, store, update, forward)
        
    def create_file_hash_messages(self, count, file_hash_message, delay, store=True, update=True, forward=True):
        """
        Endpoint decides when files will be handed to Swift to be disseminated.
        As such we will not allow messages to be send until that time, 
        because peers would not be able to do anything with this information.
        
        @param count: Number of messages
        @type file_hash_message: FileHashCarrier
        """
        # Make sure you have the filename, and a proper hash
        if isfile(file_hash_message.filename) and file_hash_message.roothash is not None and len(file_hash_message.roothash) == HASH_LENGTH:
            meta = self.get_meta_message(FILE_HASH_MESSAGE_NAME)
            
            # Messages need to be created only when they are sent, otherwise peers get sequence errors
            def send_messages():
                messages = [meta.impl(authentication=(self.my_member,), 
                                  distribution=(self.claim_global_time(), self._file_hash_distribution.claim_sequence_number()), 
                                  payload=(file_hash_message.filename, file_hash_message.directories, 
                                           file_hash_message.roothash, file_hash_message.size, 
                                           file_hash_message.timestamp, self._active_sockets()))
                        for _ in xrange(count)]
                self.dispersy.callback.register(self.dispersy.store_update_forward, args=(messages, store, update, forward), delay=delay)
                
            # Let Swift know that it should seed this file
            # Nasty hack to get the destination implementation
            self.swift_community.add_file(file_hash_message.filename, file_hash_message.roothash, 
                                          meta.destination.Implementation(meta),
                                          file_hash_message.size, file_hash_message.timestamp, send_messages)
                
    def create_addresses_messages(self, count, addresses_message, candidates, store=True, update=True, forward=True):
        """
        @param count: Number of messages
        @type addresses_message: AddressesCarrier
        @param candidates: list of candidates
        """
        meta = self.get_meta_message(ADDRESSES_MESSAGE_NAME)
        messages = [meta.impl(authentication=(self.my_member,),
                              distribution=(self.claim_global_time(),),
                              destination=tuple(candidates), 
                              payload=(addresses_message.addresses,)) for _ in xrange(count)]
        self.dispersy.store_update_forward(messages, store, update, forward)
        
    def create_api_messages(self, count, api_message, store=True, update=True, forward=True):
        """
        @param count: Number of messages
        @type api_message: APIMessageCarrier
        """
        meta = self.get_meta_message(API_MESSAGE_NAME)
        candidates = [WalkCandidate(a.addr(), True, a.addr(), a.addr(), u"unknown") for a in api_message.addresses]
        if len(candidates) == 0:
            candidates = self._candidates.values()
        messages = [meta.impl(authentication=(self.my_member,),
                              distribution=(self.claim_global_time(),),
                              destination=tuple(candidates), 
                              payload=(api_message.message,)) for _ in xrange(count)]
        self.dispersy.store_update_forward(messages, store, update, forward)
                
    def create_candidate(self, sock_addr, tunnel, lan_address, wan_address, connection_type):
        """
        Creates and returns a new EligibleWalkCandidate instance,
        opposed to the WalkCandidate the general Community creates.
        """
        assert not sock_addr in self._candidates
        assert isinstance(tunnel, bool)
        candidate = EligibleWalkCandidate(sock_addr, tunnel, lan_address, wan_address, connection_type)
        self.add_candidate(candidate)
        return candidate
    
    def _add_candidate_intro_requests_update(self, candidate, send_request):
        """
        The candidate will be added to the list of known candidates, that will be sent introduction requests.
        In case self._update_bloomfilter is a positive number, every so many seconds an introduction request will be send.
        If it does not, the single request that will be send will be accompanied by a timeout, 
        which will force resend upon failure.
        
        @param candidate: Add candidate to list
        @param send_request: Callback function assigned to IntroductionRequestTimeout
        @return: True if candidate already existed, False otherwise
        """
        # find existing candidates that are likely to be the same candidate
        others = [other.candidate for other in self._intro_request_updates.itervalues()
                  if (other.candidate.wan_address[0] == candidate.wan_address[0] and
                      other.candidate.lan_address == candidate.lan_address)]
        
        l = len(others) # need l for return value
        if l == 0:
            logger.debug("Add new %s", candidate)
        elif l == 1:
            logger.debug("Merge existing %s with %s", others[0], candidate)
            candidate.merge(others[0])
        else: # len(others) > 1
            logger.debug("Merge only first of list, %s with %s", others[0], candidate)
            candidate.merge(others[0])
        
        # Remove all similar candidates
        for o in others:
            o.stop()
            del self._intro_request_updates[o.sock_addr]
        
        if self._update_bloomfilter > 0: # Use our own looper to ensure that requests are sent periodically
            request_update = PeriodicIntroductionRequest(send_request, self._update_bloomfilter, candidate, 
                                                         delay=self._update_bloomfilter) # Wait because one is already sent
            self._looper.add_task(request_update)
        
        else: # Only add a timeout regardless if walker is enabled or not
            request_update = IntroductionRequestTimeout(candidate, send_request)
        
        # request_update should have a candidate field
        self._intro_request_updates.update({candidate.sock_addr : request_update})
        
        return l > 0 # if l is larger than 0 it means this candidate already existed
                
    def add_candidate(self, candidate):
        """
        Add the candidate to the list of peers that should receive Introduction requests regularly
        """
        Community.add_candidate(self, candidate)
        # Each candidate should only do send_introduction_request once
        if not candidate.sock_addr in self._intro_request_updates.iterkeys():
            self.send_introduction_request(candidate) 
        
    def send_introduction_request(self, walker):
        """
        Register the sending of an introduction request to this walker
        With resends if the request fails
        @type walker: Candidate
        """
        if isinstance(walker, EligibleWalkCandidate):
            logger.debug("Send introduction request %s", walker)
            walker.set_update_bloomfilter(self.update_bloomfilter)
        
            def send_request():
                self._dispersy.callback.register(self._dispersy.create_introduction_request, 
                                    (self, walker, True, True), callback=callback)
            
            def callback(result):
                if isinstance(result, Exception):
                    # Somehow the introduction request did not work
                    Event().wait(1) # If we don't wait, we risk trying to many times to no avail
                    send_request()
    
            # First add the IntroductionRequestTimeout to the list, then send request. 
            # Otherwise this method is recursively called to often
            if not self._add_candidate_intro_requests_update(walker, send_request):                    
                send_request()
        else:
            if self.dispersy.endpoint.is_bootstrap_candidate(candidate=walker):
                logger.debug("This is a BootstrapCandidate: %s", walker)
            else: # This should not happen
                logger.warning("This is not a EligibleWalkCandidate: %s", walker)
            
        
    @property
    def dispersy_enable_candidate_walker(self):
        return self._enable_candidate_walker
    
    @property
    def dispersy_enable_candidate_walker_responses(self):
        # initialization and nat meta messages will still be created
        return True
    
    @property
    def dispersy_sync_skip_enable(self):
        # Skip the creation of the bloomfilter
        return Community.dispersy_sync_skip_enable
    
    @property
    def dispersy_sync_cache_enable(self):
        # Reuse the last bloomfilter
        return Community.dispersy_sync_cache_enable
    
    @property
    def dest_dir(self):
        return self._dest_dir
    
    @dest_dir.setter
    def dest_dir(self, dest_dir):
        self._dest_dir = dest_dir
    
    @property
    def update_bloomfilter(self):
        return self._update_bloomfilter
    
    @update_bloomfilter.setter
    def update_bloomfilter(self, update_bloomfilter):
        self._update_bloomfilter = update_bloomfilter    
    
    def unload_community(self):
        """
        Taking down this Community, and the SwiftCommunity as well.
        """
        self._looper.stop()
        Community.unload_community(self)
        return self.swift_community.unload_community()
    
    def claim_global_time(self):
        """
        Overwritten to ensure that claiming is threadsafe
        """
        with self._lock:
            return Community.claim_global_time(self)