示例#1
0
    def __init__(self, network_uid, peers, state_file):
        self.messenger = None
        self.network_uid = network_uid
        self.peers = peers  # list of peer network uids
        self.quorum_size = len(peers) / 2 + 1
        self.state_file = state_file
        self.lastTime = 0  # last time node sent an update to lead proposer
        self.sendRate = 10  # num millisecs between updates sent to layer above
        self.height = self.get_height(self.network_uid)
        self.temp_updates = [
        ]  #list of string proposals waiting to be sent to level above in hieararchy

        #coordinator based data structures
        self.pending_inter_ledger = dict(
        )  # self seq_num -> transaction - may not need to use (revise new_inter_ledger fct)
        self.uncommitted_nodes = [
        ]  #list of string ids of nodes that have pending inter-ledger transactions (coordinator based)
        self.inter_ledger_seq = dict(
        )  #transaction -> seq (a list of seq num, sndr uid) --- stored in lca of coordinator based alg to track if 2 seq nums have been received for a transaction
        self.inter_ledger_transaction = dict(
        )  #seq #-> tranasaction - used by lca so that after consensus on seq # it can recover transaction and send back to clusters

        self.load_state()

        self.paxos = PaxosInstance(self.network_uid, self.quorum_size,
                                   self.promised_id, self.accepted_id,
                                   self.accepted_value)
示例#2
0
    def advance_instance(self,
                         new_instance_number,
                         new_current_value,
                         catchup=False):
        self.save_state(new_instance_number, new_current_value, None, None,
                        None)

        self.paxos = PaxosInstance(self.network_uid, self.quorum_size, None,
                                   None, None)

        print 'UPDATED: ', new_instance_number, new_current_value
示例#3
0
    def __init__(self, network_uid, peers, state_file):
        self.messenger = None
        self.network_uid = network_uid
        self.peers = peers  # list of peer network uids
        self.quorum_size = len(peers) / 2 + 1
        self.state_file = state_file

        self.load_state()

        self.paxos = PaxosInstance(self.network_uid, self.quorum_size,
                                   self.promised_id, self.accepted_id,
                                   self.accepted_value)
    def advance_instance(self, new_instance_number, new_current_value, catchup=False):
        self.save_state(new_instance_number, new_current_value, None, None, None)
        
        self.paxos = PaxosInstance(self.network_uid, self.quorum_size, None, None, None)

        print 'UPDATED: ', new_instance_number, new_current_value
        self.onUpdateCallback(new_current_value)
示例#5
0
    def __init__(self, network_uid, peers, state_file):
        self.messenger   = None
        self.network_uid = network_uid
        self.peers       = peers            # list of peer network uids
        self.quorum_size = len(peers)/2 + 1
        self.state_file  = state_file

        self.load_state()

        self.paxos = PaxosInstance(self.network_uid, self.quorum_size,
                                   self.promised_id, self.accepted_id,
                                   self.accepted_value)
示例#6
0
class BaseReplicatedValue(object):
    def __init__(self, network_uid, peers, state_file):
        self.messenger = None
        self.network_uid = network_uid
        self.peers = peers  # list of peer network uids
        self.quorum_size = len(peers) / 2 + 1
        self.state_file = state_file
        self.resolution = None
        self.processcounter = None
        self.resolutioncounter = None

        self.load_state()

        self.paxos = PaxosInstance(self.network_uid, self.quorum_size,
                                   self.promised_id, self.accepted_id,
                                   self.accepted_value)

    def set_messenger(self, messenger):
        self.messenger = messenger

    def save_state(self, instance_number, current_value, promised_id,
                   accepted_id, accepted_value):
        '''
        For crash recovery purposes, Paxos requires that some state be saved to
        persistent media prior to sending Promise and Accepted messages. We'll
        also save the state of the multi-paxos chain here so everything is kept
        in one place.
        '''
        self.instance_number = instance_number
        self.current_value = current_value
        self.promised_id = promised_id
        self.accepted_id = accepted_id
        self.accepted_value = accepted_value
        processed_number = self.processcounter
        resultioncounter = self.resolutioncounter

        tmp = self.state_file + '.tmp'

        with open(tmp, 'w') as f:
            f.write(
                json.dumps(
                    dict(instance_number=instance_number,
                         promised_id=promised_id,
                         accepted_id=accepted_id,
                         accepted_value=accepted_value,
                         current_value=current_value,
                         processed_number=processed_number,
                         resultioncounter=resultioncounter)))
            f.flush()
            os.fsync(f.fileno())  # Wait for the data to be written to disk

        # os.rename() is an atomic filesystem operation. By writing the new
        # state to a temporary file and using this method, we avoid the potential
        # for leaving a corrupted state file in the event that a crash/power loss
        # occurs in the middle of update.
        os.rename(tmp, self.state_file)

    def load_state(self):
        if not os.path.exists(self.state_file):
            with open(self.state_file, 'w') as f:
                f.write(
                    json.dumps(
                        dict(instance_number=0,
                             promised_id=None,
                             accepted_id=None,
                             accepted_value=None,
                             current_value=None)))
                f.flush()

        with open(self.state_file) as f:
            m = json.loads(f.read())

            def to_pid(v):
                return ProposalID(*v) if v else None

            self.instance_number = m['instance_number']
            self.current_value = m['current_value']
            self.promised_id = to_pid(m['promised_id'])
            self.accepted_id = to_pid(m['accepted_id'])
            self.accepted_value = m['accepted_value']
            self.processcounter = 0
            self.resolutioncounter = 0

    def propose_update(self, new_value):
        """
        This is a key method that some of the mixin classes override in order
        to provide additional functionality when new values are proposed
        """
        if self.paxos.proposed_value is None:
            self.paxos.propose_value(new_value)

    def advance_instance(self,
                         new_instance_number,
                         new_current_value,
                         catchup=False):
        self.save_state(new_instance_number, new_current_value, None, None,
                        None)

        self.paxos = PaxosInstance(self.network_uid, self.quorum_size, None,
                                   None, None)
        self.resolution = new_current_value
        print 'UPDATED: ', new_instance_number, new_current_value
        self.resolutioncounter += 1

    def send_prepare(self, proposal_id):
        for uid in self.peers:
            self.messenger.send_prepare(uid, self.instance_number, proposal_id)

    def send_accept(self, proposal_id, proposal_value):
        for uid in self.peers:
            self.messenger.send_accept(uid, self.instance_number, proposal_id,
                                       proposal_value)

    def send_accepted(self, proposal_id, proposal_value):
        for uid in self.peers:
            self.messenger.send_accepted(uid, self.instance_number,
                                         proposal_id, proposal_value)

    def receive_prepare(self, from_uid, instance_number, proposal_id):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        m = self.paxos.receive_prepare(Prepare(from_uid, proposal_id))

        if isinstance(m, Promise):
            self.save_state(self.instance_number, self.current_value,
                            m.proposal_id, m.last_accepted_id,
                            m.last_accepted_value)

            self.messenger.send_promise(from_uid, self.instance_number,
                                        m.proposal_id, m.last_accepted_id,
                                        m.last_accepted_value)
        else:
            self.messenger.send_nack(from_uid, self.instance_number,
                                     proposal_id, self.promised_id)

    def receive_nack(self, from_uid, instance_number, proposal_id,
                     promised_proposal_id):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        self.paxos.receive_nack(
            Nack(from_uid, self.network_uid, proposal_id,
                 promised_proposal_id))

    def receive_promise(self, from_uid, instance_number, proposal_id,
                        last_accepted_id, last_accepted_value):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        m = self.paxos.receive_promise(
            Promise(from_uid, self.network_uid, proposal_id, last_accepted_id,
                    last_accepted_value))

        if isinstance(m, Accept):
            self.send_accept(m.proposal_id, m.proposal_value)

    def receive_accept(self, from_uid, instance_number, proposal_id,
                       proposal_value):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        m = self.paxos.receive_accept(
            Accept(from_uid, proposal_id, proposal_value))

        if isinstance(m, Accepted):
            self.save_state(self.instance_number, self.current_value,
                            self.promised_id, proposal_id, proposal_value)
            self.send_accepted(m.proposal_id, m.proposal_value)
        else:
            self.messenger.send_nack(from_uid, self.instance_number,
                                     proposal_id, self.promised_id)

    def receive_accepted(self, from_uid, instance_number, proposal_id,
                         proposal_value):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        m = self.paxos.receive_accepted(
            Accepted(from_uid, proposal_id, proposal_value))

        if isinstance(m, Resolution):
            self.advance_instance(self.instance_number + 1, proposal_value)

    def receive_resolutionRequest(self, from_uid):
        resolution = 'No Resolution'
        if self.paxos.final_value != None:
            resolution = self.paxos.final_value
        else:
            if self.resolution != None:
                resolution = self.resolution
        self.messenger.send_resolution('Z', resolution)
class BaseReplicatedValue (object):

    def __init__(self, network_uid, peers, state_file, onUpdateCallback):
        self.messenger   = None
        self.network_uid = network_uid
        self.peers       = peers            # list of peer network uids
        self.quorum_size = len(peers)/2 + 1
        self.state_file  = state_file
        self.onUpdateCallback = onUpdateCallback

        self.load_state()

        self.paxos = PaxosInstance(self.network_uid, self.quorum_size,
                                   self.promised_id, self.accepted_id,
                                   self.accepted_value)


    def set_messenger(self, messenger):
        self.messenger = messenger

        
    def save_state(self, instance_number, current_value, promised_id, accepted_id, accepted_value):
        '''
        For crash recovery purposes, Paxos requires that some state be saved to
        persistent media prior to sending Promise and Accepted messages. We'll
        also save the state of the multi-paxos chain here so everything is kept
        in one place.
        '''
        self.instance_number = instance_number
        self.current_value   = current_value
        self.promised_id     = promised_id
        self.accepted_id     = accepted_id
        self.accepted_value  = accepted_value

        tmp = self.state_file + '.tmp'
        
        with open(tmp, 'w') as f:
            f.write( json.dumps( dict(instance_number = instance_number,
                                      promised_id     = promised_id,
                                      accepted_id     = accepted_id,
                                      accepted_value  = accepted_value,
                                      current_value   = current_value) ) )
            f.flush()
            os.fsync(f.fileno()) # Wait for the data to be written to disk
            
        # os.rename() is an atomic filesystem operation. By writing the new
        # state to a temporary file and using this method, we avoid the potential
        # for leaving a corrupted state file in the event that a crash/power loss
        # occurs in the middle of update.
        os.rename(tmp, self.state_file)

        
    def load_state(self):
        if not os.path.exists(self.state_file):
            with open(self.state_file, 'w') as f:
                f.write( json.dumps( dict(instance_number = 0,
                                          promised_id     = None,
                                          accepted_id     = None,
                                          accepted_value  = None,
                                          current_value   = None) ) )
                f.flush()

        with open(self.state_file) as f:
            m = json.loads(f.read())

            def to_pid(v):
                return ProposalID(*v) if v else None

            self.instance_number = m['instance_number']
            self.current_value   = m['current_value']
            self.promised_id     = to_pid(m['promised_id'])
            self.accepted_id     = to_pid(m['accepted_id'])
            self.accepted_value  = m['accepted_value']


    def propose_update(self, new_value):
        """
        This is a key method that some of the mixin classes override in order
        to provide additional functionality when new values are proposed
        """
        if self.paxos.proposed_value is None:
            self.paxos.propose_value( new_value )


    def advance_instance(self, new_instance_number, new_current_value, catchup=False):
        self.save_state(new_instance_number, new_current_value, None, None, None)
        
        self.paxos = PaxosInstance(self.network_uid, self.quorum_size, None, None, None)

        print 'UPDATED: ', new_instance_number, new_current_value
        self.onUpdateCallback(new_current_value)


    def send_prepare(self, proposal_id):
        for uid in self.peers:
            self.messenger.send_prepare(uid, self.instance_number, proposal_id)


    def send_accept(self, proposal_id, proposal_value):
        for uid in self.peers:
            self.messenger.send_accept(uid, self.instance_number, proposal_id, proposal_value)

            
    def send_accepted(self, proposal_id, proposal_value):
        for uid in self.peers:
            self.messenger.send_accepted(uid, self.instance_number, proposal_id, proposal_value)


    def receive_prepare(self, from_uid, instance_number, proposal_id):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return
        
        m = self.paxos.receive_prepare( Prepare(from_uid, proposal_id) )
        
        if isinstance(m, Promise):
            self.save_state(self.instance_number, self.current_value, m.proposal_id,
                            m.last_accepted_id, m.last_accepted_value)
            
            self.messenger.send_promise(from_uid, self.instance_number,
                                        m.proposal_id, m.last_accepted_id, m.last_accepted_value )
        else:
            self.messenger.send_nack(from_uid, self.instance_number, proposal_id, self.promised_id)


    def receive_nack(self, from_uid, instance_number, proposal_id, promised_proposal_id):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        self.paxos.receive_nack( Nack(from_uid, self.network_uid, proposal_id, promised_proposal_id) )
        
            
    def receive_promise(self, from_uid, instance_number, proposal_id, last_accepted_id, last_accepted_value):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        m = self.paxos.receive_promise( Promise(from_uid, self.network_uid, proposal_id,
                                                last_accepted_id, last_accepted_value) )
        
        if isinstance(m, Accept):
            self.send_accept(m.proposal_id, m.proposal_value)

            
    def receive_accept(self, from_uid, instance_number, proposal_id, proposal_value):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        m = self.paxos.receive_accept( Accept(from_uid, proposal_id, proposal_value) )
        
        if isinstance(m, Accepted):
            self.save_state(self.instance_number, self.current_value, self.promised_id,
                            proposal_id, proposal_value)
            self.send_accepted(m.proposal_id, m.proposal_value)
        else:
            self.messenger.send_nack(from_uid, self.instance_number, proposal_id, self.promised_id)

            
    def receive_accepted(self, from_uid, instance_number, proposal_id, proposal_value):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        m = self.paxos.receive_accepted( Accepted(from_uid, proposal_id, proposal_value) )
        
        if isinstance(m, Resolution):
            self.advance_instance( self.instance_number + 1, proposal_value )
示例#8
0
class BaseReplicatedValue(object):
    def __init__(self, network_uid, peers, state_file):
        self.messenger = None
        self.network_uid = network_uid
        self.peers = peers  # list of peer network uids
        self.quorum_size = len(peers) / 2 + 1
        self.state_file = state_file
        self.lastTime = 0  # last time node sent an update to lead proposer
        self.sendRate = 10  # num millisecs between updates sent to layer above
        self.height = self.get_height(self.network_uid)
        self.temp_updates = [
        ]  #list of string proposals waiting to be sent to level above in hieararchy

        #coordinator based data structures
        self.pending_inter_ledger = dict(
        )  # self seq_num -> transaction - may not need to use (revise new_inter_ledger fct)
        self.uncommitted_nodes = [
        ]  #list of string ids of nodes that have pending inter-ledger transactions (coordinator based)
        self.inter_ledger_seq = dict(
        )  #transaction -> seq (a list of seq num, sndr uid) --- stored in lca of coordinator based alg to track if 2 seq nums have been received for a transaction
        self.inter_ledger_transaction = dict(
        )  #seq #-> tranasaction - used by lca so that after consensus on seq # it can recover transaction and send back to clusters

        self.load_state()

        self.paxos = PaxosInstance(self.network_uid, self.quorum_size,
                                   self.promised_id, self.accepted_id,
                                   self.accepted_value)

    #coordinator based alg (and also the other inter ledger alg) - call wwhen a new inter ledger transaction becomes initiated and pending
    def new_inter_ledger(self, transaction):
        self.pending_inter_ledger[transaction] = True
        sndr, rcvr, amount = transaction.split('-', 2)
        self.uncommitted_nodes.append(sndr)

    def double_spending(self, network_uid):
        if network_uid in self.uncommitted_nodes:
            return True
        else:
            return False

    def send_updates(self, proposal_value):
        #TODO remove config so you don't need to import it here
        #append to temp list of updates waiting
        #case on whether this is a single proposal or ledger
        timeMillisecs = int(round(time.time() * 1000))
        if " " in proposal_value:
            #print("---------------------_REACHED")
            transactions = proposal_value.strip('][').replace('\'', '').split(
                ', ')  # returns list of string proposals
            #print("-----send_updates: ", transactions)
            self.temp_updates.extend(transactions)
        else:
            transactions = proposal_value.strip('][').split(', ')
            #print("-----send_updates: ", transactions)
            #print("-----type:", type(transactions))
            #print("-----temp:", self.temp_updates)
            self.temp_updates.append(proposal_value)
        self.handle_time(timeMillisecs)

    def handle_time(self, timeMillisecs):

        if self.network_uid in config.leader.keys():
            #print("handle time called!!")
            if (timeMillisecs - self.lastTime > self.sendRate):
                #print("temp_updates: ", self.temp_updates)
                if len(self.temp_updates) > 0:
                    if not " " in str(self.temp_updates):
                        self.messenger.send_update(self.temp_updates.pop())
                    else:
                        self.messenger.send_update(self.temp_updates)
                    self.temp_updates = []
                    self.lastTime = timeMillisecs
            # else:
            #     #append to temp list of updates waiting
            #     #case on whether this is a single proposal or ledger
            #     if proposal_value.contains(" "):
            #         transactions = proposal_value.strip('][').split(', ') # returns list of string proposals
            #         self.temp_updates.append(transactions)
            #     else:
            #         self.temp_updates.append(proposal_value)

    def get_network_uid(self):
        return self.network_uid

    #get cluster of node with network_uid (string)
    def get_cluster(self, network_uid):
        cluster = 0
        if int(network_uid) == 1 or int(network_uid) == 2 or int(
                network_uid) == 3:
            cluster = 1
        else:
            cluster = int(network_uid[0])
        return cluster

    def get_height(self, network_uid):
        return (4 - len(network_uid))

    def same_ledger(self, network_uid_a, network_uid_b):
        if self.get_cluster(network_uid_a) == self.get_cluster(
                network_uid_b) and self.get_height(
                    network_uid_a) == self.get_height(network_uid_b):
            return True
        else:
            return False

    def set_messenger(self, messenger):
        self.messenger = messenger

    def save_state(self, instance_number, current_value, promised_id,
                   accepted_id, accepted_value):
        '''
        For crash recovery purposes, Paxos requires that some state be saved to
        persistent media prior to sending Promise and Accepted messages. We'll
        also save the state of the multi-paxos chain here so everything is kept
        in one place.
        '''
        self.instance_number = instance_number
        self.current_value = current_value
        self.promised_id = promised_id
        self.accepted_id = accepted_id
        self.accepted_value = accepted_value

        # tmp = self.state_file + '.tmp'

        # with open(tmp, 'w') as f:
        #     f.write( json.dumps( dict(instance_number = instance_number,
        #                               promised_id     = promised_id,
        #                               accepted_id     = accepted_id,
        #                               accepted_value  = accepted_value,
        #                               current_value   = current_value) ) )
        #     f.flush()
        #     os.fsync(f.fileno()) # Wait for the data to be written to disk

        # # os.rename() is an atomic filesystem operation. By writing the new
        # # state to a temporary file and using this method, we avoid the potential
        # # for leaving a corrupted state file in the event that a crash/power loss
        # # occurs in the middle of update.
        # os.rename(tmp, self.state_file)

    def load_state(self):
        self.instance_number = 0
        self.current_value = None
        self.promised_id = None
        self.accepted_id = None
        self.accepted_value = None
        # if not os.path.exists(self.state_file):
        #     with open(self.state_file, 'w') as f:
        #         f.write( json.dumps( dict(instance_number = 0,
        #                                   promised_id     = None,
        #                                   accepted_id     = None,
        #                                   accepted_value  = None,
        #                                   current_value   = None) ) )
        #         f.flush()

        # with open(self.state_file) as f:
        #     m = json.loads(f.read())

        #     def to_pid(v):
        #         return ProposalID(*v) if v else None

        #     self.instance_number = m['instance_number']
        #     self.current_value   = m['current_value']
        #     self.promised_id     = to_pid(m['promised_id'])
        #     self.accepted_id     = to_pid(m['accepted_id'])
        #     self.accepted_value  = m['accepted_value']

    def propose_update(self, new_value):
        """
        This is a key method that some of the mixin classes override in order
        to provide additional functionality when new values are proposed
        """
        # this happens when client sends propose message to server, and received by messenger of replicated_value
        if self.paxos.proposed_value is None:
            self.paxos.propose_value(new_value, False)
        #if self.instance_number > 0:

    def advance_instance(self,
                         new_instance_number,
                         new_current_value,
                         catchup=False):
        self.save_state(new_instance_number, new_current_value, None, None,
                        None)

        # TODO - line commented out below to keep samw leader for now
        #self.paxos = PaxosInstance(self.network_uid, self.quorum_size, None, None, None)

        print('UPDATED -- instance_number: ', new_instance_number, 'value: ',
              new_current_value)

    def send_prepare(self, proposal_id):
        for uid in self.peers:
            self.messenger.send_prepare(uid, self.instance_number, proposal_id)

    def send_accept(self, proposal_id, proposal_value):
        for uid in self.peers:
            self.messenger.send_accept(uid, self.instance_number, proposal_id,
                                       proposal_value)

    def send_accepted(self, proposal_id, proposal_value):
        for uid in self.peers:
            self.messenger.send_accepted(uid, self.instance_number,
                                         proposal_id, proposal_value)

    def receive_prepare(self, from_uid, instance_number, proposal_id):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        m = self.paxos.receive_prepare(Prepare(from_uid, proposal_id))

        if isinstance(m, Promise):
            self.save_state(self.instance_number, self.current_value,
                            m.proposal_id, m.last_accepted_id,
                            m.last_accepted_value)

            self.messenger.send_promise(from_uid, self.instance_number,
                                        m.proposal_id, m.last_accepted_id,
                                        m.last_accepted_value)
        else:
            self.messenger.send_nack(from_uid, self.instance_number,
                                     proposal_id, self.promised_id)

    def receive_nack(self, from_uid, instance_number, proposal_id,
                     promised_proposal_id):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        self.paxos.receive_nack(
            Nack(from_uid, self.network_uid, proposal_id,
                 promised_proposal_id))

    def receive_promise(self, from_uid, instance_number, proposal_id,
                        last_accepted_id, last_accepted_value):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        m = self.paxos.receive_promise(
            Promise(from_uid, self.network_uid, proposal_id, last_accepted_id,
                    last_accepted_value))

        if isinstance(m, Accept):
            self.send_accept(m.proposal_id, m.proposal_value)

    def receive_accept(self, from_uid, instance_number, proposal_id,
                       proposal_value):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        m = self.paxos.receive_accept(
            Accept(from_uid, proposal_id, proposal_value))

        if isinstance(m, Accepted):
            self.save_state(self.instance_number, self.current_value,
                            self.promised_id, proposal_id, proposal_value)
            self.send_accepted(m.proposal_id, m.proposal_value)
        else:
            self.messenger.send_nack(from_uid, self.instance_number,
                                     proposal_id, self.promised_id)

    def receive_accepted(self, from_uid, instance_number, proposal_id,
                         proposal_value):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        m = self.paxos.receive_accepted(
            Accepted(from_uid, proposal_id, proposal_value))

        if isinstance(m, Resolution):
            print("----Resolution!!!")
            if self.height == 0:
                result = self.paxos.update_ledger(proposal_value, True)
            else:
                result = self.paxos.update_ledger(proposal_value, False)

            self.send_updates(
                proposal_value
            )  #send update if last update was more than 10millisec ago
            if self.height == 0:
                self.messenger.send_reply(result)
            #self.messenger.send_reply(self.accepted_value)
            self.advance_instance(self.instance_number + 1, proposal_value)

    #Coordinator based algorithm
    def receive_propose_to_lca(self, proposal):
        sndr, rcvr, amount = proposal.split('-', 2)
        if not self.get_height(sndr) == 0 or not self.get_height(rcvr) == 0:
            print(
                "------error in receive_propose_to_lca: inter-ledger transaction not at leaf level"
            )
            return
        sndr_cluster = self.get_cluster(sndr)
        rcvr_cluster = self.get_cluster(rcvr)
        sndr_leader_addr = config.peers[0][sndr_cluster][str(sndr_cluster *
                                                             1000)]
        rcvr_leader_addr = config.peers[0][rcvr_cluster][str(rcvr_cluster *
                                                             1000)]
        self.send_seq_req(sndr_leader_addr, proposal)
        self.send_seq_req(rcvr_leader_addr, proposal)

    def send_seq_req(self, to_addr, proposal):
        self.messenger.send_seq_req(to_addr, proposal)

    def receive_seq_req(self, lca_id, proposal):
        print("----received seq request")
        # get next seq num
        seq_num = self.paxos.next_seq_num()
        # TODO store seq num and transaction mapping ?
        #send seq num back to lca
        to_addr = config.peers[self.get_height(lca_id)][self.get_cluster(
            lca_id)][lca_id]
        self.messenger.send_seq(to_addr, seq_num, proposal)

    def receive_seq(self, seq_num, proposal):
        print("-----received seq num")
        #remember seq_nums are lists right now of form [seq num, sndr's uid]
        if proposal in self.inter_ledger_seq.keys():
            seq_1 = self.inter_ledger_seq[proposal][0]
            #remove proposal now that we have two seq_nums
            del self.inter_ledger_seq[proposal]
            seq_2 = seq_num[0]
            new_seq = '{0}-{1}'.format(seq_1, seq_2)

            print("new seq: ", new_seq)
            self.inter_ledger_transaction[new_seq] = proposal
            self.propose_update_c(new_seq)
        else:
            self.inter_ledger_seq[proposal] = seq_num

    #Coordinator based algorithm - consensus functions for lca's cluster
    def propose_update_c(self, new_value):
        # check whether leader is steady and prev sent prepare
        if self.paxos.proposed_value is None:
            self.paxos.propose_value(new_value, False)
        if (self.instance_number > 0):
            print("skipping prepare")
            self.paxos.propose_value(new_value, True)
            m = self.paxos.accept(
            )  # skip sending prepare and waiting for promise
            self.send_accept_c(m.proposal_id, m.proposal_value)
        else:
            m = self.paxos.prepare()  # Advances to the next proposal number
            self.send_prepare_c(m.proposal_id)

    def send_prepare_c(self, proposal_id):
        for uid in self.peers:
            self.messenger.send_prepare_c(uid, self.instance_number,
                                          proposal_id)

    def receive_prepare_c(self, from_uid, instance_number, proposal_id):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        m = self.paxos.receive_prepare(Prepare(from_uid, proposal_id))

        if isinstance(m, Promise):
            self.save_state(self.instance_number, self.current_value,
                            m.proposal_id, m.last_accepted_id,
                            m.last_accepted_value)

            self.messenger.send_promise_c(from_uid, self.instance_number,
                                          m.proposal_id, m.last_accepted_id,
                                          m.last_accepted_value)
        else:
            self.messenger.send_nack(from_uid, self.instance_number,
                                     proposal_id, self.promised_id)

    def receive_promise_c(self, from_uid, instance_number, proposal_id,
                          last_accepted_id, last_accepted_value):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        m = self.paxos.receive_promise(
            Promise(from_uid, self.network_uid, proposal_id, last_accepted_id,
                    last_accepted_value))

        if isinstance(m, Accept):
            self.send_accept_c(m.proposal_id, m.proposal_value)

    def send_accept_c(self, proposal_id, proposal_value):
        for uid in self.peers:
            self.messenger.send_accept_c(uid, self.instance_number,
                                         proposal_id, proposal_value)

    def receive_accept_c(self, from_uid, instance_number, proposal_id,
                         proposal_value):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        m = self.paxos.receive_accept(
            Accept(from_uid, proposal_id, proposal_value))

        if isinstance(m, Accepted):
            self.save_state(self.instance_number, self.current_value,
                            self.promised_id, proposal_id, proposal_value)
            self.send_accepted_c(m.proposal_id, m.proposal_value)
        else:
            self.messenger.send_nack(from_uid, self.instance_number,
                                     proposal_id, self.promised_id)

    def send_accepted_c(self, proposal_id, proposal_value):
        for uid in self.peers:
            self.messenger.send_accepted_c(uid, self.instance_number,
                                           proposal_id, proposal_value)

    def leaf_cluster_addrs(self, cluster):
        yield config.peers[0][cluster][str(cluster * 1000)]
        yield config.peers[0][cluster][str(cluster * 1000 + 1)]
        yield config.peers[0][cluster][str(cluster * 1000 + 2)]

    def receive_accepted_c(self, from_uid, instance_number, proposal_id,
                           proposal_value):
        # Only process messages for the current link in the multi-paxos chain
        if instance_number != self.instance_number:
            return

        m = self.paxos.receive_accepted(
            Accepted(from_uid, proposal_id, proposal_value))

        if isinstance(m, Resolution):
            print("----Resolution!!!")
            #get transaction from dict based on seq num (proposal_value) and then remove from dict
            if proposal_value in self.inter_ledger_transaction:
                # in lead proposer of cluster
                transaction = self.inter_ledger_transaction[proposal_value]
                del self.inter_ledger_transaction[proposal_value]
                #determine involved clusters, and their lead proposers
                sndr, rcvr, amount = transaction.split('-', 2)
                sndr_cluster = self.get_cluster(sndr)
                rcvr_cluster = self.get_cluster(rcvr)
                for addr in self.leaf_cluster_addrs(sndr_cluster):
                    self.messenger.send_lcacommit_c(addr, proposal_value,
                                                    transaction)
                for addr in self.leaf_cluster_addrs(rcvr_cluster):
                    self.messenger.send_lcacommit_c(addr, proposal_value,
                                                    transaction)
            #advance instance
            self.advance_instance(self.instance_number + 1, proposal_value)

    def receive_lcacommit_c(self, seq_num, transaction):
        print("received lcacommit")
        #update data structures so that sndr can now transact again
        if transaction in self.pending_inter_ledger.keys():
            del self.pending_inter_ledger[transaction]
        sndr, rcvr, amount = transaction.split('-', 2)
        if sndr in self.uncommitted_nodes:
            self.uncommitted_nodes.remove(sndr)
        #update ledger
        result = self.paxos.update_ledger(transaction, True)
        print(result)
        self.send_updates(transaction)

    # mobility
    def receive_mobility_req(self, node, new_cluster):
        # if (self.same_ledger(self.uid, node)) {

        # } else {

        # }
        for addr in self.leaf_cluster_addrs(new_cluster):
            self.messenger.send_account(addr, node, self.paxos.accounts[node])

    def receive_account(self, node, account):
        self.paxos.update_account(node, account)
        print("updated account")