Exemplo n.º 1
0
 def create(self, msg: Dict, **kwargs) -> ConsistencyProof:
     cp = ConsistencyProof(**msg)
     if cp.ledgerId != kwargs['ledger_id'] \
             or cp.seqNoStart != kwargs['seq_no_start'] \
             or cp.seqNoEnd != kwargs['seq_no_end']:
         raise MismatchedMessageReplyException
     return cp
Exemplo n.º 2
0
def test_missing_txn_request(ledger_no_genesis):
    """
    Testing LedgerManager's `_missing_txns`
    """
    ledger = ledger_no_genesis
    for i in range(20):
        txn = random_txn(i)
        ledger.add(txn)

    # Callbacks don't matter in this test
    ledger_info = LedgerInfo(0, ledger, *[None] * 6)
    assert ledger_info.catchupReplyTimer is None
    assert LedgerManager._missing_txns(ledger_info) == (False, 0)

    ledger_info.catchupReplyTimer = time.perf_counter()

    # Ledger is already ahead
    cp = ConsistencyProof(0, 1, 10, 1, 1,
                          'GJybBTHjzMzPWsE6n9qNQWAmhJP88dTcdbgkGLhYGFYn',
                          'Gv9AdSeib9EnBakfpgkU79dPMtjcnFWXvXeiCX4QAgAC', [])
    ledger_info.catchUpTill = cp
    ledger_info.receivedCatchUpReplies = [(i, {}) for i in range(1, 15)]
    assert not LedgerManager._missing_txns(ledger_info)[0]

    # Ledger is behind but catchup replies present
    cp = ConsistencyProof(0, 1, 30, 1, 1,
                          'Gv9AdSeib9EnBakfpgkU79dPMtjcnFWXvXeiCX4QAgAC',
                          'EEUnqHf2GWEpvmibiXDCZbNDSpuRgqdvCpJjgp3KFbNC', [])
    ledger_info.catchUpTill = cp
    ledger_info.receivedCatchUpReplies = [(i, {}) for i in range(21, 31)]
    assert not LedgerManager._missing_txns(ledger_info)[0]
    ledger_info.receivedCatchUpReplies = [(i, {}) for i in range(21, 35)]
    assert not LedgerManager._missing_txns(ledger_info)[0]

    # Ledger is behind
    cp = ConsistencyProof(0, 1, 30, 1, 1,
                          'Gv9AdSeib9EnBakfpgkU79dPMtjcnFWXvXeiCX4QAgAC',
                          'EEUnqHf2GWEpvmibiXDCZbNDSpuRgqdvCpJjgp3KFbNC', [])
    ledger_info.catchUpTill = cp
    ledger_info.receivedCatchUpReplies = [(i, {}) for i in range(21, 26)]
    assert LedgerManager._missing_txns(ledger_info) == (True, 5)

    ledger_info.receivedCatchUpReplies = [(i, {}) for i in range(26, 31)]
    assert LedgerManager._missing_txns(ledger_info) == (True, 5)
Exemplo n.º 3
0
    def processConsistencyProof(self, proof: ConsistencyProof, frm: str):
        logger.debug("{} received consistency proof: {} from {}".format(
            self, proof, frm))
        ledgerId = getattr(proof, f.LEDGER_ID.nm)
        ledgerInfo = self.getLedgerInfoByType(ledgerId)
        ledgerInfo.recvdConsistencyProofs[frm] = ConsistencyProof(*proof)

        if self.canProcessConsistencyProof(proof):
            canCatchup, catchUpFrm = self.canStartCatchUpProcess(ledgerId)
            if canCatchup:
                self.startCatchUpProcess(ledgerId, catchUpFrm)
Exemplo n.º 4
0
    def _finish_no_catchup(self):
        root = Ledger.hashToStr(self._ledger.tree.root_hash)
        last_3pc = self._get_last_txn_3PC_key()
        if not last_3pc:
            self._finish()
            return

        view_no, pp_seq_no = last_3pc
        cons_proof = ConsistencyProof(self._ledger_id, self._ledger.size,
                                      self._ledger.size, view_no, pp_seq_no,
                                      root, root, [])
        self._finish(cons_proof)
Exemplo n.º 5
0
def test_missing_txn_request(ledger_no_genesis):
    """
    Testing LedgerManager's `_missing_txns`
    """
    ledger = ledger_no_genesis
    for i in range(20):
        txn = random_txn(i)
        ledger.add(txn)

    service = create_fake_catchup_rep_service(ledger)
    assert service._num_missing_txns() == 0

    # Ledger is already ahead
    cp = ConsistencyProof(0, 1, 10, 1, 1,
                          'GJybBTHjzMzPWsE6n9qNQWAmhJP88dTcdbgkGLhYGFYn',
                          'Gv9AdSeib9EnBakfpgkU79dPMtjcnFWXvXeiCX4QAgAC', [])
    service._catchup_till = cp
    service._received_catchup_txns = [(i, {}) for i in range(1, 15)]
    assert service._num_missing_txns() == 0

    # Ledger is behind but catchup replies present
    cp = ConsistencyProof(0, 1, 30, 1, 1,
                          'Gv9AdSeib9EnBakfpgkU79dPMtjcnFWXvXeiCX4QAgAC',
                          'EEUnqHf2GWEpvmibiXDCZbNDSpuRgqdvCpJjgp3KFbNC', [])
    service._catchup_till = cp
    service._received_catchup_txns = [(i, {}) for i in range(21, 31)]
    assert service._num_missing_txns() == 0
    service._received_catchup_txns = [(i, {}) for i in range(21, 35)]
    assert service._num_missing_txns() == 0

    # Ledger is behind
    cp = ConsistencyProof(0, 1, 30, 1, 1,
                          'Gv9AdSeib9EnBakfpgkU79dPMtjcnFWXvXeiCX4QAgAC',
                          'EEUnqHf2GWEpvmibiXDCZbNDSpuRgqdvCpJjgp3KFbNC', [])
    service._catchup_till = cp
    service._received_catchup_txns = [(i, {}) for i in range(21, 26)]
    assert service._num_missing_txns() == 5

    service._received_catchup_txns = [(i, {}) for i in range(26, 31)]
    assert service._num_missing_txns() == 5
Exemplo n.º 6
0
    def _get_cons_proof_for_catchup(self):
        if not self._quorum.consistency_proof.is_reached(len(
                self._cons_proofs)):
            return None

        grpd_prf, _ = self._get_group_consistency_proofs(self._cons_proofs)
        result = self._get_latest_reliable_proof(grpd_prf)
        if not result:
            logger.info("{} cannot start catchup since received only {} "
                        "consistency proofs but need at least {}".format(
                            self, len(self._cons_proofs),
                            self._quorum.consistency_proof.value))
            return None

        return ConsistencyProof(self._ledger_id, *result)
Exemplo n.º 7
0
    def _build_consistency_proof(
            self, ledger_id: int, seq_no_start: int,
            seq_no_end: int) -> Optional[ConsistencyProof]:
        ledger = self._provider.ledger(ledger_id)

        if seq_no_end < seq_no_start:
            logger.error(
                "{} cannot build consistency proof: end {} is less than start {}"
                .format(self, seq_no_end, seq_no_start))
            return

        if seq_no_start > ledger.size:
            logger.error(
                "{} cannot build consistency proof: start {} is more than ledger size {}"
                .format(self, seq_no_start, ledger.size))
            return

        if seq_no_end > ledger.size:
            logger.error(
                "{} cannot build consistency proof: end {} is more than ledger size {}"
                .format(self, seq_no_end, ledger.size))
            return

        if seq_no_start == 0:
            # Consistency proof for an empty tree cannot exist. Using the root
            # hash now so that the node which is behind can verify that
            # TODO: Make this an empty list
            old_root = ledger.tree.root_hash
            old_root = Ledger.hashToStr(old_root)
            proof = [
                old_root,
            ]
        else:
            proof = self._make_consistency_proof(ledger, seq_no_start,
                                                 seq_no_end)
            old_root = ledger.tree.merkle_tree_hash(0, seq_no_start)
            old_root = Ledger.hashToStr(old_root)

        new_root = ledger.tree.merkle_tree_hash(0, seq_no_end)
        new_root = Ledger.hashToStr(new_root)

        # TODO: Delete when INDY-1946 gets implemented
        three_pc_key = self._provider.three_phase_key_for_txn_seq_no(
            ledger_id, seq_no_end)
        view_no, pp_seq_no = three_pc_key if three_pc_key else (0, 0)

        return ConsistencyProof(ledger_id, seq_no_start, seq_no_end, view_no,
                                pp_seq_no, old_root, new_root, proof)
Exemplo n.º 8
0
 def _validate_requested_cons_proof(self, **kwargs):
     if kwargs['ledger_id'] in self.ledger_ids and \
             (isinstance(kwargs['seq_no_start'], int) and kwargs[
                 'seq_no_start'] > 0) and \
             (isinstance(kwargs['seq_no_end'], int) and kwargs[
                 'seq_no_end'] > 0):
         if 'cons_proof' in kwargs:
             try:
                 # the input is expected as a dict (serialization with ujson==1.33)
                 return ConsistencyProof(**kwargs['cons_proof'])
             except TypeError as ex:
                 logger.warning(
                     '{} could not create CONSISTENCY_PROOF out of {}'.
                     format(self, **kwargs['cons_proof']))
         else:
             return True
Exemplo n.º 9
0
    def _buildConsistencyProof(self, ledgerId, seqNoStart, seqNoEnd):

        ledger = self.getLedgerInfoByType(ledgerId).ledger

        ledgerSize = ledger.size
        if seqNoStart > ledgerSize:
            logger.error("{} cannot build consistency proof from {} "
                         "since its ledger size is {}".format(
                             self, seqNoStart, ledgerSize))
            return
        if seqNoEnd > ledgerSize:
            logger.error("{} cannot build consistency "
                         "proof till {} since its ledger size is {}".format(
                             self, seqNoEnd, ledgerSize))
            return
        if seqNoEnd < seqNoStart:
            self.error('{} cannot build consistency proof since end {} is '
                       'lesser than start {}'.format(self, seqNoEnd,
                                                     seqNoStart))
            return

        if seqNoStart == 0:
            # Consistency proof for an empty tree cannot exist. Using the root
            # hash now so that the node which is behind can verify that
            # TODO: Make this an empty list
            oldRoot = ledger.tree.root_hash
            proof = [
                oldRoot,
            ]
        else:
            proof = ledger.tree.consistency_proof(seqNoStart, seqNoEnd)
            oldRoot = ledger.tree.merkle_tree_hash(0, seqNoStart)

        newRoot = ledger.tree.merkle_tree_hash(0, seqNoEnd)
        key = self.owner.three_phase_key_for_txn_seq_no(ledgerId, seqNoEnd)
        logger.debug('{} found 3 phase key {} for ledger {} seqNo {}'.format(
            self, key, ledgerId, seqNoEnd))
        if key is None:
            # The node receiving consistency proof should check if it has
            # received this sentinel 3 phase key (0, 0) in spite of seeing a
            # non-zero txn seq no
            key = (0, 0)

        return ConsistencyProof(ledgerId, seqNoStart, seqNoEnd, *key,
                                Ledger.hashToStr(oldRoot),
                                Ledger.hashToStr(newRoot),
                                [Ledger.hashToStr(p) for p in proof])
Exemplo n.º 10
0
    def startCatchUpProcess(self, ledgerId: int, proof: ConsistencyProof):
        if ledgerId not in self.ledgerRegistry:
            self.discard(proof,
                         reason="Unknown ledger type {}".format(ledgerId))
            return

        self.do_pre_catchup(ledgerId)
        logger.debug("{} started catching up with consistency proof {}".format(
            self, proof))

        if proof is None:
            self.catchupCompleted(ledgerId)
            return

        ledgerInfo = self.getLedgerInfoByType(ledgerId)
        ledgerInfo.state = LedgerState.syncing
        ledgerInfo.consistencyProofsTimer = None
        ledgerInfo.recvdConsistencyProofs = {}

        p = ConsistencyProof(*proof)
        ledgerInfo.catchUpTill = p

        if self.mark_catchup_completed_if_possible(ledgerInfo):
            logger.debug(
                '{} found that ledger {} does not need catchup'.format(
                    self, ledgerId))
        else:
            eligible_nodes = self.nodes_to_request_txns_from
            if eligible_nodes:
                reqs = self.getCatchupReqs(p)
                for (req, to) in zip(reqs, eligible_nodes):
                    self.sendTo(req, to)
                if reqs:
                    ledgerInfo.catchupReplyTimer = time.perf_counter()
                    batchSize = getattr(reqs[0], f.SEQ_NO_END.nm) - \
                                getattr(reqs[0], f.SEQ_NO_START.nm) + 1
                    timeout = self._getCatchupTimeout(len(reqs), batchSize)
                    self._schedule(
                        partial(self.request_txns_if_needed, ledgerId),
                        timeout)
            else:
                logger.info(
                    '{} needs to catchup ledger {} but it has not found'
                    ' any connected nodes'.format(self, ledgerId))
Exemplo n.º 11
0
    def process_consistency_proof(self, proof: ConsistencyProof, frm: str):
        if not self._can_process_consistency_proof(proof):
            return

        logger.info("{} received consistency proof: {} from {}".format(
            self, proof, frm))
        self._cons_proofs[frm] = ConsistencyProof(*proof)

        if not self._is_catchup_needed():
            self._finish_no_catchup()
            return

        if self._should_schedule_reask_cons_proofs():
            self._schedule_reask_cons_proof()

        cp = self._get_cons_proof_for_catchup()
        if not cp:
            return

        self._finish(cp)
Exemplo n.º 12
0
    def canStartCatchUpProcess(self, ledgerId: int):
        ledgerInfo = self.getLedgerInfoByType(ledgerId)
        recvdConsProof = ledgerInfo.recvdConsistencyProofs
        # Consider an f value when this node was not connected
        adjustedQuorum = Quorums(self.owner.totalNodes)
        if len([v for v in recvdConsProof.values() if v is not None]) == \
                adjustedQuorum.f + 1:
            # At least once correct node believes that this node is behind.

            # Start timer that will expire in some time and if till that time
            # enough CPs are not received, then explicitly request CPs
            # from other nodes, see `request_CPs_if_needed`

            ledgerInfo.consistencyProofsTimer = time.perf_counter()
            # TODO: find appropriate moment to unschedule this event!
            self._schedule(
                partial(self.request_CPs_if_needed, ledgerId),
                self.config.ConsistencyProofsTimeout *
                (self.owner.totalNodes - 1))
        if adjustedQuorum.consistency_proof.is_reached(len(recvdConsProof)):
            logger.debug("{} deciding on the basis of CPs {} and f {}".format(
                self, recvdConsProof, adjustedQuorum.f))
            grpdPrf, null_proofs_count = self._groupConsistencyProofs(
                recvdConsProof)
            # If at least n-f-1 nodes were found to be at the same state
            # then this node's state is good too
            if adjustedQuorum.ledger_status.is_reached(null_proofs_count):
                return True, None
            result = self._latestReliableProof(grpdPrf, ledgerInfo.ledger)
            cp = ConsistencyProof(ledgerId, *result) if result else None
            return bool(result), cp

        logger.debug("{} cannot start catchup since received only {} "
                     "consistency proofs but need at least {}".format(
                         self, len(recvdConsProof),
                         adjustedQuorum.consistency_proof.value))
        return False, None
     'p1': 'v1',
     'p2': 'v2'
 },
  LedgerStatus(1, 20, 1, 2, '77wuDUSr4FtAJzJbSqSW7bBw8bKAbra8ABSAjR72Nipq',
               CURRENT_PROTOCOL_VERSION)),
 (LEDGER_STATUS, {
     f.LEDGER_ID.nm: 100
 },
  LedgerStatus(1, 20, 1, 2, '77wuDUSr4FtAJzJbSqSW7bBw8bKAbra8ABSAjR72Nipq',
               CURRENT_PROTOCOL_VERSION)),
 (CONSISTENCY_PROOF, {
     f.LEDGER_ID.nm: 1,
     f.SEQ_NO_START.nm: 10
 },
  ConsistencyProof(1, 2, 20, 1, 3,
                   'BvmagFYpXAYNTuNW8Qssk9tMhEEPucLqL55YuwngUvMw',
                   'Dce684wcwhV2wNZCuYTzdW9Kr13ZXFgiuAuAGibFZc4v',
                   ['58qasGZ9y3TB1pMz7ARKjJeccEbvbx6FT6g3NFnjYsTS'])),
 (PREPREPARE, {
     f.INST_ID.nm: 1,
     f.VIEW_NO.nm: 0,
     f.SEQ_NO_START.nm: 10
 }, pre_prepare_msg),
 (PREPREPARE, {
     f.INST_ID.nm: -1,
     f.VIEW_NO.nm: 1,
     f.PP_SEQ_NO.nm: 10
 }, pre_prepare_msg),
 (PREPARE, {
     f.INST_ID.nm: 1,
     f.VIEW_NO.nm: 0,
     f.SEQ_NO_START.nm: 10
Exemplo n.º 14
0
 def create(self, msg: Dict, **kwargs) -> ConsistencyProof:
     return ConsistencyProof(**msg)