Skip to content
Leberwurscht edited this page Feb 28, 2012 · 91 revisions
WebServer [IMPLEMENTED] (webserver.py)
    ...

ControlSample [IMPLEMENTED] [mapped with sqlalchemy.ext.declarative] (partners.py)
    partner_id
    timestamp
    penalty (=1 for offense, =0 for no offense, =NULL if partner was de-kicked manually)
    offense (only necessary if penalty!=0)
    webfinger_address (only necessary if penalty!=0, needed because only one offense per webfinger address must be counted to prevent profile owners from getting servers kicked)

Violation [IMPLEMENTED] [mapped with sqlalchemy.ext.declarative] (partners.py)
    partner_id
    timestamp
    description

Partner [IMPLEMENTED] [provide sqlalchemy mapper for this class] (partners.py)
    name # may not contain newline
    accept_password # may not contain newline

    address
    control_probability
    last_connection
    kicked

    connection_schedule (determines connection direction: if this field is "", partner is a client)
    provide_username # may not contain newline
    provide_password # may not contain newline
    
    __init__(partner_name, accept_password, base_url, control_probability, connection_schedule, provide_username, provide_password)
    get_synchronization_address()

    control_sample():
        if self==MySelf: return False
        else random

PartnerDatabase [IMPLEMENTED] (partners.py)
    cleanup()
        delete old saved control samples

    register_control_sample(partner_name, webfinger_address=str, offense=None/str)
    register_violation(partner_name, violation)
    register_connection(partner_name)

    get_partners()

    get_partner(partner_name):
        must return None only if partner_name==None, as this is the representation for our own server

    save_partner(partner)
    delete_partner(partner_name)

Profile [IMPLEMENTED] (states.py)
    full_name
    hometown
    country_code
    services
    submission_timestamp
    captcha_signature

Message(abstract): [IMPLEMENTED] (synchronization.py)
    transmit()
    @classmethod receive()

Ghost [IMPLEMENTED] [provide sqlalchemy mapper for this class] (states.py)
    retrieval_timestamp (=None means profile expired)
    hash
    
DeleteMessage(Message): [IMPLEMENTED] (synchronization.py)
    retrieval_timestamp (=None means that partner won't take responsibility) / TODO: rename?
    hash

    transmit(partnersocket)
    @classmethod receive(partnersocket)

State(Message) [IMPLEMENTED] [provide sqlalchemy mapper for this class, but require Profile/retrieval_timestamp != None] (states.py)
    webfinger_address
    retrieval_timestamp (=None means that partner won't take responsibility)
    Profile (mapped with "Composite Column Types")
    hash (read-only-property)

    @classmethod retrieve(address):
        ...
        run self.validate(), return None on failure

    ==(): # assumption: retrieval_timestamp!=None, same address
        Profile==None for both || same hash

    assert_validity(reference_timestamp=now): # assumption: retrieval_timestamp!=None
        (!) self.retrieval_timestamp<=reference_timestamp
        (!) self.retrieval_timestamp > reference_timestamp - RESPONSIBILITY_TIMESPAN[rename!]

        for Profile!=None:
            (!) state.retrieval_timestamp>state.submission_timestamp
            (!) state.submission_timestamp <= reference_timestamp
            (!) not expired
            (!) captcha signature

There are three roles that the State class can take over:

  • retrieval_timestamp==None, Profile==None: if this is transmitted by a partner, this means that the partner knows that the profile has changed, but can't take responsibility because it was retrieved a too long time ago. The address must be added to the submission_queue.
  • retrieval_timestamp!=None, Profile==None: if this is transmitted by a partner, he tells us that the profile does not exists anymore/is invalid, and takes responsibility for this information.
  • retrieval_timestamp!=None, Profile!=None: if this is transmitted by a partner, he tells us that the profile has changed and he tells us the new content. The partner takes responsibility for this information. For such a state, the hash attribute can be read, and the state can be saved to the database.

.

HashTrie [IMPLEMENTED] (hashtrie.py)
    database_path
    lock
    trieserver (the Popened server process)

    __init__(path, erase=False)
    get_missing_hashes_as_server(socket)
    get_missing_hashes_as_client(socket)
    delete()
    add()
    close(erase=False)

StateDatabase [IMPLEMENTED] (states.py)
    database_path
    Session
    hashtrie
    lock

    cleanup_timestamp  # initialized in __init__

    cleanup()
        update cleanup_timestamp in variables table

    search()

    save(state):
        ValidState => add, (delete old one)
            (!) respect RESUBMISSION_INTERVAL when old Profile exists
            (!) Profile newer than old Profile
        InvalidState => delete

    get_ghosts(list of missing hashes)

    get_invalid_state(binhash, timestamp):
        find webfinger address for binhash from database
        return a State(address=webfinger_address, retrieval_timestamp=timestamp, profile=None)

    get_valid_state(binhash):
        get state from states table
        
        if retrieval_timestamp too old:
            state.Profile := None
            state.retrieval_timestamp := None
            return state
        else:
            return state

    close()


Claim [IMPLEMENTED] (context.py)
    timestamp=time.time()
    state
    partner_name (=None means claim is made by ourselves)

    __cmp__():
        MySelf > other partners
        timestamp

    validate():
        if partner:
            (!) partner not kicked

            if partner.control_sample():
              partner.register_control_sample()
              retrieved_state := State.retrieve(address)
              if not state==retrieved_state:
                  offense := Offense(state, retrieved_state)
                  partner <- offense
                  state = retrieved_state
                  partner = None

        try:
            state.validate()
            make sure self.address==profile.address
        except Violation, violation:
            if partner:
                partner <- violation
            return None

        return state

Submission: [IMPLEMENTED] (context.py)
    webfinger_address

Context: [IMPLEMENTED] (context.py)
    statedb
    partnerdb

    submission_queue
    validation_queue
    assimilation_queue

    synchronization_address

    logger

    submit_address(webfinger_address)

    process_state(state, partner_name):
        if state.retrieval_timestamp: # partner takes responsibility
            claim := Claim(state, partner_name)
            put into validation queue
        else: # partner does not take responsibility
            put state.address into submission queue

    synchronize_as_server(partnersocket, partner_name)
    synchronize_as_client(partnersocket, partner_name)

Application: [IMPLEMENTED] (sduds.py)
    ready_for_synchronization (threading.Event)

    start()
    terminate()
    synchronize_with_partner()