def test_resend_instance_change_messages(looper,
                                         txnPoolNodeSet,
                                         tconf,
                                         sdk_wallet_steward,
                                         sdk_pool_handle):
    primary_node = txnPoolNodeSet[0]
    old_view_no = checkViewNoForNodes(txnPoolNodeSet, 0)
    assert primary_node.master_replica.isPrimary
    for n in txnPoolNodeSet:
        n.nodeIbStasher.delay(icDelay(3 * tconf.INSTANCE_CHANGE_TIMEOUT))
    assert set([n.view_changer.instance_change_rounds for n in txnPoolNodeSet]) == {0}
    disconnect_node_and_ensure_disconnected(looper,
                                            txnPoolNodeSet,
                                            primary_node,
                                            stopNode=False)
    txnPoolNodeSet.remove(primary_node)
    looper.run(eventually(partial(check_count_connected_node, txnPoolNodeSet, 4),
                          timeout=5,
                          acceptableExceptions=[AssertionError]))
    looper.runFor(2*tconf.INSTANCE_CHANGE_TIMEOUT)
    assert set([n.view_changer.instance_change_rounds for n in txnPoolNodeSet]) == {1}

    looper.runFor(tconf.INSTANCE_CHANGE_TIMEOUT)
    looper.run(eventually(partial(checkViewNoForNodes, txnPoolNodeSet, expectedViewNo=old_view_no + 1),
                          timeout=tconf.VIEW_CHANGE_TIMEOUT))
    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle, sdk_wallet_steward, 5)
    ensure_all_nodes_have_same_data(looper, txnPoolNodeSet)
Exemple #2
0
def do_view_change_with_unaligned_prepare_certificates(
        slow_nodes, nodes, looper, sdk_pool_handle, sdk_wallet_client):
    """
    Perform view change with some nodes reaching lower last prepared certificate than others.
    With current implementation of view change this can result with view change taking a lot of time.
    """
    fast_nodes = [n for n in nodes if n not in slow_nodes]

    all_stashers = [n.nodeIbStasher for n in nodes]
    slow_stashers = [n.nodeIbStasher for n in slow_nodes]

    # Delay some PREPAREs and all COMMITs
    with delay_rules(slow_stashers, pDelay()):
        with delay_rules(all_stashers, cDelay()):
            # Send request
            request = sdk_send_random_request(looper, sdk_pool_handle, sdk_wallet_client)

            # Wait until this request is prepared on fast nodes
            looper.run(eventually(check_last_prepared_certificate, fast_nodes, (0, 1)))
            # Make sure its not prepared on slow nodes
            looper.run(eventually(check_last_prepared_certificate, slow_nodes, None))

            # Trigger view change
            for n in nodes:
                n.view_changer.on_master_degradation()

        # Now commits are processed
        # Wait until view change is complete
        looper.run(eventually(check_view_change_done, nodes, 1, timeout=60))

    # Finish request gracefully
    sdk_get_reply(looper, request)
def test_send_proof_works(aliceAgent, aliceAcceptedFaber, aliceAcceptedAcme,
                                    acmeAgent, emptyLooper):
    # 1. request Claims from Faber
    faberLink = aliceAgent.wallet.getLink('Faber College')
    name, version, origin = faberLink.availableClaims[0]
    schemaKey = SchemaKey(name, version, origin)
    aliceAgent.sendReqClaim(faberLink, schemaKey)

    # 2. check that claim is received from Faber
    async def chkClaims():
        claim = await aliceAgent.prover.wallet.getClaimSignature(ID(schemaKey))
        assert claim.primaryClaim

    emptyLooper.run(eventually(chkClaims, timeout=waits.expectedClaimsReceived()))

    # 3. send Proof Request to Alice
    alice_link = acmeAgent.wallet.getLink('Alice')
    acmeAgent.sendProofReq(alice_link, 'Job-Application-v0.3')

    def chkProofRequest():
        assert len(aliceAgent.wallet.getMatchingLinksWithProofReq("Job-Application-2", "Acme Corp")) > 0

    emptyLooper.run(eventually(chkProofRequest, timeout=waits.expectedClaimsReceived()))

    # 4. send proof to Acme
    acme_link, acme_proof_req = aliceAgent.wallet.getMatchingLinksWithProofReq("Job-Application-2", "Acme Corp")[0]
    aliceAgent.sendProof(acme_link, acme_proof_req)

    # 5. check that proof is verified by Acme
    def chkProof():
        internalId = acmeAgent.get_internal_id_by_nonce(acme_link.invitationNonce)
        link = acmeAgent.wallet.getLinkBy(internalId=internalId)
        assert "Job-Application-2" in link.verifiedClaimProofs

    emptyLooper.run(eventually(chkProof, timeout=waits.expectedClaimsReceived()))
def test_request_older_than_stable_checkpoint_removed(chkFreqPatched, looper, txnPoolNodeSet, sdk_pool_handle,
                                                      sdk_wallet_steward, reqs_for_checkpoint):
    timeout = waits.expectedTransactionExecutionTime(len(txnPoolNodeSet))
    max_batch_size = chkFreqPatched.Max3PCBatchSize

    # Send some requests (insufficient for checkpoint),
    # wait replies and check that current checkpoint is not stable
    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle, sdk_wallet_steward, 2 * max_batch_size)
    looper.run(eventually(chkChkpoints, txnPoolNodeSet, 1, retryWait=1, timeout=timeout))
    checkRequestCounts(txnPoolNodeSet, 2 * max_batch_size, 2)

    # From the steward send a request creating a user with None role
    sdk_wallet_user = sdk_add_new_nym(looper, sdk_pool_handle, sdk_wallet_steward)
    looper.run(eventually(chkChkpoints, txnPoolNodeSet, 1, retryWait=1, timeout=timeout))
    checkRequestCounts(txnPoolNodeSet, 2 * max_batch_size + 1, 3)

    # From the created user send a request creating another user.
    # Dynamic validation of this request must fail since a user with None role cannot create users.
    # However, the 3PC-batch with the sent request must be ordered.
    with pytest.raises(RequestRejectedException):
        sdk_add_new_nym(looper, sdk_pool_handle, sdk_wallet_user)
    looper.run(eventually(chkChkpoints, txnPoolNodeSet, 1, retryWait=1, timeout=timeout))
    checkRequestCounts(txnPoolNodeSet, 2 * max_batch_size + 2, 4)

    # Send more requests to cause checkpoint stabilization
    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle, sdk_wallet_steward, max_batch_size)
    # Check that checkpoint is stable now
    # and verify that requests for it were removed
    looper.run(eventually(chkChkpoints, txnPoolNodeSet, 1, 0, retryWait=1, timeout=timeout))
    checkRequestCounts(txnPoolNodeSet, 0, 0)

    # Send more requests to cause new checkpoint
    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle, sdk_wallet_steward, reqs_for_checkpoint + 1)
    looper.run(eventually(chkChkpoints, txnPoolNodeSet, 2, 0, retryWait=1, timeout=timeout))
    checkRequestCounts(txnPoolNodeSet, 1, 1)
def test_zstack_non_utf8(tdir, looper, tconf):
    """
    ZStack gets a non utf-8 message and does not hand it over to the
    processing method
    :return:
    """
    names = ['Alpha', 'Beta']
    genKeys(tdir, names)
    (alpha, beta), (alphaP, betaP) = create_and_prep_stacks(names, tdir,
                                                            looper, tconf)

    # Send a utf-8 message and see its received
    for uid in alpha.remotes:
        alpha.transmit(b'{"k1": "v1"}', uid, serialized=True)
    looper.run(eventually(chkPrinted, betaP, {"k1": "v1"}))

    # Send a non utf-8 message and see its not received (by the receiver method)
    for uid in alpha.remotes:
        alpha.transmit(b'{"k2": "v2\x9c"}', uid, serialized=True)
    with pytest.raises(AssertionError):
        looper.run(eventually(chkPrinted, betaP, {"k2": "v2\x9c"}))
    # TODO: A better test where the output of the parsing method is checked
        # requires spyable methods

    # Again send a utf-8 message and see its received (checks if stack is
    # functional after receiving a bad message)
    for uid in alpha.remotes:
        alpha.transmit(b'{"k3": "v3"}', uid, serialized=True)
    looper.run(eventually(chkPrinted, betaP, {"k3": "v3"}))
def test_propagate_of_ordered_request_doesnt_stash_requests_in_authenticator(
        looper, txnPoolNodeSet, sdk_pool_handle, sdk_wallet_client):

    # Universal delayer
    def stopAll(msg):
        return 100000

    def check_verified_req_list_is_empty():
        for node in txnPoolNodeSet:
            assert len(node.clientAuthNr._verified_reqs) == 0

    # Order one request while cutting off last node
    lastNode = txnPoolNodeSet[-1]
    with delay_rules(lastNode.nodeIbStasher, stopAll), \
         delay_rules(lastNode.clientIbStasher, stopAll):
        sdk_send_random_and_check(looper, txnPoolNodeSet,
                                  sdk_pool_handle,
                                  sdk_wallet_client, 1)
        old_propagates = [n.spylog.count('processPropagate') for n in txnPoolNodeSet]

    def check_more_propagates_delivered():
        new_propagates = [n.spylog.count('processPropagate') for n in txnPoolNodeSet]
        assert all(old < new for old, new in zip(old_propagates, new_propagates))

    # Wait until more propagates are delivered to all nodes
    looper.run(eventually(check_more_propagates_delivered))

    # Make sure that verified req list will be empty eventually
    looper.run(eventually(check_verified_req_list_is_empty))
def testOrderingCase1(looper, txnPoolNodeSet, sdk_wallet_client, sdk_pool_handle):
    """
    Scenario -> PRE-PREPARE not received by the replica, Request not received
    for ordering by the replica, but received enough commits to start ordering.
    It queues up the request so when a PRE-PREPARE is received or request is
    receievd for ordering, an order can be triggered
    https://www.pivotaltracker.com/story/show/125239401

    Reproducing by - Pick a node with no primary replica, replica ignores
    forwarded request to replica and delay reception of PRE-PREPARE sufficiently
    so that enough COMMITs reach to trigger ordering.
    """
    delay = 10
    replica = getNonPrimaryReplicas(txnPoolNodeSet, instId=0)[0]
    delaysPrePrepareProcessing(replica.node, delay=delay, instId=0)

    def doNotProcessReqDigest(self, _):
        pass

    patchedMethod = types.MethodType(doNotProcessReqDigest, replica)
    replica.processRequest = patchedMethod

    def chk(n):
        assert replica.spylog.count(replica.doOrder.__name__) == n

    sdk_send_random_request(looper, sdk_pool_handle, sdk_wallet_client)
    timeout = delay - 5
    looper.run(eventually(chk, 0, retryWait=1, timeout=timeout))
    timeout = delay + 5
    looper.run(eventually(chk, 1, retryWait=1, timeout=timeout))
def testMultipleInstanceChangeMsgsMarkNodeAsSuspicious(looper, txnPoolNodeSet):
    maliciousNode = txnPoolNodeSet[0]
    for i in range(0, 5):
        maliciousNode.send(maliciousNode.view_changer._create_instance_change_msg(i, 0))

    def chk(instId):
        for node in txnPoolNodeSet:
            if node.name != maliciousNode.name:
                args = getAllArgs(node, ViewChanger.process_instance_change_msg)
                assert len(args) == 5
                for arg in args:
                    assert arg['frm'] == maliciousNode.name

    numOfNodes = len(txnPoolNodeSet)
    instanceChangeTimeout = waits.expectedPoolViewChangeStartedTimeout(
        numOfNodes)

    for i in range(0, 5):
        looper.run(eventually(chk, i, retryWait=1,
                              timeout=instanceChangeTimeout))

    def g():
        for node in txnPoolNodeSet:
            if node.name != maliciousNode.name:
                frm, reason, code = getAllArgs(node, Node.reportSuspiciousNode)
                assert frm == maliciousNode.name
                assert isinstance(reason, SuspiciousNode)
                suspectingNodes = \
                    getNodeSuspicions(node,
                                      Suspicions.FREQUENT_INST_CHNG.code)
                assert len(suspectingNodes) == 13

    timeout = waits.expectedTransactionExecutionTime(numOfNodes)
    looper.run(eventually(g, retryWait=1, timeout=timeout))
def test_api():
    loop = asyncio.get_event_loop()
    with pytest.raises(PlenumValueError):
        loop.run_until_complete(eventually(lambda x: True, timeout=0))
    with pytest.raises(PlenumValueError):
        loop.run_until_complete(eventually(lambda x: True, timeout=250))
    loop.close()
def do_view_change_with_delayed_commits_on_all_but_one(nodes, nodes_without_one_stashers,
                                          except_node,
                                          looper,
                                          sdk_pool_handle,
                                          sdk_wallet_client):
    new_view_no = except_node.viewNo + 1
    old_last_ordered = except_node.master_replica.last_ordered_3pc
    # delay commits for all nodes except node X
    with delay_rules(nodes_without_one_stashers, cDelay(sys.maxsize)):
        # send one  request
        requests2 = sdk_send_random_requests(looper, sdk_pool_handle,
                                             sdk_wallet_client, 1)

        def last_ordered(node: Node, last_ordered):
            assert node.master_replica.last_ordered_3pc == last_ordered

        # wait until except_node ordered txn
        looper.run(
            eventually(last_ordered, except_node, (except_node.viewNo,
                                                   old_last_ordered[1] + 1)))

        # trigger view change on all nodes
        for node in nodes:
            node.view_changer.on_master_degradation()

        # wait for view change done on all nodes
        looper.run(eventually(view_change_done, nodes, new_view_no))

    sdk_get_replies(looper, requests2)
def disconnect(looper, disconnected_stack, connection_timeout):
    disconnected_motor, other_stacks = disconnected_stack
    looper.run(eventually(
        checkStackDisonnected, disconnected_motor.stack, other_stacks,
        retryWait=1, timeout=connection_timeout))
    looper.run(eventually(
        checkStacksConnected, other_stacks, retryWait=1, timeout=connection_timeout))
def testValidatorSuspensionByTrustee(trustee, trusteeWallet, looper, nodeSet):
    node = nodeSet[-1]
    nodeNym = hexToFriendly(node.nodestack.verhex)
    suspendNode(looper, trustee, trusteeWallet, nodeNym, node.name)
    for n in nodeSet[:-1]:
        looper.run(eventually(checkNodeNotInNodeReg, n, node.name))
    looper.run(eventually(checkNodeNotInNodeReg, trustee, node.name))
def nymsAddedInQuickSuccession(nodeSet, addedTrustAnchor, looper,
                               trustAnchor, trustAnchorWallet):
    usigner = DidSigner()
    nym = usigner.verkey
    idy = Identity(identifier=nym)
    trustAnchorWallet.addTrustAnchoredIdentity(idy)
    # Creating a NYM request with same nym again
    req = idy.ledgerRequest()
    trustAnchorWallet._pending.appendleft((req, idy.identifier))
    reqs = trustAnchorWallet.preparePending()
    trustAnchor.submitReqs(*reqs)

    def check():
        assert trustAnchorWallet._trustAnchored[nym].seqNo

    timeout = waits.expectedTransactionExecutionTime(len(nodeSet))
    looper.run(eventually(check, timeout=timeout))

    timeout = waits.expectedReqNAckQuorumTime()
    looper.run(eventually(checkNacks,
                          trustAnchor,
                          req.reqId,
                          "is already added",
                          retryWait=1, timeout=timeout))
    count = 0
    for node in nodeSet:
        for seq, txn in node.domainLedger.getAllTxn():
            if txn[TXN_TYPE] == NYM and txn[TARGET_NYM] == usigner.identifier:
                count += 1

    assert(count == len(nodeSet))
def testPropagateRecvdBeforeRequest(setup, looper, txnPoolNodeSet, sent1):
    A, B, C, D = txnPoolNodeSet

    def x():
        # A should not have received a request from the client
        assert len(recvdRequest(A)) == 0
        # A should have received only one PROPAGATE
        assert len(recvdPropagate(A)) == 1
        # A should have sent only one PROPAGATE
        assert len(sentPropagate(A)) == 1

    timeout = waits.expectedNodeToNodeMessageDeliveryTime() + delaySec - 2
    looper.run(eventually(x, retryWait=.5, timeout=timeout))

    def y():
        # A should have received a request from the client
        assert len(recvdRequest(A)) == 1
        # A should still have sent only one PROPAGATE
        assert len(sentPropagate(A)) == 1

    timeout = waits.expectedNodeToNodeMessageDeliveryTime() + delaySec + 2
    looper.run(eventually(y, retryWait=.5, timeout=timeout))

    def chk():
        # A should have forwarded the request
        assertLength(forwardedRequest(A), 1)

    timeout = waits.expectedClientRequestPropagationTime(
        len(txnPoolNodeSet)) + delaySec
    looper.run(eventually(chk, retryWait=1, timeout=timeout))
    auth_obj = A.authNr(0).core_authenticator
    auth_calling_count = get_count(auth_obj, auth_obj.authenticate)
    assert auth_calling_count == reqCount
def testRescheduleUpgradeToLowerVersionThanPreviouslyScheduled(
        looper, tconf, nodeSet, validUpgrade, trustee, trusteeWallet):
    """
    A node starts at version 1.2 running has scheduled upgrade for version 1.5
    but get a txn for upgrade 1.4, it will schedule it and cancel upgrade to 1.5.
    """
    upgr1 = deepcopy(validUpgrade)

    upgr2 = deepcopy(upgr1)
    upgr2[VERSION] = bumpVersion(upgr1[VERSION])
    upgr2[NAME] += randomString(3)
    # upgr2[SHA256] = get_valid_code_hash()
    upgr2[SHA256] = 'ef9c3984e7a31994d4f692139116120bd0dd1ff7e270b6a2d773f8f2f9214d4c'

    # An upgrade for higher version scheduled, it should pass
    ensureUpgradeSent(looper, trustee, trusteeWallet, upgr2)
    looper.run(
        eventually(
            checkUpgradeScheduled,
            nodeSet,
            upgr2[VERSION],
            retryWait=1,
            timeout=waits.expectedUpgradeScheduled()))

    # An upgrade for lower version scheduled, the transaction should pass and
    # the upgrade should be scheduled
    ensureUpgradeSent(looper, trustee, trusteeWallet, upgr1)
    looper.run(
        eventually(
            checkUpgradeScheduled,
            nodeSet,
            upgr1[VERSION],
            retryWait=1,
            timeout=waits.expectedUpgradeScheduled()))
def testPrePrepareWithHighSeqNo(looper, txnPoolNodeSet, propagated1):
    def chk():
        for r in getNonPrimaryReplicas(txnPoolNodeSet, instId):
            nodeSuspicions = len(getNodeSuspicions(
                r.node, Suspicions.WRONG_PPSEQ_NO.code))
            assert nodeSuspicions == 1

    def checkPreprepare(replica, viewNo, ppSeqNo, req, numOfPrePrepares):
        assert (replica.prePrepares[viewNo, ppSeqNo][0]) == \
               (req.identifier, req.reqId, req.digest)

    primary = getPrimaryReplica(txnPoolNodeSet, instId)
    nonPrimaryReplicas = getNonPrimaryReplicas(txnPoolNodeSet, instId)
    req = propagated1.reqDigest
    primary.doPrePrepare(req)
    timeout = waits.expectedPrePrepareTime(len(txnPoolNodeSet))
    for np in nonPrimaryReplicas:
        looper.run(
            eventually(checkPreprepare, np, primary.viewNo,
                       primary.lastPrePrepareSeqNo - 1, req, 1,
                       retryWait=.5, timeout=timeout))

    newReqDigest = (req.identifier, req.reqId + 1, req.digest)
    incorrectPrePrepareReq = PrePrepare(instId,
                                        primary.viewNo,
                                        primary.lastPrePrepareSeqNo + 2,
                                        *newReqDigest,
                                        get_utc_epoch())
    primary.send(incorrectPrePrepareReq, TPCStat.PrePrepareSent)

    timeout = waits.expectedPrePrepareTime(len(txnPoolNodeSet))
    looper.run(eventually(chk, retryWait=1, timeout=timeout))
def testPropagateRecvdAfterRequest(setup, looper, txnPoolNodeSet):
    A, B, C, D = txnPoolNodeSet  # type: TestNode
    sent1 = sdk_json_to_request_object(setup[0][0])

    def x():
        # A should have received a request from the client
        assert len(recvdRequest(A)) == 1
        # A should not have received a PROPAGATE
        assert len(recvdPropagate(A)) == 0
        # A should have sent a PROPAGATE
        assert len(sentPropagate(A)) == 1

    timeout = howlong - 2
    looper.run(eventually(x, retryWait=.5, timeout=timeout))
    for n in txnPoolNodeSet:
        n.nodeIbStasher.resetDelays()

    def y():
        # A should have received 3 PROPAGATEs
        assert len(recvdPropagate(A)) == 3
        # A should have total of 4 PROPAGATEs (3 from other nodes and 1 from
        # itself)
        key = sent1.digest
        assert key in A.requests
        assert len(A.requests[key].propagates) == 4
        # A should still have sent only one PROPAGATE
        assert len(sentPropagate(A)) == 1

    timeout = howlong + 2
    looper.run(eventually(y, retryWait=.5, timeout=timeout))
    auth_obj = A.authNr(0).core_authenticator
    auth_calling_count = get_count(auth_obj, auth_obj.authenticate)
    assert auth_calling_count == reqCount
def test_req_drop_on_propagate_phase_on_non_primary_and_then_ordered(
        tconf, setup, looper, txnPoolNodeSet,
        sdk_wallet_client, sdk_pool_handle):
    global initial_ledger_size
    A, B, C, D = txnPoolNodeSet  # type: TestNode
    sent1 = sdk_json_to_request_object(setup[0][0])
    lagged_node = C

    def check_propagates_and_3pc_delayed():
        # Node should have received a request from the client
        assert len(recvdRequest(lagged_node)) == 1
        # Node should not have received a PROPAGATE
        assert len(recvdPropagate(lagged_node)) == 0
        # Node should have sent a PROPAGATE
        assert len(sentPropagate(lagged_node)) == 1
        # Node should have not received PrePrepares for master instance
        assert len(recvdPrePrepareForInstId(lagged_node, 0)) == 0
        # Node should have not received Prepares for master instance
        assert len(recvdPrepareForInstId(lagged_node, 0)) == 0
        # Node should have not received Commits for master instance
        assert len(recvdCommitForInstId(lagged_node, 0)) == 0
        # Node should have 1 request in requests queue
        assert len(lagged_node.requests) == 1

    timeout = howlong - 2
    looper.run(eventually(check_propagates_and_3pc_delayed, retryWait=.5, timeout=timeout))

    def check_drop():
        assert len(lagged_node.requests) == 0

    timeout = tconf.PROPAGATES_PHASE_REQ_TIMEOUT + tconf.OUTDATED_REQS_CHECK_INTERVAL + 1
    looper.run(eventually(check_drop, retryWait=.5, timeout=timeout))

    for n in txnPoolNodeSet:
        n.nodeIbStasher.resetDelays()

    def check_propagates_received():
        # Node should have received 3 PROPAGATEs
        assert len(recvdPropagate(lagged_node)) == 3
        # Node should have total of 4 PROPAGATEs (3 from other nodes and 1 from
        # itself)
        key = sent1.digest
        assert key in lagged_node.requests
        assert len(lagged_node.requests[key].propagates) == 4
        # Node should still have sent two PROPAGATEs since request
        # was dropped and re-received over propagate
        assert len(sentPropagate(lagged_node)) == 2

    timeout = howlong + 2
    looper.run(eventually(check_propagates_received, retryWait=.5, timeout=timeout))

    def check_ledger_size():
        # The request should be eventually ordered
        for n in txnPoolNodeSet:
            assert n.domainLedger.size - initial_ledger_size == 1

    looper.run(eventually(check_ledger_size, retryWait=.5, timeout=timeout))

    sdk_ensure_pool_functional(looper, txnPoolNodeSet, sdk_wallet_client, sdk_pool_handle)
def test_all_replicas_hold_request_keys(
        perf_chk_patched,
        looper,
        txnPoolNodeSet,
        sdk_wallet_client,
        sdk_pool_handle):
    """
    All replicas whether primary or non primary hold request keys of forwarded
    requests. Once requests are ordered, they request keys are removed from replica.
    """
    tconf = perf_chk_patched
    delay_3pc = 2
    delay_3pc_messages(txnPoolNodeSet, 0, delay_3pc)
    delay_3pc_messages(txnPoolNodeSet, 1, delay_3pc)

    def chk(count):
        # All replicas have same amount of forwarded request keys and all keys
        # are finalised.
        for node in txnPoolNodeSet:
            for r in node.replicas.values():
                if r.isPrimary is False:
                    assert len(r.requestQueues[DOMAIN_LEDGER_ID]) == count
                    for i in range(count):
                        k = r.requestQueues[DOMAIN_LEDGER_ID][i]
                        assert r.requests[k].finalised
                elif r.isPrimary is True:
                    assert len(r.requestQueues[DOMAIN_LEDGER_ID]) == 0

    reqs = sdk_signed_random_requests(looper,
                                      sdk_wallet_client,
                                      tconf.Max3PCBatchSize - 1)
    req_resps = sdk_send_signed_requests(sdk_pool_handle, reqs)
    # Only non primary replicas should have all request keys with them
    looper.run(eventually(chk, tconf.Max3PCBatchSize - 1))
    sdk_get_replies(looper, req_resps, timeout=sdk_eval_timeout(
        tconf.Max3PCBatchSize - 1, len(txnPoolNodeSet),
        add_delay_to_timeout=delay_3pc))
    # Replicas should have no request keys with them since they are ordered
    looper.run(eventually(chk, 0))  # Need to wait since one node might not
    # have processed it.

    delay = 1
    for node in txnPoolNodeSet:
        node.nodeIbStasher.delay(nom_delay(delay))

    ensure_view_change(looper, txnPoolNodeSet)
    reqs = sdk_signed_random_requests(looper,
                                      sdk_wallet_client,
                                      2 * tconf.Max3PCBatchSize)
    req_resps = sdk_send_signed_requests(sdk_pool_handle, reqs)
    looper.run(eventually(chk, 2 * tconf.Max3PCBatchSize))

    # Since each nomination is delayed and there will be multiple nominations
    # so adding some extra time
    timeout = waits.expectedPoolElectionTimeout(len(txnPoolNodeSet)) + \
              len(txnPoolNodeSet) * delay
    ensureElectionsDone(looper, txnPoolNodeSet, customTimeout=timeout)
    sdk_get_replies(looper, req_resps, timeout=timeout)
    looper.run(eventually(chk, 0))
def test_no_propagate_request_on_different_prepares_on_backup_before_vc(looper, txnPoolNodeSet,
                                                  sdk_pool_handle, sdk_wallet_client):
    ''' Send random request and do view change then fast_nodes (2,3 - with
    primary backup replica) will have prepare or send preprepare on backup
    replicas and slow_nodes are have not and transaction will ordered on all
    master replicas. Check last ordered after view change and after another
    one request.'''
    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                              sdk_wallet_client, 1)
    slow_instance = 1
    slow_nodes = txnPoolNodeSet[1:3]
    fast_nodes = [n for n in txnPoolNodeSet if n not in slow_nodes]
    nodes_stashers = [n.nodeIbStasher for n in slow_nodes]
    old_last_ordered = txnPoolNodeSet[0].master_replica.last_ordered_3pc
    with delay_rules(nodes_stashers, pDelay(instId=slow_instance)):
        with delay_rules(nodes_stashers, ppDelay(instId=slow_instance)):
            # send one request
            sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                                      sdk_wallet_client, 1)
            old_view_no = txnPoolNodeSet[0].viewNo
            looper.run(
                eventually(is_prepared,
                           fast_nodes,
                           2,
                           slow_instance))

            # trigger view change on all nodes
            ensure_view_change(looper, txnPoolNodeSet)
            # wait for view change done on all nodes
            ensureElectionsDone(looper, txnPoolNodeSet)

    primary = getPrimaryReplica(txnPoolNodeSet, slow_instance).node
    non_primaries = [n for n in txnPoolNodeSet if n is not primary]

    check_last_ordered(non_primaries,
                       slow_instance,
                       (old_view_no, old_last_ordered[1] + 1))

    # Backup primary replica must not advance last_ordered_3pc
    # up to the master's value
    check_last_ordered([primary],
                       slow_instance,
                       (old_view_no, old_last_ordered[1]))

    check_last_ordered(txnPoolNodeSet,
                       txnPoolNodeSet[0].master_replica.instId,
                       (old_last_ordered[0], old_last_ordered[1] + 1))

    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                              sdk_wallet_client, 1)
    looper.run(
        eventually(check_last_ordered,
                   txnPoolNodeSet,
                   slow_instance,
                   (txnPoolNodeSet[0].viewNo, 1)))
    assert all(0 == node.spylog.count(node.request_propagates)
               for node in txnPoolNodeSet)
def test_claim_from_libindy_works(
        aliceAgent,
        aliceAcceptedFaber,
        aliceAcceptedAcme,
        acmeAgent,
        emptyLooper,
        faberAgent):
    faberLink = aliceAgent.wallet.getConnection('Faber College')
    name, version, origin = faberLink.availableClaims[0]
    schemaKey = SchemaKey(name, version, origin)
    timeout = waits.expectedClaimsReceived()

    schema = faberAgent.issuer.wallet._schemasByKey[schemaKey]

    async def create_claim_and_send_to_prover():
        claimReq = await aliceAgent.prover.createClaimRequest(
            schemaId=ID(schemaKey),
            proverId='b1134a647eb818069c089e7694f63e6d',
            reqNonRevoc=False)

        assert claimReq

        attr = faberAgent.issuer_backend.get_record_by_internal_id(1)
        faberAgent.issuer._attrRepo.addAttributes(schemaKey=schemaKey,
                                                  userId=claimReq.userId,
                                                  attributes=attr)
        claim_signature, claim_attributes = await faberAgent.issuer.issueClaim(ID(schemaKey=schemaKey), claimReq)

        msg = get_claim_libindy_msg(claim_signature, schema.seqId)

        await aliceAgent.handleReqClaimResponse(msg)

    emptyLooper.run(eventually(
        create_claim_and_send_to_prover, timeout=timeout))

    # 2. check that claim is received from Faber
    async def chkClaims():
        claim = await aliceAgent.prover.wallet.getClaimSignature(ID(schemaKey))
        assert claim.primaryClaim

    emptyLooper.run(eventually(chkClaims, timeout=timeout))

    # 3. send proof to Acme
    acme_link, acme_proof_req = aliceAgent.wallet.getMatchingConnectionsWithProofReq(
        "Job-Application", "Acme Corp")[0]
    aliceAgent.sendProof(acme_link, acme_proof_req)

    # 4. check that proof is verified by Acme
    def chkProof():
        internalId = acmeAgent.get_internal_id_by_nonce(
            acme_link.request_nonce)
        link = acmeAgent.wallet.getConnectionBy(internalId=internalId)
        assert "Job-Application" in link.verifiedClaimProofs

    timeout = waits.expectedClaimsReceived()
    emptyLooper.run(eventually(chkProof, timeout=timeout))
def test_claim_request_from_libsovrin_works(aliceAgent, aliceAcceptedFaber, aliceAcceptedAcme,
                  acmeAgent, emptyLooper):
    faberLink = aliceAgent.wallet.getLink('Faber College')
    name, version, origin = faberLink.availableClaims[0]
    schemaKey = SchemaKey(name, version, origin)
    timeout = waits.expectedClaimsReceived()

    async def create_claim_init_data_and_send_msg():
        claimReq = await aliceAgent.prover.createClaimRequest(
            schemaId=ID(schemaKey),
            proverId='b1134a647eb818069c089e7694f63e6d',
            reqNonRevoc=False)

        assert claimReq

        msg = (
            {
                'issuer_did': 'FuN98eH2eZybECWkofW6A9BKJxxnTatBCopfUiNxo6ZB',
                'claim_def_seq_no': 14,
                'blinded_ms': {
                    'prover_did': 'b1134a647eb818069c089e7694f63e6d',
                    'u': str(crypto_int_to_str(claimReq.U)),
                    'ur': None
                },
                'type': 'CLAIM_REQUEST',
                'schema_seq_no': 13,
                'nonce': 'b1134a647eb818069c089e7694f63e6d',
            }
        )

        aliceAgent.signAndSendToLink(msg=msg, linkName=faberLink.name)

    emptyLooper.run(eventually(create_claim_init_data_and_send_msg, timeout=timeout))

    # 2. check that claim is received from Faber
    async def chkClaims():
        claim = await aliceAgent.prover.wallet.getClaims(ID(schemaKey))
        assert claim.primaryClaim

    emptyLooper.run(eventually(chkClaims, timeout=timeout))

    # 3. send proof to Acme
    acme_link, acme_proof_req = aliceAgent.wallet.getMatchingLinksWithProofReq(
        "Job-Application", "Acme Corp")[0]
    aliceAgent.sendProof(acme_link, acme_proof_req)

    # 4. check that proof is verified by Acme
    def chkProof():
        internalId = acmeAgent.get_internal_id_by_nonce(acme_link.invitationNonce)
        link = acmeAgent.wallet.getLinkBy(internalId=internalId)
        assert "Job-Application" in link.verifiedClaimProofs

    timeout = waits.expectedClaimsReceived()
    emptyLooper.run(eventually(chkProof, timeout=timeout))
def test_view_change_done_delayed(txnPoolNodeSet, looper, sdk_pool_handle, sdk_wallet_client):
    """
    A node is slow so is behind other nodes, after view change, it catches up
    but it also gets view change message as delayed, a node should start
    participating only when caught up and ViewChangeCone quorum received.
    """
    nprs = [r.node for r in getNonPrimaryReplicas(txnPoolNodeSet, 0)]
    slow_node = nprs[-1]
    other_nodes = [n for n in txnPoolNodeSet if n != slow_node]
    delay_3pc = 10
    delay_vcd = 25
    delay_3pc_messages([slow_node], 0, delay_3pc)
    slow_node.nodeIbStasher.delay(vcd_delay(delay_vcd))

    def chk(node):
        assert node.view_changer.has_acceptable_view_change_quorum
        assert node.view_changer._primary_verified
        assert node.isParticipating
        assert None not in {r.isPrimary for r in node.replicas.values()}

    sdk_send_batches_of_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                                         sdk_wallet_client, 5 * 4, 4)

    ensure_view_change(looper, nodes=txnPoolNodeSet)

    # After view change, the slow node successfully completes catchup
    waitNodeDataEquality(looper, slow_node, *other_nodes)

    # Other nodes complete view change, select primary and participate
    for node in other_nodes:
        looper.run(eventually(chk, node, retryWait=1))

    # Since `ViewChangeCone` is delayed, slow_node is not able to select primary
    # and participate
    assert not slow_node.view_changer.has_acceptable_view_change_quorum
    assert not slow_node.view_changer._primary_verified
    assert not slow_node.isParticipating
    assert {r.isPrimary for r in slow_node.replicas.values()} == {None}

    # Send requests to make sure pool is functional
    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle, sdk_wallet_client, 5)

    # Repair network
    slow_node.reset_delays_and_process_delayeds()

    # `slow_node` selects primary and participate
    looper.run(eventually(chk, slow_node, retryWait=1))

    # Processes requests received during lack of primary
    waitNodeDataEquality(looper, slow_node, *other_nodes)

    # Send more requests and compare data of all nodes
    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle, sdk_wallet_client, 5)
    ensure_all_nodes_have_same_data(looper, txnPoolNodeSet)
def test_req_drop_on_commit_phase_on_master_primary_and_then_ordered(
        tconf, setup, looper, txnPoolNodeSet,
        sdk_wallet_client, sdk_pool_handle):
    global initial_ledger_size
    A, B, C, D = txnPoolNodeSet  # type: TestNode
    lagged_node = A

    def check_propagates():
        # Node should have received a request from the client
        assert len(recvdRequest(lagged_node)) == 1
        # Node should have received a PROPAGATEs
        assert len(recvdPropagate(lagged_node)) == 3
        # Node should have sent a PROPAGATE
        assert len(sentPropagate(lagged_node)) == 1
        # Node should have one request in the requests queue
        assert len(lagged_node.requests) == 1

    timeout = howlong - 2
    looper.run(eventually(check_propagates, retryWait=.5, timeout=timeout))

    def check_prepares_received():
        # Node should have received all Prepares for master instance
        assert len(recvdPrepareForInstId(lagged_node, 0)) == 3
        assert len(lagged_node.requests) == 1

    looper.run(eventually(check_prepares_received, retryWait=.5, timeout=timeout))

    def check_drop():
        # Node should have not received Commits for master instance
        assert len(recvdCommitForInstId(lagged_node, 0)) == 0
        # Request object should be dropped by timeout
        assert len(lagged_node.requests) == 0

    timeout = tconf.ORDERING_PHASE_REQ_TIMEOUT + tconf.OUTDATED_REQS_CHECK_INTERVAL + 1
    looper.run(eventually(check_drop, retryWait=.5, timeout=timeout))

    for n in txnPoolNodeSet:
        n.nodeIbStasher.resetDelays()

    def check_commits_received():
        # Node should have received all delayed Commits for master instance
        assert len(recvdCommitForInstId(lagged_node, 0)) == 3

    timeout = howlong * 2
    looper.run(eventually(check_commits_received, retryWait=.5, timeout=timeout))

    def check_ledger_size():
        # The request should be eventually ordered
        for n in txnPoolNodeSet:
            assert n.domainLedger.size - initial_ledger_size == 1

    looper.run(eventually(check_ledger_size, retryWait=.5, timeout=timeout))

    sdk_ensure_pool_functional(looper, txnPoolNodeSet, sdk_wallet_client, sdk_pool_handle)
Exemple #25
0
def checkIdentityRequestFailed(looper, client, req, cause):
    timeout = waits.expectedReqRejectQuorumTime()
    # TODO: Just for now, better to have a generic negative response checker
    try:
        looper.run(eventually(checkRejects,
                              client,
                              req.reqId,
                              cause, retryWait=1, timeout=timeout))
    except AssertionError:
        looper.run(eventually(checkNacks,
                              client,
                              req.reqId,
                              cause, retryWait=1, timeout=timeout))
def test_proof_from_libindy_works(
        aliceAgent,
        aliceAcceptedFaber,
        aliceAcceptedAcme,
        acmeAgent,
        emptyLooper,
        faberAgent):
    # 1. request Claims from Faber
    faberLink = aliceAgent.wallet.getConnection('Faber College')
    name, version, origin = faberLink.availableClaims[0]
    schemaKey = SchemaKey(name, version, origin)
    aliceAgent.sendReqClaim(faberLink, schemaKey)

    schema = faberAgent.issuer.wallet._schemasByKey[schemaKey]

    # 2. check that claim is received from Faber
    async def chkClaims():
        claim = await aliceAgent.prover.wallet.getClaimSignature(ID(schemaKey))
        assert claim.primaryClaim

    timeout = waits.expectedClaimsReceived()
    emptyLooper.run(eventually(chkClaims, timeout=timeout))

    # 3. send proof to Acme
    acme_link, acme_proof_req = aliceAgent.wallet.getMatchingConnectionsWithProofReq(
        "Job-Application", "Acme Corp")[0]

    async def create_proof():
        proofRequest = ProofRequest("proof1",
                                    "1.0",
                                    int(acme_proof_req.nonce),
                                    verifiableAttributes=acme_proof_req.verifiableAttributes,
                                    predicates=acme_proof_req.predicates)

        proof = await  aliceAgent.prover.presentProof(proofRequest)

        msg = get_proof_libindy_msg(
            acme_link, acme_proof_req, proof, str(schema.seqId), schema.seqId)

        aliceAgent.signAndSendToLink(msg=msg, linkName=acme_link.name)

    emptyLooper.run(eventually(create_proof, timeout=timeout))

    # 4. check that proof is verified by Acme
    def chkProof():
        internalId = acmeAgent.get_internal_id_by_nonce(
            acme_link.request_nonce)
        link = acmeAgent.wallet.getConnectionBy(internalId=internalId)
        assert "Job-Application" in link.verifiedClaimProofs

    emptyLooper.run(eventually(chkProof, timeout=timeout))
def testOrderingWhenPrePrepareNotReceived(looper, txnPoolNodeSet,
                                          sdk_wallet_client, sdk_pool_handle):
    """
    Send commits but delay pre-prepare and prepares such that enough
    commits are received, now the request should not be ordered until
    pre-prepare is received and ordering should just happen once,
    """
    delay = 10
    non_prim_reps = getNonPrimaryReplicas(txnPoolNodeSet, 0)

    slow_rep = non_prim_reps[0]
    slow_node = slow_rep.node
    slow_node.nodeIbStasher.delay(ppDelay(delay, 0))
    slow_node.nodeIbStasher.delay(pDelay(delay, 0))

    stash_pp = []
    stash_p = []
    orig_pp_method = slow_rep.processPrePrepare
    orig_p_method = slow_rep.processPrepare

    def patched_pp(self, msg, sender):
        stash_pp.append((msg, sender))

    def patched_p(self, msg, sender):
        stash_p.append((msg, sender))

    slow_rep.processPrePrepare = \
        types.MethodType(patched_pp, slow_rep)
    slow_rep.processPrepare = \
        types.MethodType(patched_p, slow_rep)

    def chk1():
        assert len(slow_rep.commitsWaitingForPrepare) > 0

    sdk_send_random_request(looper, sdk_pool_handle, sdk_wallet_client)
    timeout = waits.expectedPrePrepareTime(len(txnPoolNodeSet)) + delay
    looper.run(eventually(chk1, retryWait=1, timeout=timeout))

    for m, s in stash_pp:
        orig_pp_method(m, s)

    for m, s in stash_p:
        orig_p_method(m, s)

    def chk2():
        assert len(slow_rep.commitsWaitingForPrepare) == 0
        assert slow_rep.spylog.count(slow_rep.doOrder.__name__) == 1

    timeout = waits.expectedOrderingTime(len(non_prim_reps) + 1) + 2 * delay
    looper.run(eventually(chk2, retryWait=1, timeout=timeout))
def test_claim_request_from_libindy_works(
        aliceAgent,
        aliceAcceptedFaber,
        aliceAcceptedAcme,
        acmeAgent,
        emptyLooper,
        faberAgent):
    faberLink = aliceAgent.wallet.getConnection('Faber College')
    name, version, origin = faberLink.availableClaims[0]
    schemaKey = SchemaKey(name, version, origin)
    timeout = waits.expectedClaimsReceived()

    schema = faberAgent.issuer.wallet._schemasByKey[schemaKey]

    async def create_claim_init_data_and_send_msg():
        claimReq = await aliceAgent.prover.createClaimRequest(
            schemaId=ID(schemaKey),
            proverId='b1134a647eb818069c089e7694f63e6d',
            reqNonRevoc=False)

        assert claimReq

        msg = get_claim_request_libindy_msg(claimReq, schema.seqId)

        aliceAgent.signAndSendToLink(msg=msg, linkName=faberLink.name)

    emptyLooper.run(eventually(
        create_claim_init_data_and_send_msg, timeout=timeout))

    # 2. check that claim is received from Faber
    async def chkClaims():
        claim = await aliceAgent.prover.wallet.getClaimSignature(ID(schemaKey))
        assert claim.primaryClaim

    emptyLooper.run(eventually(chkClaims, timeout=timeout))

    # 3. send proof to Acme
    acme_link, acme_proof_req = aliceAgent.wallet.getMatchingConnectionsWithProofReq(
        "Job-Application", "Acme Corp")[0]
    aliceAgent.sendProof(acme_link, acme_proof_req)

    # 4. check that proof is verified by Acme
    def chkProof():
        internalId = acmeAgent.get_internal_id_by_nonce(
            acme_link.request_nonce)
        link = acmeAgent.wallet.getConnectionBy(internalId=internalId)
        assert "Job-Application" in link.verifiedClaimProofs

    timeout = waits.expectedClaimsReceived()
    emptyLooper.run(eventually(chkProof, timeout=timeout))
def some_requests(txn_pool_node_set_post_creation, looper,
                  sdk_wallet_steward, sdk_pool_handle):
    def check_auctions_amount(expected_amount):
        assert auctions['pqr'][did] == expected_amount

    old_bls_store_size = None
    for node in txn_pool_node_set_post_creation:
        if old_bls_store_size is None:
            old_bls_store_size = node.bls_bft.bls_store._kvs.size
        else:
            assert node.bls_bft.bls_store._kvs.size == old_bls_store_size

    op = {
        TXN_TYPE: AUCTION_START,
        DATA: {'id': 'pqr'}
    }
    successful_op(looper, op, sdk_wallet_steward, sdk_pool_handle)

    op = {
        TXN_TYPE: PLACE_BID,
        DATA: {'id': 'pqr', AMOUNT: 20}
    }
    successful_op(looper, op, sdk_wallet_steward, sdk_pool_handle)

    _, did = sdk_wallet_steward
    for node in txn_pool_node_set_post_creation:
        auctions = node.get_req_handler(AUCTION_LEDGER_ID).auctions
        assert 'pqr' in auctions
        looper.run(eventually(check_auctions_amount, 20))

    op = {
        TXN_TYPE: PLACE_BID,
        DATA: {'id': 'pqr', AMOUNT: 40}
    }
    successful_op(looper, op, sdk_wallet_steward, sdk_pool_handle)

    for node in txn_pool_node_set_post_creation:
        auctions = node.get_req_handler(AUCTION_LEDGER_ID).auctions
        assert 'pqr' in auctions
        looper.run(eventually(check_auctions_amount, 40))

    op = {
        TXN_TYPE: AUCTION_END,
        DATA: {'id': 'pqr'}
    }
    successful_op(looper, op, sdk_wallet_steward, sdk_pool_handle)
    for node in txn_pool_node_set_post_creation:
        # Not all batches might have BLS-sig but at least one of them will have
        assert node.bls_bft.bls_store._kvs.size > old_bls_store_size
def testKitZStacksCommunication(registry, tdir, looper):
    # TODO: Use a packet capture tool
    genKeys(tdir, registry.keys())
    stacks = []
    names = []
    for name, ha in registry.items():
        printer = Printer(name)
        stackParams = dict(name=name, ha=ha, basedirpath=tdir,
                           auth_mode=AuthMode.RESTRICTED.value)
        reg = copy(registry)
        reg.pop(name)
        stack = KITZStack(stackParams, printer.print, reg)
        stacks.append(stack)
        names.append((name, printer))

    prepStacks(looper, *stacks, connect=False, useKeys=True)
    # TODO: the connection may not be established for the first try because
    # some of the stacks may not have had a remote yet (that is they haven't had yet called connect)
    timeout = 4 * KITZStack.RETRY_TIMEOUT_RESTRICTED + 1
    looper.run(eventually(
        checkStacksConnected, stacks, retryWait=1, timeout=timeout))

    def send_recv():
        for i, j in combinations(range(len(stacks)), 2):
            alpha = stacks[i]
            beta = stacks[j]

            alpha.send({'greetings': 'hi'}, beta.name)
            beta.send({'greetings': 'hello'}, alpha.name)

            looper.run(
                eventually(chkPrinted, names[i][1], {'greetings': 'hello'}, timeout=15))
            looper.run(eventually(chkPrinted, names[j][1], {
                       'greetings': 'hi'}, timeout=15))

            names[i][1].reset()
            names[j][1].reset()

    def pr():
        for stack in stacks:
            for r in stack.remotes.values():
                print(r._lastSocketEvents())

    for _ in range(100):
        # send_recv()
        looper.run(eventually(
            checkStacksConnected, stacks, retryWait=1, timeout=timeout))
        # pr()
        looper.runFor(30)
Exemple #31
0
def upgradeScheduledExpForceTrue(validUpgradeSentExpForceTrue, looper, nodeSet,
                                 validUpgradeExpForceTrue):
    looper.run(eventually(checkUpgradeScheduled, nodeSet,
                          validUpgradeExpForceTrue[VERSION], retryWait=1,
                          timeout=waits.expectedUpgradeScheduled()))
Exemple #32
0
def test_backup_primary_restores_pp_seq_no_if_view_is_same(
        looper, txnPoolNodeSet, sdk_pool_handle, sdk_wallet_client, tconf,
        tdir, allPluginsPath, chkFreqPatched, view_no):
    # Get a node with a backup primary replica
    replica = getPrimaryReplica(txnPoolNodeSet, instId=backup_inst_id)
    node = replica.node

    # Send some 3PC-batches and wait until the replica orders the 3PC-batches
    sdk_send_batches_of_random(looper,
                               txnPoolNodeSet,
                               sdk_pool_handle,
                               sdk_wallet_client,
                               num_reqs=7,
                               num_batches=7,
                               timeout=tconf.Max3PCBatchWait)

    seq_no = 7 if view_no == 0 else 8

    looper.run(
        eventually(lambda: assertExp(replica.last_ordered_3pc ==
                                     (view_no, seq_no)),
                   retryWait=1,
                   timeout=waits.expectedTransactionExecutionTime(nodeCount)))

    # Check view no of the node and lastPrePrepareSeqNo of the replica
    assert node.viewNo == view_no
    assert replica.lastPrePrepareSeqNo == seq_no

    # Ensure that the node has stored the last sent PrePrepare key
    assert LAST_SENT_PRE_PREPARE in node.nodeStatusDB
    last_sent_pre_prepare_key = \
        node_status_db_serializer.deserialize(
            node.nodeStatusDB.get(LAST_SENT_PRE_PREPARE))
    assert last_sent_pre_prepare_key == {
        str(backup_inst_id): [view_no, seq_no]
    }

    # Restart the node containing the replica
    disconnect_node_and_ensure_disconnected(looper,
                                            txnPoolNodeSet,
                                            node.name,
                                            stopNode=True)
    looper.removeProdable(node)
    txnPoolNodeSet.remove(node)

    node = start_stopped_node(node, looper, tconf, tdir, allPluginsPath)
    txnPoolNodeSet.append(node)

    looper.run(checkNodesConnected(txnPoolNodeSet))
    ensureElectionsDone(looper, txnPoolNodeSet)

    replica = node.replicas[backup_inst_id]

    # Verify that after the successful propagate primary procedure the replica
    # (which must still be the primary in its instance) has restored
    # lastPrePrepareSeqNo and adjusted last_ordered_3pc and shifted
    # the watermarks correspondingly
    assert node.viewNo == view_no
    assert replica.isPrimary
    assert replica.lastPrePrepareSeqNo == seq_no
    assert replica.last_ordered_3pc == (view_no, seq_no)
    assert replica.h == seq_no
    assert replica.H == seq_no + LOG_SIZE

    # Verify also that the stored last sent PrePrepare key has not been erased
    assert LAST_SENT_PRE_PREPARE in node.nodeStatusDB

    # Send a 3PC-batch and ensure that the replica orders it
    sdk_send_batches_of_random(looper,
                               txnPoolNodeSet,
                               sdk_pool_handle,
                               sdk_wallet_client,
                               num_reqs=1,
                               num_batches=1,
                               timeout=tconf.Max3PCBatchWait)

    seq_no = 8 if view_no == 0 else 9
    looper.run(
        eventually(lambda: assertExp(replica.last_ordered_3pc ==
                                     (view_no, seq_no)),
                   retryWait=1,
                   timeout=waits.expectedTransactionExecutionTime(nodeCount)))
Exemple #33
0
def testReplicasRejectSamePrePrepareMsg(looper, txnPoolNodeSet, sdk_pool_handle, sdk_wallet_client):
    """
    Replicas should not accept PRE-PREPARE for view "v" and prepare sequence
    number "n" if it has already accepted a request with view number "v" and
    sequence number "n"

    """
    numOfNodes = 4
    fValue = getMaxFailures(numOfNodes)
    primaryRepl = getPrimaryReplica(txnPoolNodeSet, 1)
    logger.debug("Primary Replica: {}".format(primaryRepl))
    nonPrimaryReplicas = getNonPrimaryReplicas(txnPoolNodeSet, 1)
    logger.debug("Non Primary Replicas: " + str(nonPrimaryReplicas))

    # Delay COMMITs so request is not ordered and checks can be made
    c_delay = 10
    for node in txnPoolNodeSet:
        node.nodeIbStasher.delay(cDelay(delay=c_delay, instId=1))

    req1 = sdk_send_random_requests(looper,
                                    sdk_pool_handle,
                                    sdk_wallet_client,
                                    1)[0]
    request1 = sdk_json_to_request_object(req1[0])
    for npr in nonPrimaryReplicas:
        looper.run(eventually(checkPrepareReqSent,
                              npr,
                              request1.key,
                              primaryRepl.viewNo,
                              retryWait=1))
    prePrepareReq = primaryRepl._ordering_service.sent_preprepares[primaryRepl.viewNo,
                                                primaryRepl.lastPrePrepareSeqNo]
    looper.run(eventually(checkPrePrepareReqRecvd,
                          nonPrimaryReplicas,
                          prePrepareReq,
                          retryWait=1))

    # logger.debug("Patching the primary replica's pre-prepare sending method ")
    # orig_method = primaryRepl.sendPrePrepare

    # def patched(self, ppReq):
    #     self._ordering_service.sent_preprepares[ppReq.viewNo, ppReq.ppSeqNo] = ppReq
    #     ppReq = updateNamedTuple(ppReq, **{f.PP_SEQ_NO.nm: 1})
    #     self.send(ppReq, TPCStat.PrePrepareSent)
    #
    # primaryRepl.sendPrePrepare = types.MethodType(patched, primaryRepl)
    logger.debug(
        "Decrementing the primary replica's pre-prepare sequence number by "
        "one...")
    primaryRepl._ordering_service._lastPrePrepareSeqNo -= 1
    view_no = primaryRepl.viewNo
    request2 = sdk_json_to_request_object(
        sdk_send_random_requests(looper,
                                 sdk_pool_handle,
                                 sdk_wallet_client,
                                 1)[0][0])
    timeout = waits.expectedPrePrepareTime(len(txnPoolNodeSet))
    looper.run(eventually(checkPrePrepareReqSent, primaryRepl, request2,
                          retryWait=1, timeout=timeout))

    # Since the node is malicious, it will not be able to process requests due
    # to conflicts in PRE-PREPARE
    primaryRepl.node.stop()
    looper.removeProdable(primaryRepl.node)

    reqIdr = [request2.digest]
    tm = get_utc_epoch()
    prePrepareReq = PrePrepare(
        primaryRepl.instId,
        view_no,
        primaryRepl.lastPrePrepareSeqNo,
        tm,
        reqIdr,
        init_discarded(),
        primaryRepl._ordering_service.generate_pp_digest([request2.digest], view_no, tm),
        DOMAIN_LEDGER_ID,
        primaryRepl._ordering_service.get_state_root_hash(DOMAIN_LEDGER_ID),
        primaryRepl._ordering_service.get_txn_root_hash(DOMAIN_LEDGER_ID),
        0,
        True
    )

    logger.debug("""Checking whether all the non primary replicas have received
                the pre-prepare request with same sequence number""")
    timeout = waits.expectedPrePrepareTime(len(txnPoolNodeSet))
    looper.run(eventually(checkPrePrepareReqRecvd,
                          nonPrimaryReplicas,
                          prePrepareReq,
                          retryWait=1,
                          timeout=timeout))
    logger.debug("""Check that none of the non primary replicas didn't send
    any prepare message "
                             in response to the pre-prepare message""")
    timeout = waits.expectedPrepareTime(len(txnPoolNodeSet))
    looper.runFor(timeout)  # expect prepare processing timeout

    # check if prepares have not been sent
    for npr in nonPrimaryReplicas:
        with pytest.raises(AssertionError):
            looper.run(eventually(checkPrepareReqSent,
                                  npr,
                                  request2.key,
                                  view_no,
                                  retryWait=1,
                                  timeout=timeout))

    timeout = waits.expectedTransactionExecutionTime(len(txnPoolNodeSet)) + c_delay
    result1 = sdk_get_replies(looper, [req1])[0][1]
    logger.debug("request {} gives result {}".format(request1, result1))
Exemple #34
0
def test_node_requests_missing_preprepares_and_prepares_after_long_disconnection(
        looper, txnPoolNodeSet, sdk_wallet_client, sdk_pool_handle, tconf,
        tdirWithPoolTxns, allPluginsPath):
    """
    2 of 4 nodes go down, so pool can not process any more incoming requests.
    A new request comes in.
    Test than waits for some time to ensure that PrePrepare was created
    long enough seconds to be dropped by time checker.
    Two stopped nodes come back alive.
    Another request comes in.
    Check that previously disconnected two nodes request missing PREPREPARES
    and PREPARES and the pool successfully handles both transactions.
    """
    INIT_REQS_CNT = 5
    MISSING_REQS_CNT = 4
    REQS_AFTER_RECONNECT_CNT = 1
    alive_nodes = []
    disconnected_nodes = []

    for node in txnPoolNodeSet:
        if node.hasPrimary:
            alive_nodes.append(node)
        else:
            disconnected_nodes.append(node)

    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                              sdk_wallet_client, INIT_REQS_CNT)

    waitNodeDataEquality(looper, disconnected_nodes[0], *txnPoolNodeSet)
    init_ledger_size = txnPoolNodeSet[0].domainLedger.size

    current_node_set = set(txnPoolNodeSet)
    for node in disconnected_nodes:
        disconnect_node_and_ensure_disconnected(looper,
                                                current_node_set,
                                                node,
                                                stopNode=False)
        current_node_set.remove(node)

    sdk_send_random_requests(looper, sdk_pool_handle, sdk_wallet_client,
                             MISSING_REQS_CNT)

    looper.run(
        eventually(check_pp_out_of_sync,
                   alive_nodes,
                   disconnected_nodes,
                   retryWait=1,
                   timeout=expectedPoolGetReadyTimeout(len(txnPoolNodeSet))))

    preprepare_deviation = 4
    tconf.ACCEPTABLE_DEVIATION_PREPREPARE_SECS = preprepare_deviation
    time.sleep(preprepare_deviation * 2)

    for node in disconnected_nodes:
        current_node_set.add(node)
        reconnect_node_and_ensure_connected(looper, current_node_set, node)

    for node in txnPoolNodeSet:
        assert node.domainLedger.size == init_ledger_size

    for node in disconnected_nodes:
        assert node.master_replica.spylog.count(
            Replica._request_pre_prepare) == 0
        assert node.master_replica.spylog.count(Replica._request_prepare) == 0
        assert node.master_replica.spylog.count(
            Replica.process_requested_pre_prepare) == 0
        assert node.master_replica.spylog.count(
            Replica.process_requested_prepare) == 0

    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                              sdk_wallet_client, REQS_AFTER_RECONNECT_CNT)

    waitNodeDataEquality(looper, disconnected_nodes[0], *txnPoolNodeSet)

    for node in disconnected_nodes:
        assert node.master_replica.spylog.count(
            Replica._request_pre_prepare) > 0
        assert node.master_replica.spylog.count(Replica._request_prepare) > 0
        assert node.master_replica.spylog.count(
            Replica.process_requested_pre_prepare) > 0
        assert node.master_replica.spylog.count(
            Replica.process_requested_prepare) > 0

    for node in txnPoolNodeSet:
        assert node.domainLedger.size == (init_ledger_size + MISSING_REQS_CNT +
                                          REQS_AFTER_RECONNECT_CNT)
def poolUpgradeScheduled(poolUpgradeSubmitted, poolNodesStarted, validUpgrade):
    nodes = poolNodesStarted.nodes.values()
    timeout = waits.expectedUpgradeScheduled()
    poolNodesStarted.looper.run(
        eventually(checkUpgradeScheduled, nodes,
                   validUpgrade[VERSION], retryWait=1, timeout=timeout))
def testPoolUpgradeCancelled(poolUpgradeCancelled, poolNodesStarted):
    nodes = poolNodesStarted.nodes.values()
    timeout = waits.expectedNoUpgradeScheduled()
    poolNodesStarted.looper.run(
        eventually(checkNoUpgradeScheduled,
                   nodes, retryWait=1, timeout=timeout))
Exemple #37
0
def testPrePrepareWhenPrimaryStatusIsUnknown(tdir_for_func):
    nodeNames = genNodeNames(4)
    nodeReg = genNodeReg(names=nodeNames)
    with TestNodeSet(nodeReg=nodeReg, tmpdir=tdir_for_func) as nodeSet:
        with Looper(nodeSet) as looper:
            prepareNodeSet(looper, nodeSet)

            nodeA, nodeB, nodeC, nodeD = tuple(
                addNodeBack(
                    nodeSet, looper, nodeNames[i]) for i in range(
                    0, 4))

            # Since primary selection is round robin, A and B will be primaries

            # Nodes C and D delays self nomination so A and B can become
            # primaries
            # nodeC.delaySelfNomination(10)
            # nodeD.delaySelfNomination(10)

            # Node D delays receiving PRIMARY messages from all nodes so it
            # will not know whether it is primary or not

            # delayD = 5
            # nodeD.nodeIbStasher.delay(delayerMsgTuple(delayD, Primary))

            checkPoolReady(looper=looper, nodes=nodeSet)

            # client1, wal = setupClient(looper, nodeSet, tmpdir=tdir_for_func)
            # request = sendRandomRequest(wal, client1)

            # TODO Rethink this
            instNo = 0

            timeout = waits.expectedClientRequestPropagationTime(len(nodeSet))
            for i in range(3):
                node = nodeSet.getNode(nodeNames[i])
                # Nodes A, B and C should have received PROPAGATE request
                # from Node D
                looper.run(
                    eventually(checkIfPropagateRecvdFromNode, node, nodeD,
                               request.identifier,
                               request.reqId, retryWait=1, timeout=timeout))

            def assert_msg_count(typ, count):
                assert len(getPendingRequestsForReplica(nodeD.replicas[instNo],
                                                        typ)) == count

            # Node D should have 1 pending PRE-PREPARE request
            timeout = waits.expectedPrePrepareTime(len(nodeSet))
            looper.run(eventually(assert_msg_count, PrePrepare, 1,
                                  retryWait=1, timeout=timeout))

            # Node D should have 2 pending PREPARE requests(from node B and C)
            timeout = waits.expectedPrepareTime(len(nodeSet))
            looper.run(eventually(assert_msg_count, Prepare, 2, retryWait=1,
                                  timeout=timeout))

            # Its been checked above that replica stashes 3 phase messages in
            # lack of primary, now avoid delay (fix the network)
            nodeD.nodeIbStasher.reset_delays_and_process_delayeds()

            # Node D should have no pending PRE-PREPARE, PREPARE or COMMIT
            # requests
            for reqType in [PrePrepare, Prepare, Commit]:
                looper.run(
                    eventually(
                        lambda: assertLength(
                            getPendingRequestsForReplica(
                                nodeD.replicas[instNo],
                                reqType),
                            0),
                        retryWait=1,
                        timeout=delayD))  # wait little more than delay
def ensureConnectedToTestEnv(cli):
    if not cli.activeEnv:
        cli.enterCmd("connect test")
        cli.looper.run(
            eventually(checkConnectedToEnv, cli, retryWait=1, timeout=10))
def testPrimaryElectionWithTie(electTieFixture, looper, txnPoolNodeSet):
    """
    Primary selection (Rainy Day)
    A, B, C, D, E
    A, B, C, D startup. E is lagging.
    A sees the minimum number of nodes, and then sends Nominate(A)
    At the same exact time, B sees the minimum number of nodes, and then sends out Nominate(B)
    A sees B sending Nominate(B), but it has already nominated itself, so it does nothing
    B sees A sending Nominate(A), but it has already nominated itself, so it does nothing
    C sees A sending Nominate(A), and sends Nominate(A)
    D sees B sending Nominate(B), and sends Nominate(B)
    There's a split. C and A think A is the primary, B and D think B is the primary
    All nodes can see that there is a split. Each sends out Reelection([A,B])

    A and B both see Reelection([A,B]) from themselves as well as the other 3 (the number from others should be at least f+1),

    1. they wait a random amount of time (between 0 and 2 seconds),
    2. they each send out a Nominate(self)

    Voting is repeated until we have a good election.
    """

    # TODO optimize the sending messages in batches, for example, we don't
    #     send messages more often than 400 milliseconds. Once those 400
    #     millis have passed, we send the several queued messages in one
    #     batch.

    A, B, C, D = txnPoolNodeSet

    checkPoolReady(looper, txnPoolNodeSet)

    for node in txnPoolNodeSet:
        for instId, replica in enumerate(node.elector.replicas):
            logger.debug("replica {} {} with votes {}".
                         format(replica.name, replica.instId,
                                node.elector.nominations.get(instId, {})))

    nominationTimeout = waits.expectedPoolNominationTimeout(len(txnPoolNodeSet))
    logger.debug("Check nomination")
    # Checking whether Node A nominated itself
    looper.run(eventually(checkNomination, A, A.name,
                          retryWait=1, timeout=nominationTimeout))

    # Checking whether Node B nominated itself
    looper.run(eventually(checkNomination, B, B.name,
                          retryWait=1, timeout=nominationTimeout))

    # Checking whether Node C nominated Node A
    looper.run(eventually(checkNomination, C, A.name,
                          retryWait=1, timeout=nominationTimeout))

    # Checking whether Node D nominated Node D
    looper.run(eventually(checkNomination, D, B.name,
                          retryWait=1, timeout=nominationTimeout))

    # No node should be primary
    for node in txnPoolNodeSet:
        assert node.hasPrimary is False

    for node in txnPoolNodeSet:
        node.resetDelays()

    checkProtocolInstanceSetup(looper=looper, nodes=txnPoolNodeSet, retryWait=1)
Exemple #40
0
def test_catchup_not_triggered_if_another_in_progress(
        looper, chkFreqPatched, reqs_for_checkpoint, txnPoolNodeSet,
        sdk_pool_handle, sdk_wallet_client, broken_node_and_others):
    """
    A node misses 3pc messages and checkpoints during some period but later it
    stashes some amount of checkpoints and starts catchup. When the node is
    performing the catchup, it receives more checkpoints enough to start a new
    catchup but it does not start it because the first catchup is in progress.
    """
    max_batch_size = chkFreqPatched.Max3PCBatchSize
    broken_node, other_nodes = broken_node_and_others

    logger.info(
        "Step 1: The node misses quite a lot of 3PC-messages and checkpoints")

    send_reqs_batches_and_get_suff_replies(
        looper, txnPoolNodeSet, sdk_pool_handle, sdk_wallet_client,
        reqs_for_checkpoint + max_batch_size)

    waitNodeDataInequality(looper, broken_node, *other_nodes)

    logger.info(
        "Step 2: The node receives 3PC-messages but cannot process them because of "
        "missed ones. But the node eventually stashes some amount of checkpoints "
        "and after that starts catchup")

    repaired_node = repair_broken_node(broken_node)
    initial_do_start_catchup_times = repaired_node.spylog.count(
        Node._do_start_catchup)
    initial_all_ledgers_caught_up = repaired_node.spylog.count(
        Node.allLedgersCaughtUp)

    with delay_rules(repaired_node.nodeIbStasher, cr_delay()):
        send_reqs_batches_and_get_suff_replies(
            looper, txnPoolNodeSet, sdk_pool_handle, sdk_wallet_client,
            (Replica.STASHED_CHECKPOINTS_BEFORE_CATCHUP + 1) *
            reqs_for_checkpoint - max_batch_size)

        ensure_all_nodes_have_same_data(looper, other_nodes)

        looper.run(
            eventually(
                lambda: assertExp(repaired_node.mode == Mode.discovering),
                timeout=waits.expectedPoolInterconnectionTime(
                    len(txnPoolNodeSet)) +
                waits.expectedPoolConsistencyProof(len(txnPoolNodeSet))))

        assert repaired_node.spylog.count(
            Node._do_start_catchup) - initial_do_start_catchup_times == 1

        logger.info(
            "Step 3: While doing the catchup, the node receives new checkpoints "
            "enough to start a new catchup but the node does not start it because "
            "the former is in progress")

        process_checkpoint_times_before = \
            repaired_node.master_replica._checkpointer.spylog.count(CheckpointService.process_checkpoint)

        send_reqs_batches_and_get_suff_replies(
            looper, txnPoolNodeSet, sdk_pool_handle, sdk_wallet_client,
            (Replica.STASHED_CHECKPOINTS_BEFORE_CATCHUP + 1) *
            reqs_for_checkpoint)

        # Wait until the node receives the new checkpoints from all the other nodes
        looper.run(
            eventually(lambda: assertExp(
                repaired_node.master_replica._checkpointer.spylog.count(
                    CheckpointService.process_checkpoint
                ) - process_checkpoint_times_before ==
                (Replica.STASHED_CHECKPOINTS_BEFORE_CATCHUP + 1) *
                (len(txnPoolNodeSet) - 1)),
                       timeout=waits.expectedPoolInterconnectionTime(
                           len(txnPoolNodeSet))))

        # New catchup is not started when another one is in progress
        assert repaired_node.spylog.count(
            Node._do_start_catchup) - initial_do_start_catchup_times == 1
        assert repaired_node.mode == Mode.discovering

    logger.info("Step 4: The node completes the catchup. The ledger has been "
                "updated to the level determined on its start")

    looper.run(
        eventually(lambda: assertExp(repaired_node.mode == Mode.participating),
                   timeout=waits.expectedPoolCatchupTime(len(txnPoolNodeSet))))
    assert repaired_node.spylog.count(
        Node._do_start_catchup) - initial_do_start_catchup_times == 1
    assert repaired_node.spylog.count(
        Node.allLedgersCaughtUp) - initial_all_ledgers_caught_up == 1
    assert repaired_node.domainLedger.size == other_nodes[0].domainLedger.size
Exemple #41
0
    def _expect(expected,
                mapper=None,
                line_no=None,
                within=None,
                ignore_extra_lines=None):
        cur_cli = current_cli()

        def _():
            expected_ = expected if not mapper \
                else [s.format(**mapper) for s in expected]
            assert isinstance(expected_, List)
            bm = get_bookmark()
            actual = ''.join(cur_cli.cli.output.writes).splitlines()[bm:]
            assert isinstance(actual, List)
            explanation = ''
            expected_index = 0
            for i in range(min(len(expected_), len(actual))):
                e = expected_[expected_index]
                assert isinstance(e, str)
                a = actual[i]
                assert isinstance(a, str)
                is_p = type(e) == P
                if (not is_p and a != e) or (is_p and not e.match(a)):
                    if ignore_extra_lines:
                        continue
                    explanation += "line {} doesn't match\n"\
                                   "  expected: {}\n"\
                                   "    actual: {}\n".format(i, e, a)
                expected_index += 1

            if len(expected_) > len(actual):
                for e in expected_:
                    try:
                        p = re.compile(e) if type(e) == P else None
                    except Exception as err:
                        explanation += "ERROR COMPILING REGEX for {}: {}\n".\
                            format(e, err)
                    for a in actual:
                        if (p and p.fullmatch(a)) or a == e:
                            break
                    else:
                        explanation += "missing: {}\n".format(e)

            if len(expected_) < len(actual) and ignore_extra_lines is None:
                for a in actual:
                    for e in expected_:
                        p = re.compile(e) if type(e) == P else None
                        if (p and p.fullmatch(a)) or a == e:
                            break
                    else:
                        explanation += "extra: {}\n".format(a)

            if explanation:
                explanation += "\nexpected:\n"
                for x in expected_:
                    explanation += "  > {}\n".format(x)
                explanation += "\nactual:\n"
                for x in actual:
                    explanation += "  > {}\n".format(x)
                if line_no:
                    explanation += "section ends line number: {}\n".format(
                        line_no)
                pytest.fail(''.join(explanation))
            else:
                inc_bookmark(len(actual))

        if within:
            cur_cli.looper.run(eventually(_, timeout=within))
        else:
            _()
Exemple #42
0
def testReplyMatchesRequest(looper, nodeSet, tdir, up):
    '''
    This tests does check following things:
      - wallet works correctly when used by multiple clients
      - clients do receive responses for exactly the same request they sent
    '''

    def makeClient(id):
        client, wallet = genTestClient(nodeSet,
                                       tmpdir=tdir,
                                       name="client-{}".format(id))
        looper.add(client)
        looper.run(client.ensureConnectedToNodes())
        return client, wallet

    # creating clients
    numOfClients = 3
    numOfRequests = 1

    clients = set()
    sharedWallet = None
    for i in range(numOfClients):
        client, wallet = makeClient(i)
        if sharedWallet is None:
            sharedWallet = wallet
        clients.add(client)

    for i in range(1, numOfRequests + 1):
        # sending requests
        requests = {}
        for client in clients:
            op = randomOperation()
            req = sharedWallet.signOp(op)

            request = client.submitReqs(req)[0][0]
            requests[client] = (request.reqId, request.operation['amount'])

        # checking results
        responseTimeout = waits.expectedTransactionExecutionTime(nodeCount)
        for client, (reqId, sentAmount) in requests.items():
            looper.run(eventually(checkResponseRecvdFromNodes,
                                  client,
                                  nodeCount,
                                  reqId,
                                  retryWait=1,
                                  timeout=responseTimeout))

            print("Expected amount for request {} is {}".
                  format(reqId, sentAmount))

            # This looks like it fails on some python versions
            # replies = [r[0]['result']['amount']
            #            for r in client.inBox
            #            if r[0]['op'] == 'REPLY'
            #            and r[0]['result']['reqId'] == reqId]

            replies = []
            for r in client.inBox:
                if r[0]['op'] == 'REPLY' and r[0]['result']['reqId'] == reqId:
                    if 'amount' not in r[0]['result']:
                        logger.debug('{} cannot find amount in {}'.
                                     format(client, r[0]['result']))
                    replies.append(r[0]['result']['amount'])

            assert all(replies[0] == r for r in replies)
            assert replies[0] == sentAmount
def test_node_catchup_after_restart_with_txns(
        newNodeCaughtUp,
        txnPoolNodeSet,
        tconf,
        nodeSetWithNodeAddedAfterSomeTxns,
        tdirWithPoolTxns,
        allPluginsPath):
    """
    A node that restarts after some transactions should eventually get the
    transactions which happened while it was down
    :return:
    """
    looper, newNode, client, wallet, _, _ = nodeSetWithNodeAddedAfterSomeTxns
    logger.debug("Stopping node {} with pool ledger size {}".
                 format(newNode, newNode.poolManager.txnSeqNo))
    disconnect_node_and_ensure_disconnected(looper, txnPoolNodeSet, newNode)
    looper.removeProdable(newNode)
    # for n in txnPoolNodeSet[:4]:
    #     for r in n.nodestack.remotes.values():
    #         if r.name == newNode.name:
    #             r.removeStaleCorrespondents()
    # looper.run(eventually(checkNodeDisconnectedFrom, newNode.name,
    #                       txnPoolNodeSet[:4], retryWait=1, timeout=5))
    # TODO: Check if the node has really stopped processing requests?
    logger.debug("Sending requests")
    more_requests = 5
    sendReqsToNodesAndVerifySuffReplies(looper, wallet, client, more_requests)
    logger.debug("Starting the stopped node, {}".format(newNode))
    nodeHa, nodeCHa = HA(*newNode.nodestack.ha), HA(*newNode.clientstack.ha)
    newNode = TestNode(
        newNode.name,
        basedirpath=tdirWithPoolTxns,
        config=tconf,
        ha=nodeHa,
        cliha=nodeCHa,
        pluginPaths=allPluginsPath)
    looper.add(newNode)
    txnPoolNodeSet[-1] = newNode

    # Delay catchup reply processing so LedgerState does not change
    delay_catchup_reply = 5
    newNode.nodeIbStasher.delay(cr_delay(delay_catchup_reply))
    looper.run(checkNodesConnected(txnPoolNodeSet))

    # Make sure ledger starts syncing (sufficient consistency proofs received)
    looper.run(eventually(check_ledger_state, newNode, DOMAIN_LEDGER_ID,
                          LedgerState.syncing, retryWait=.5, timeout=5))

    confused_node = txnPoolNodeSet[0]
    new_node_ledger = newNode.ledgerManager.ledgerRegistry[DOMAIN_LEDGER_ID]
    cp = new_node_ledger.catchUpTill
    start, end = cp.seqNoStart, cp.seqNoEnd
    cons_proof = confused_node.ledgerManager._buildConsistencyProof(
        DOMAIN_LEDGER_ID, start, end)

    bad_send_time = None

    def chk():
        nonlocal bad_send_time
        entries = newNode.ledgerManager.spylog.getAll(
            newNode.ledgerManager.canProcessConsistencyProof.__name__)
        for entry in entries:
            # `canProcessConsistencyProof` should return False after `syncing_time`
            if entry.result == False and entry.starttime > bad_send_time:
                return
        assert False

    def send_and_chk(ledger_state):
        nonlocal bad_send_time, cons_proof
        bad_send_time = perf_counter()
        confused_node.ledgerManager.sendTo(cons_proof, newNode.name)
        # Check that the ConsistencyProof messages rejected
        looper.run(eventually(chk, retryWait=.5, timeout=5))
        check_ledger_state(newNode, DOMAIN_LEDGER_ID, ledger_state)

    send_and_chk(LedgerState.syncing)

    # Not accurate timeout but a conservative one
    timeout = waits.expectedPoolGetReadyTimeout(len(txnPoolNodeSet)) + \
        2 * delay_catchup_reply
    waitNodeDataEquality(looper, newNode, *txnPoolNodeSet[:-1],
                         customTimeout=timeout)
    assert new_node_ledger.num_txns_caught_up == more_requests
    send_and_chk(LedgerState.synced)
def test_discard_3PC_messages_for_already_ordered(looper, txnPoolNodeSet,
                                                  sdk_wallet_client,
                                                  sdk_pool_handle):
    """
    Nodes discard any 3PC messages for already ordered 3PC keys
    (view_no, pp_seq_no). Delay all 3PC messages to a node so it cannot respond
    to them unless the other nodes order them, now when the slow node will get
    them it will respond but other nodes will not process them and discard them
    """
    slow_node = [r.node for r in getNonPrimaryReplicas(txnPoolNodeSet, 0)][-1]
    other_nodes = [n for n in txnPoolNodeSet if n != slow_node]
    delay = 20
    delay_3pc_messages([slow_node], 0, delay)
    delay_3pc_messages([slow_node], 1, delay)

    sent_batches = 3
    sdk_send_batches_of_random_and_check(looper,
                                         txnPoolNodeSet,
                                         sdk_pool_handle,
                                         sdk_wallet_client,
                                         num_reqs=2 * sent_batches,
                                         num_batches=sent_batches)

    # send_reqs_batches_and_get_suff_replies(looper, wallet1, client1,
    #                                        2 * sent_batches, sent_batches)

    def chk(node, inst_id, p_count, c_count):
        # A node will still record PREPRAREs even if more than n-f-1, till the
        # request is not ordered
        assert len(
            node.replicas[inst_id]._ordering_service.prepares) >= p_count
        assert len(node.replicas[inst_id]._ordering_service.commits) == c_count

    def count_discarded(inst_id, count):
        for node in other_nodes:
            assert countDiscarded(node.replicas[inst_id].stasher,
                                  'already ordered 3 phase message') == count

    # `slow_node` did not receive any PREPAREs or COMMITs
    chk(slow_node, 0, 0, 0)

    # `other_nodes` have not discarded any 3PC message
    count_discarded(0, 0)

    # `other_nodes` have not recorded any PREPAREs or COMMITs from `slow_node`
    chk_commits_prepares_recvd(0, other_nodes, slow_node)

    slow_node.reset_delays_and_process_delayeds()
    waitNodeDataEquality(looper, slow_node, *other_nodes)

    # `slow_node` did receive correct number of PREPAREs and COMMITs
    looper.run(
        eventually(chk,
                   slow_node,
                   0,
                   sent_batches - 1,
                   sent_batches,
                   retryWait=1))

    # `other_nodes` have not recorded any PREPAREs or COMMITs from `slow_node`
    chk_commits_prepares_recvd(0, other_nodes, slow_node)

    # `other_nodes` have discarded PREPAREs and COMMITs all batches
    count_discarded(0, 2 * sent_batches)
Exemple #45
0
def test_audit_ledger_updated_after_freshness_updated(looper, tconf,
                                                      txnPoolNodeSet,
                                                      initial_domain_size,
                                                      initial_pool_size,
                                                      initial_config_size):
    # 1. Wait for the first freshness update
    looper.run(
        eventually(check_freshness_updated_for_all,
                   txnPoolNodeSet,
                   timeout=2 * FRESHNESS_TIMEOUT))

    audit_size_initial = [node.auditLedger.size for node in txnPoolNodeSet]
    view_no = txnPoolNodeSet[0].master_replica.last_ordered_3pc[0]
    pp_seq_no = txnPoolNodeSet[0].master_replica.last_ordered_3pc[1]
    initial_seq_no = txnPoolNodeSet[0].auditLedger.size

    # 2. Wait for the second freshness update
    bls_multi_sigs_after_first_update = get_all_multi_sig_values_for_all_nodes(
        txnPoolNodeSet)
    looper.run(
        eventually(check_updated_bls_multi_sig_for_all_ledgers,
                   txnPoolNodeSet,
                   bls_multi_sigs_after_first_update,
                   FRESHNESS_TIMEOUT,
                   timeout=FRESHNESS_TIMEOUT + 5))

    # 3. check that there is audit ledger txn created for each ledger updated as a freshness check
    check_audit_ledger_updated(audit_size_initial,
                               txnPoolNodeSet,
                               audit_txns_added=3)
    for node in txnPoolNodeSet:
        check_audit_txn(
            txn=node.auditLedger.getBySeqNo(node.auditLedger.size - 2),
            view_no=view_no,
            pp_seq_no=pp_seq_no + 1,
            seq_no=initial_seq_no + 1,
            txn_time=node.master_replica.last_accepted_pre_prepare_time,
            ledger_id=POOL_LEDGER_ID,
            txn_root=node.getLedger(POOL_LEDGER_ID).tree.root_hash,
            state_root=node.getState(POOL_LEDGER_ID).committedHeadHash,
            pool_size=initial_pool_size,
            domain_size=initial_domain_size,
            config_size=initial_config_size,
            last_pool_seqno=None,
            last_domain_seqno=2,
            last_config_seqno=3,
            primaries=pp_seq_no + 1 - 1)

        check_audit_txn(
            txn=node.auditLedger.getBySeqNo(node.auditLedger.size - 1),
            view_no=view_no,
            pp_seq_no=pp_seq_no + 2,
            seq_no=initial_seq_no + 2,
            txn_time=node.master_replica.last_accepted_pre_prepare_time,
            ledger_id=DOMAIN_LEDGER_ID,
            txn_root=node.getLedger(DOMAIN_LEDGER_ID).tree.root_hash,
            state_root=node.getState(DOMAIN_LEDGER_ID).committedHeadHash,
            pool_size=initial_pool_size,
            domain_size=initial_domain_size,
            config_size=initial_config_size,
            last_pool_seqno=4,
            last_domain_seqno=None,
            last_config_seqno=3,
            primaries=pp_seq_no + 2 - 1)

        check_audit_txn(
            txn=node.auditLedger.getBySeqNo(node.auditLedger.size),
            view_no=view_no,
            pp_seq_no=pp_seq_no + 3,
            seq_no=initial_seq_no + 3,
            txn_time=node.master_replica.last_accepted_pre_prepare_time,
            ledger_id=CONFIG_LEDGER_ID,
            txn_root=node.getLedger(CONFIG_LEDGER_ID).tree.root_hash,
            state_root=node.getState(CONFIG_LEDGER_ID).committedHeadHash,
            pool_size=initial_pool_size,
            domain_size=initial_domain_size,
            config_size=initial_config_size,
            last_pool_seqno=4,
            last_domain_seqno=5,
            last_config_seqno=None,
            primaries=pp_seq_no + 3 - 1)
def test_second_checkpoint_after_catchup_can_be_stabilized(
        chkFreqPatched, looper, txnPoolNodeSet, sdk_wallet_steward,
        sdk_wallet_client, sdk_pool_handle, tdir, tconf,
        allPluginsPath):
    _, new_node = sdk_add_new_steward_and_node(
        looper, sdk_pool_handle, sdk_wallet_steward,
        'EpsilonSteward', 'Epsilon', tdir, tconf,
        allPluginsPath=allPluginsPath)
    txnPoolNodeSet.append(new_node)
    looper.run(checkNodesConnected(txnPoolNodeSet))
    waitNodeDataEquality(looper, new_node, *txnPoolNodeSet[:-1])
    # Epsilon did not participate in ordering of the batch with EpsilonSteward
    # NYM transaction and the batch with Epsilon NODE transaction.
    # Epsilon got these transactions via catch-up.

    master_replica = new_node.replicas._master_replica

    assert len(master_replica._checkpointer._checkpoint_state) == 0

    assert len(master_replica._checkpointer._stashed_recvd_checkpoints) == 0

    assert master_replica.h == 2
    assert master_replica.H == 17

    sdk_send_random_and_check(looper, txnPoolNodeSet,
                              sdk_pool_handle, sdk_wallet_client, 1)

    for replica in new_node.replicas.values():
        looper.run(eventually(
            lambda r: assertExp(len(r._checkpointer._checkpoint_state) == 1), replica))

        assert len(replica._checkpointer._stashed_recvd_checkpoints) == 0

        assert replica.h == 2
        assert replica.H == 17

    sdk_send_random_and_check(looper, txnPoolNodeSet,
                              sdk_pool_handle, sdk_wallet_client, 6)
    stabilization_timeout = \
        waits.expectedTransactionExecutionTime(len(txnPoolNodeSet))
    looper.runFor(stabilization_timeout)

    for replica in new_node.replicas.values():
        assert len(replica._checkpointer._checkpoint_state) == 2
        keys_iter = iter(replica._checkpointer._checkpoint_state)

        assert next(keys_iter) == (3, 5)
        assert replica._checkpointer._checkpoint_state[3, 5].seqNo == 5
        assert replica._checkpointer._checkpoint_state[3, 5].digest is None
        assert replica._checkpointer._checkpoint_state[3, 5].isStable is False

        assert next(keys_iter) == (6, 10)
        assert replica._checkpointer._checkpoint_state[6, 10].seqNo == 9
        assert replica._checkpointer._checkpoint_state[6, 10].digest is None
        assert replica._checkpointer._checkpoint_state[6, 10].isStable is False

        # nothing is stashed since it's ordered during catch-up
        assert len(replica._checkpointer._stashed_recvd_checkpoints) == 0

        assert replica.h == 2
        assert replica.H == 17

    sdk_send_random_and_check(looper, txnPoolNodeSet,
                              sdk_pool_handle, sdk_wallet_client, 1)
    looper.runFor(stabilization_timeout)

    for replica in new_node.replicas.values():
        assert len(replica._checkpointer._checkpoint_state) == 1
        keys_iter = iter(replica._checkpointer._checkpoint_state)

        assert next(keys_iter) == (6, 10)
        assert replica._checkpointer._checkpoint_state[6, 10].seqNo == 10
        assert replica._checkpointer._checkpoint_state[6, 10].digest is not None
        assert replica._checkpointer._checkpoint_state[6, 10].isStable is True

        assert len(replica._checkpointer._stashed_recvd_checkpoints) == 0

        assert replica.h == 10
        assert replica.H == 25
Exemple #47
0
def try_view_change(looper, nodes):
    for node in nodes:
        looper.run(eventually(node.view_changer.serviceQueues))
def test_view_not_changed_when_primary_disconnected_from_less_than_quorum(
        txnPoolNodeSet, looper, sdk_pool_handle, sdk_wallet_client):
    """
    Less than quorum nodes lose connection with primary, this should not
    trigger view change as the protocol can move ahead
    """
    pr_node = get_master_primary_node(txnPoolNodeSet)
    npr = getNonPrimaryReplicas(txnPoolNodeSet, 0)
    partitioned_rep = npr[0]
    partitioned_node = partitioned_rep.node

    lost_pr_calls = partitioned_node.spylog.count(
        partitioned_node.lost_master_primary.__name__)

    recv_inst_chg_calls = {
        node.name: node.spylog.count(
            node.view_changer.process_instance_change_msg.__name__)
        for node in txnPoolNodeSet
        if node != partitioned_node and node != pr_node
    }

    view_no = checkViewNoForNodes(txnPoolNodeSet)
    orig_retry_meth = partitioned_node.nodestack.retryDisconnected

    def wont_retry(self, exclude=None):
        # Do not attempt to retry connection
        pass

    # simulating a partition here
    # Disconnect a node from only the primary of the master and dont retry to
    # connect to it
    partitioned_node.nodestack.retryDisconnected = types.MethodType(
        wont_retry, partitioned_node.nodestack)
    r = partitioned_node.nodestack.getRemote(pr_node.nodestack.name)
    r.disconnect()

    def chk1():
        # Check that the partitioned node detects losing connection with
        # primary and sends an instance change which is received by other
        # nodes except the primary (since its disconnected from primary)
        assert partitioned_node.spylog.count(
            partitioned_node.lost_master_primary.__name__) > lost_pr_calls
        for node in txnPoolNodeSet:
            if node != partitioned_node and node != pr_node:
                assert node.view_changer.spylog.count(
                    node.view_changer.process_instance_change_msg.__name__
                ) > recv_inst_chg_calls[node.name]

    looper.run(eventually(chk1, retryWait=1, timeout=10))

    def chk2():
        # Check the view does not change
        with pytest.raises(AssertionError):
            assert checkViewNoForNodes(txnPoolNodeSet) == view_no + 1

    looper.run(eventually(chk2, retryWait=1, timeout=10))
    # Send some requests and make sure the request execute
    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                              sdk_wallet_client, 5)

    # Repair the connection so the node is no longer partitioned
    partitioned_node.nodestack.retryDisconnected = types.MethodType(
        orig_retry_meth, partitioned_node.nodestack)

    # Send some requests and make sure the request execute
    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                              sdk_wallet_client, 5)

    # Partitioned node should have the same ledger and state as others
    # eventually
    waitNodeDataEquality(looper, partitioned_node,
                         *[n for n in txnPoolNodeSet if n != partitioned_node])
def test_reconnect_primary_and_not_primary(looper, txnPoolNodeSet,
                                           sdk_wallet_steward, sdk_pool_handle,
                                           tconf):
    """
    Test steps:
    Pool of 7 nodes.
    count of instances must be 3
    1. Choose node, that is not primary on all replicas (3 index)
    2. Disconnect them
    3. Ensure, that number of replicas was decreased
    4. Choose current primary node (must be 0)
    5. Disconnect primary
    6. Ensure, that view change complete and primary was selected
    7. Add node back from 1 step
    8. Add node back from 4 step
    9. Check, that count of instance (f+1 = 3)
    10. Send some requests and check, that pool works.
    """
    restNodes = set(txnPoolNodeSet)
    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                              sdk_wallet_steward, 5)
    assert txnPoolNodeSet[0].master_replica.isPrimary
    node_after_all_primary = txnPoolNodeSet[3]
    # Disconnect node after all primaries (after all backup primaries)
    disconnect_node_and_ensure_disconnected(looper,
                                            restNodes,
                                            node_after_all_primary,
                                            stopNode=False)
    # -------------------------------------------------------
    restNodes.remove(node_after_all_primary)
    looper.run(
        eventually(partial(check_count_connected_node, restNodes, 6),
                   timeout=5,
                   acceptableExceptions=[AssertionError]))
    sdk_send_random_and_check(looper, restNodes, sdk_pool_handle,
                              sdk_wallet_steward, 5)
    # Get primary node for backup replica
    primary_node = txnPoolNodeSet[0]
    assert primary_node.master_replica.isPrimary
    old_view_no = checkViewNoForNodes(restNodes, 0)
    # disconnect primary node
    disconnect_node_and_ensure_disconnected(looper,
                                            restNodes,
                                            primary_node,
                                            stopNode=False)
    # -------------------------------------------------------
    restNodes.remove(primary_node)
    looper.run(
        eventually(partial(check_count_connected_node, restNodes, 5),
                   timeout=5,
                   acceptableExceptions=[AssertionError]))
    looper.run(
        eventually(partial(checkViewNoForNodes,
                           restNodes,
                           expectedViewNo=old_view_no + 1),
                   timeout=tconf.VIEW_CHANGE_TIMEOUT))
    sdk_send_random_and_check(looper, restNodes, sdk_pool_handle,
                              sdk_wallet_steward, 5)
    logger.debug("restNodes: {}".format(restNodes))
    restNodes.add(node_after_all_primary)
    # Return back node after all primary
    reconnect_node_and_ensure_connected(looper, restNodes,
                                        node_after_all_primary)
    looper.run(
        checkNodesConnected(restNodes,
                            customTimeout=5 * tconf.RETRY_TIMEOUT_RESTRICTED))
    looper.run(
        eventually(partial(check_count_connected_node, restNodes, 6),
                   timeout=5,
                   acceptableExceptions=[AssertionError]))
    assert len(set([len(n.replicas) for n in restNodes])) == 1
    sdk_send_random_and_check(looper, restNodes, sdk_pool_handle,
                              sdk_wallet_steward, 5)
    # Return back primary node
    restNodes.add(primary_node)
    reconnect_node_and_ensure_connected(looper, restNodes, primary_node)
    looper.run(
        checkNodesConnected(restNodes,
                            customTimeout=5 * tconf.RETRY_TIMEOUT_RESTRICTED))
    sdk_send_random_and_check(looper, restNodes, sdk_pool_handle,
                              sdk_wallet_steward, 5)
Exemple #50
0
def test_commit_signature_validation_integration(looper,
                                                 txnPoolNodeSet,
                                                 sdk_pool_handle,
                                                 sdk_wallet_steward,
                                                 sdk_wallet_client,
                                                 tconf,
                                                 tdir):
    '''
    All nodes receive PrePrepare1(txn1 for pool_ledger)
    Nodes 1, 2 ordered txn1 and nodes 3, 4 did not.
    All nodes  receive PrePrepare2(txn2 for domain_ledger)
    Nodes 3, 4 receive commits from nodes 1, 2
    Nodes 3, 4 ordered txn1
    Check that all nodes ordered txn2
    '''
    fast_nodes = txnPoolNodeSet[:2]
    slow_nodes = txnPoolNodeSet[2:]

    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                              sdk_wallet_steward, 1)

    # create new steward
    new_steward_wallet_handle = sdk_add_new_nym(looper,
                                                sdk_pool_handle,
                                                sdk_wallet_steward,
                                                alias="testClientSteward945",
                                                role=STEWARD_STRING)

    sigseed, verkey, bls_key, nodeIp, nodePort, clientIp, clientPort, key_proof = \
        prepare_new_node_data(tconf, tdir, "new_node")

    # create node request to add new demote node
    _, steward_did = new_steward_wallet_handle
    node_request = looper.loop.run_until_complete(
        prepare_node_request(steward_did,
                             new_node_name="new_node",
                             clientIp=clientIp,
                             clientPort=clientPort,
                             nodeIp=nodeIp,
                             nodePort=nodePort,
                             bls_key=bls_key,
                             sigseed=sigseed,
                             services=[],
                             key_proof=key_proof))

    first_ordered = txnPoolNodeSet[0].master_last_ordered_3PC
    with ord_delay(slow_nodes):
        request1 = sdk_sign_and_send_prepared_request(looper, new_steward_wallet_handle,
                                                      sdk_pool_handle, node_request)

        key1 = get_key_from_req(request1[0])

        def check_nodes_receive_pp(view_no, seq_no):
            for node in txnPoolNodeSet:
                assert node.master_replica.getPrePrepare(view_no, seq_no)

        looper.run(eventually(check_nodes_receive_pp, first_ordered[0], first_ordered[1] + 1))

        def check_fast_nodes_ordered_request():
            for n in fast_nodes:
                assert key1 not in n.requests or n.requests[key1].executed
            for n in slow_nodes:
                assert not n.requests[key1].executed

        looper.run(eventually(check_fast_nodes_ordered_request))

        request2 = sdk_send_random_request(looper, sdk_pool_handle, sdk_wallet_client)
        looper.run(eventually(check_nodes_receive_pp, first_ordered[0], first_ordered[1] + 2))

        def check_nodes_receive_commits(view_no, seq_no):
            for node in txnPoolNodeSet:
                assert len(node.master_replica.commits[view_no, seq_no].voters) >= node.f + 1
        looper.run(eventually(check_nodes_receive_commits, first_ordered[0], first_ordered[1] + 2))

    sdk_get_and_check_replies(looper, [request1])
    sdk_get_and_check_replies(looper, [request2])
Exemple #51
0
def test_limited_stash_3pc_while_catchup(tdir, tconf, looper, testNodeClass,
                                         txnPoolNodeSet, sdk_pool_handle,
                                         sdk_wallet_client, allPluginsPath,
                                         chkFreqPatched):
    '''
    Test that the lagging_node can process messages from catchup stash after catchup
     and request lost messages from other nodes.
    '''

    # Prepare nodes
    lagging_node = txnPoolNodeSet[-1]
    rest_nodes = txnPoolNodeSet[:-1]

    # Check that requests executed well
    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                              sdk_wallet_client, 1)

    # Stop one node
    waitNodeDataEquality(looper, lagging_node, *rest_nodes)
    disconnect_node_and_ensure_disconnected(looper,
                                            txnPoolNodeSet,
                                            lagging_node,
                                            stopNode=True)
    looper.removeProdable(lagging_node)

    # Order 2 checkpoints on rest_nodes (2 txns in 2 batches)
    sdk_send_batches_of_random_and_check(looper, txnPoolNodeSet,
                                         sdk_pool_handle, sdk_wallet_client,
                                         2 * CHK_FREQ, 2)
    waitNodeDataEquality(looper, *rest_nodes)

    # Restart stopped node and wait for successful catch up
    lagging_node = start_stopped_node(
        lagging_node,
        looper,
        tconf,
        tdir,
        allPluginsPath,
        start=False,
    )

    initial_all_ledgers_caught_up = lagging_node.spylog.count(
        Node.allLedgersCaughtUp)

    with delay_rules(lagging_node.nodeIbStasher, cs_delay(),
                     msg_rep_delay(types_to_delay=[PREPARE, PREPREPARE])):
        with delay_rules(lagging_node.nodeIbStasher, cr_delay()):
            looper.add(lagging_node)
            txnPoolNodeSet[-1] = lagging_node
            looper.run(checkNodesConnected(txnPoolNodeSet))

            # Order 2 checkpoints in the first lagging node catchup (2 txns in 2 batches)
            sdk_send_batches_of_random_and_check(looper, txnPoolNodeSet,
                                                 sdk_pool_handle,
                                                 sdk_wallet_client,
                                                 2 * CHK_FREQ, 2)

        # Check that firs txn was ordered from stash after first catchup
        looper.run(
            eventually(lambda: assertExp(lagging_node.master_last_ordered_3PC[
                1] == txnPoolNodeSet[0].master_last_ordered_3PC[1] - 1),
                       retryWait=1,
                       timeout=waits.expectedPoolCatchupTime(
                           len(txnPoolNodeSet))))

        # Order 2 checkpoints in the second lagging node catchup (2 txns in 2 batches)
        sdk_send_batches_of_random_and_check(looper, txnPoolNodeSet,
                                             sdk_pool_handle,
                                             sdk_wallet_client, 2 * CHK_FREQ,
                                             2)

    waitNodeDataEquality(looper, *txnPoolNodeSet, customTimeout=5)
    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                              sdk_wallet_client, 1)
    looper.run(
        eventually(lambda: assertExp(lagging_node.master_last_ordered_3PC == n.
                                     master_last_ordered_3PC
                                     for n in txnPoolNodeSet)))

    # check that catch-up was started only twice
    assert lagging_node.spylog.count(
        Node.allLedgersCaughtUp) == initial_all_ledgers_caught_up + 2
Exemple #52
0
def test_checkpoints_after_view_change(tconf, looper, chkFreqPatched,
                                       reqs_for_checkpoint, txnPoolNodeSet,
                                       sdk_pool_handle, sdk_wallet_client):
    '''
    Tests that there is no infinite catchups if there is
    a quorum of stashed checkpoints received during the view change
    '''

    # Prepare nodes
    lagging_node = txnPoolNodeSet[-1]
    rest_nodes = txnPoolNodeSet[:-1]

    initial_all_ledgers_caught_up = lagging_node.spylog.count(
        Node.allLedgersCaughtUp)
    initial_start_catchup = lagging_node.spylog.count(Node.start_catchup)

    with delay_rules(lagging_node.nodeIbStasher, lsDelay()):
        with delay_rules(lagging_node.nodeIbStasher, vcd_delay()):
            ensure_view_change(looper, txnPoolNodeSet)
            looper.run(
                eventually(lambda: assertExp(lagging_node.
                                             view_change_in_progress is True),
                           timeout=waits.expectedPoolCatchupTime(
                               len(txnPoolNodeSet))))
            ensureElectionsDone(looper=looper,
                                nodes=rest_nodes,
                                instances_list=range(2))

            assert all(n.view_change_in_progress is False for n in rest_nodes)
            assert lagging_node.view_change_in_progress is True

            # make sure that more requests are being ordered while catch-up is in progress on the lagging node
            # stash enough stable checkpoints for starting a catch-up
            num_checkpoints = Replica.STASHED_CHECKPOINTS_BEFORE_CATCHUP + 1
            num_reqs = reqs_for_checkpoint * num_checkpoints + 1
            sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                                      sdk_wallet_client, num_reqs)
            looper.run(
                eventually(check_last_ordered_3pc_on_master, rest_nodes,
                           (1, num_reqs + 1)))
            looper.run(
                eventually(check_last_ordered_3pc_on_backup, rest_nodes,
                           (1, num_reqs + 1)))

            # all good nodes stabilized checkpoint
            looper.run(eventually(chkChkpoints, rest_nodes, 2, 0))

            assert get_stashed_checkpoints(
                lagging_node) == num_checkpoints * len(rest_nodes)
            # lagging node is doing the view change and stashing all checkpoints
            assert lagging_node.view_change_in_progress is True
            looper.run(
                eventually(lambda: assertExp(
                    get_stashed_checkpoints(lagging_node) == 2 * len(rest_nodes
                                                                     )),
                           timeout=waits.expectedPoolCatchupTime(
                               len(txnPoolNodeSet))))

    # check that view change is finished
    ensureElectionsDone(looper=looper, nodes=txnPoolNodeSet)
    assert lagging_node.view_change_in_progress is False

    # check that last_ordered is set
    looper.run(
        eventually(check_last_ordered_3pc_on_master, [lagging_node],
                   (1, num_reqs + 1)))
    looper.run(
        eventually(check_last_ordered_3pc_on_backup, [lagging_node],
                   (1, num_reqs + 1)))

    # check that checkpoint is stabilized for master
    looper.run(eventually(chk_chkpoints_for_instance, [lagging_node], 0, 2, 0))

    # check that the catch-up is finished
    assert lagging_node.mode == Mode.participating
    assert lagging_node.spylog.count(
        Node.allLedgersCaughtUp) == initial_all_ledgers_caught_up + 1
    assert lagging_node.spylog.count(
        Node.start_catchup) == initial_start_catchup + 1

    waitNodeDataEquality(looper, *txnPoolNodeSet, customTimeout=5)
def test_successive_batch_do_no_change_state(looper, tdirWithPoolTxns,
                                             tdirWithDomainTxnsUpdated, tconf,
                                             nodeSet, trustee, trusteeWallet,
                                             monkeypatch):
    """
    Send 2 NYM txns in different batches such that the second batch does not
    change state so that state root remains same, but keep the identifier
    and reqId different. Make sure the first request is not ordered by the
    primary before PRE-PREPARE for the second is sent.
    Also check reject and commit
    :return:
    """
    prim_node = getPrimaryReplica(nodeSet, 0).node
    other_nodes = [n for n in nodeSet if n != prim_node]
    # Delay only first PRE-PREPARE
    pp_seq_no_to_delay = 1

    def specific_pre_prepare(wrappedMsg):
        nonlocal pp_seq_no_to_delay
        msg, sender = wrappedMsg
        if isinstance(msg, PrePrepare) and \
                msg.instId == 0 and \
                msg.ppSeqNo == pp_seq_no_to_delay:
            return 5

    def delay_commits(wrappedMsg):
        msg, sender = wrappedMsg
        if isinstance(msg, Commit) and msg.instId == 0:
            return 10

    def new_identity():
        wallet = Wallet(randomString(5))
        signer = DidSigner()
        new_idr, _ = wallet.addIdentifier(signer=signer)
        verkey = wallet.getVerkey(new_idr)
        idy = Identity(identifier=new_idr, verkey=verkey, role=None)
        return idy

    def submit_id_req(idy):
        nonlocal all_reqs
        trusteeWallet.updateTrustAnchoredIdentity(idy)
        reqs = trusteeWallet.preparePending()
        all_reqs.extend(reqs)
        trustee.submitReqs(*reqs)

    def check_verkey(i, vk):
        for node in nodeSet:
            data = node.reqHandler.idrCache.getNym(i, isCommitted=True)
            assert data[VERKEY] == vk

    def check_uncommitted(count):
        for node in nodeSet:
            assert len(node.reqHandler.idrCache.unCommitted) == count

    for node in other_nodes:
        node.nodeIbStasher.delay(specific_pre_prepare)

    idy = new_identity()
    new_idr = idy.identifier
    verkey = idy.verkey

    all_reqs = []

    # Setting the same verkey twice but in different batches with different
    #  request ids
    for _ in range(3):
        submit_id_req(idy)
        looper.runFor(.2)

    waitForSufficientRepliesForRequests(looper,
                                        trustee,
                                        requests=all_reqs,
                                        add_delay_to_timeout=5)

    # Number of uncommitted entries is 0
    looper.run(eventually(check_uncommitted, 0))

    check_verkey(new_idr, verkey)

    pp_seq_no_to_delay = 4
    for node in other_nodes:
        node.nodeIbStasher.delay(specific_pre_prepare)
        # disable requesting missing 3 phase messages. Otherwise PP delay won't work
        for rpl in node.replicas:
            monkeypatch.setattr(rpl, '_request_missing_three_phase_messages',
                                lambda *x, **y: None)

    # Setting the verkey to `x`, then `y` and then back to `x` but in different
    # batches with different request ids. The idea is to change
    # state root to `t` then `t'` and then back to `t` and observe that no
    # errors are encountered

    idy = new_identity()
    new_idr = idy.identifier
    verkey = idy.verkey
    submit_id_req(idy)
    looper.runFor(.2)

    new_verkey = SimpleSigner().verkey
    idy.verkey = new_verkey
    submit_id_req(idy)
    looper.runFor(.2)

    idy.verkey = verkey
    submit_id_req(idy)
    looper.runFor(.2)

    waitForSufficientRepliesForRequests(looper,
                                        trustee,
                                        requests=all_reqs,
                                        add_delay_to_timeout=5)

    # Number of uncommitted entries is 0
    looper.run(eventually(check_uncommitted, 0))

    check_verkey(new_idr, verkey)
    monkeypatch.undo()

    # Delay COMMITs so that IdrCache can be checked for correct
    # number of entries

    uncommitteds = {}
    methods = {}
    for node in nodeSet:
        node.nodeIbStasher.delay(delay_commits)

        cache = node.reqHandler.idrCache
        uncommitteds[cache._name] = []

        cre = cache.currentBatchCreated
        com = cache.onBatchCommitted
        methods[cache._name] = (cre, com)

        # Patch methods to record and check roots after commit

        def patched_cre(self, stateRoot):
            uncommitteds[self._name].append(stateRoot)
            return methods[self._name][0](stateRoot)

        def patched_com(self, stateRoot):
            assert uncommitteds[self._name][0] == stateRoot
            rv = methods[self._name][1](stateRoot)
            uncommitteds[self._name] = uncommitteds[self._name][1:]
            return rv

        cache.currentBatchCreated = types.MethodType(patched_cre, cache)
        cache.onBatchCommitted = types.MethodType(patched_com, cache)

    # Set verkey of multiple identities
    more = 5
    keys = {}
    for _ in range(more):
        idy = new_identity()
        keys[idy.identifier] = idy.verkey
        submit_id_req(idy)
        looper.runFor(.01)

    # Correct number of uncommitted entries
    looper.run(eventually(check_uncommitted, more, retryWait=1))

    waitForSufficientRepliesForRequests(looper,
                                        trustee,
                                        requests=all_reqs,
                                        add_delay_to_timeout=10)

    # Number of uncommitted entries is 0
    looper.run(eventually(check_uncommitted, 0))

    # The verkeys are correct
    for i, v in keys.items():
        check_verkey(i, v)

    waitNodeDataEquality(looper, nodeSet[0], *nodeSet[1:])

    keys = {}
    for _ in range(3):
        idy = new_identity()
        keys[idy.identifier] = idy.verkey
        submit_id_req(idy)
        looper.runFor(.01)

    # Correct number of uncommitted entries
    looper.run(eventually(check_uncommitted, 3, retryWait=1))

    # Check batch reject
    for node in nodeSet:
        cache = node.reqHandler.idrCache
        initial = cache.unCommitted
        cache.batchRejected()
        # After reject, last entry is removed
        assert cache.unCommitted == initial[:-1]
        root = cache.unCommitted[0][0]
        cache.onBatchCommitted(root)
        # Calling commit with same root results in Assertion error
        with pytest.raises(AssertionError):
            cache.onBatchCommitted(root)
def test_unstash_three_phase_msg_after_catchup_in_view_change(
        txnPoolNodeSet, looper, tconf, sdk_pool_handle, sdk_wallet_steward):
    """
    1. Delay Commit on Node4
    2. Order 1 req
    3. Delay Commit on all nodes
    4. Order 1 req
    5. Delay CatchupRep on Node4
    6. Delay Ledger Status and ViewChangeDones on Nodes1-3
    7. Start View change on all nodes
    8. Wait until Node4 got 3 stashed CatchupReps
    9. Reset delaying of Commits on all Nodes
    10. Reset Ledger Status on Nodes1-3
    11. Check that 3 nodes finished VC while Node4 is syncing and not finished
    12. Reset CatchupRep on Node4
    13. Check that Node4 finished VC, and there was just 1 round of cacth-up (edited)
    """
    slow_node = txnPoolNodeSet[-1]
    fast_nodes = txnPoolNodeSet[:-1]
    view_no = txnPoolNodeSet[0].viewNo
    old_stashed = slow_node.master_replica.stasher.num_stashed_future_view
    last_ordered = txnPoolNodeSet[0].master_replica.last_ordered_3pc

    with delay_rules(
        [n.nodeIbStasher for n in txnPoolNodeSet],
            msg_rep_delay(types_to_delay=[PREPREPARE, PREPARE, COMMIT])):

        # Delay Commit messages for slow_node.
        slow_node.nodeIbStasher.delay(cDelay(sys.maxsize))
        sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                                  sdk_wallet_steward, 1)

        # Delay Commit messages for fast_nodes.
        for n in fast_nodes:
            n.nodeIbStasher.delay(cDelay(sys.maxsize))

        request2 = sdk_send_random_request(looper, sdk_pool_handle,
                                           sdk_wallet_steward)

        def check_commits(commit_key):
            for n in fast_nodes:
                for r in n.replicas.values():
                    assert commit_key in r.commits
                    assert len(r.commits[commit_key].voters) == 1

        looper.run(eventually(check_commits, (view_no, last_ordered[1] + 2)))

        # Delay CatchupRep messages for the slow_node.
        with delay_rules([slow_node.nodeIbStasher], cr_delay()):
            with delay_rules([n.nodeIbStasher for n in fast_nodes],
                             vcd_delay()):
                with delay_rules(
                    [n.nodeIbStasher for n in fast_nodes],
                        msg_rep_delay(types_to_delay=[LEDGER_STATUS])):

                    for n in txnPoolNodeSet:
                        n.view_changer.on_master_degradation()
                    looper.run(
                        eventually(lambda: assertExp(slow_node.mode == Mode.
                                                     discovering)))

                    # Reset delay Commit messages for all nodes.
                    for n in txnPoolNodeSet:
                        n.nodeIbStasher.reset_delays_and_process_delayeds(
                            COMMIT)

                    assert slow_node.view_change_in_progress
                    assert slow_node.mode == Mode.discovering
                    looper.run(
                        eventually(_check_nodes_stashed, fast_nodes,
                                   old_stashed,
                                   len(txnPoolNodeSet) - 1))
                    looper.run(
                        eventually(_check_nodes_stashed, [slow_node],
                                   old_stashed, (len(txnPoolNodeSet) - 1) * 2))

            waitForViewChange(looper,
                              fast_nodes,
                              expectedViewNo=view_no + 1,
                              customTimeout=2 * tconf.VIEW_CHANGE_TIMEOUT)
            ensureElectionsDone(looper=looper,
                                nodes=fast_nodes,
                                instances_list=range(
                                    fast_nodes[0].requiredNumberOfInstances),
                                customTimeout=2 * tconf.VIEW_CHANGE_TIMEOUT)
        sdk_get_and_check_replies(looper, [request2])
        waitForViewChange(looper, [slow_node],
                          expectedViewNo=view_no + 1,
                          customTimeout=2 * tconf.VIEW_CHANGE_TIMEOUT)
        ensureElectionsDone(looper=looper, nodes=txnPoolNodeSet)
        _check_nodes_stashed(fast_nodes, old_stashed, 0)
        assert all(n.master_replica.last_ordered_3pc == (last_ordered[0],
                                                         last_ordered[1] + 2)
                   for n in txnPoolNodeSet)
        assert slow_node.catchup_rounds_without_txns == 1
Exemple #55
0
def test_checkpoint_across_views(sent_batches, chkFreqPatched, looper,
                                 txnPoolNodeSet, sdk_pool_handle,
                                 sdk_wallet_client):
    """
    Test checkpointing across views.
    This test checks that checkpointing and garbage collection works correctly
    no matter if view change happened before a checkpoint or after a checkpoint
    """
    batch_size = 2
    send_batches_of_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                                     sdk_wallet_client,
                                     batch_size * sent_batches, sent_batches)

    # Check that correct garbage collection happens
    non_gced_batch_count = (
        sent_batches - CHK_FREQ) if sent_batches >= CHK_FREQ else sent_batches
    looper.run(
        eventually(checkRequestCounts,
                   txnPoolNodeSet,
                   batch_size * non_gced_batch_count,
                   non_gced_batch_count,
                   non_gced_batch_count,
                   retryWait=1))

    ensure_view_change(looper, txnPoolNodeSet)
    ensureElectionsDone(looper=looper, nodes=txnPoolNodeSet)
    ensure_all_nodes_have_same_data(looper, nodes=txnPoolNodeSet)

    # Check that after view change, proper clean up is done
    for node in txnPoolNodeSet:
        for r in node.replicas:
            assert not r.checkpoints
            # No stashed checkpoint for previous view
            assert not [
                view_no
                for view_no in r.stashedRecvdCheckpoints if view_no < r.viewNo
            ]
            assert r._h == 0
            assert r._lastPrePrepareSeqNo == 0
            assert r.h == 0
            assert r.H == r._h + chkFreqPatched.LOG_SIZE

    checkRequestCounts(txnPoolNodeSet, 0, 0, 0)

    # Even after view change, chekpointing works
    send_batches_of_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                                     sdk_wallet_client,
                                     batch_size * sent_batches, sent_batches)

    looper.run(
        eventually(checkRequestCounts,
                   txnPoolNodeSet,
                   batch_size * non_gced_batch_count,
                   non_gced_batch_count,
                   non_gced_batch_count,
                   retryWait=1))

    # Send more batches so one more checkpoint happens. This is done so that
    # when this test finishes, all requests are garbage collected and the
    # next run of this test (with next param) has the calculations correct
    more = CHK_FREQ - non_gced_batch_count
    send_batches_of_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                                     sdk_wallet_client, batch_size * more,
                                     more)
    looper.run(
        eventually(checkRequestCounts, txnPoolNodeSet, 0, 0, 0, retryWait=1))
Exemple #56
0
def test_node_requests_missing_preprepares_prepares_and_commits(
        looper, txnPoolNodeSet, sdk_wallet_client, sdk_pool_handle, tdir,
        allPluginsPath):
    """
    1 of 4 nodes goes down ((simulate this by dropping requests)). A new request comes in and is ordered by
    the 3 remaining nodes. After a while the previously disconnected node
    comes back alive. Another request comes in. Check that the previously
    disconnected node requests missing PREPREPARES, PREPARES and COMMITS,
    orders the previous request and all the nodes successfully handles
    the last request.
    """
    INIT_REQS_CNT = 5
    MISSING_REQS_CNT = 4
    REQS_AFTER_RECONNECT_CNT = 1
    disconnected_node = txnPoolNodeSet[3]
    alive_nodes = txnPoolNodeSet[:3]
    disconnected_node_stashers = disconnected_node.nodeIbStasher

    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                              sdk_wallet_client, INIT_REQS_CNT)
    init_ledger_size = txnPoolNodeSet[0].domainLedger.size

    with delay_rules_without_processing(disconnected_node_stashers,
                                        delay_3pc()):
        last_ordered_key = txnPoolNodeSet[0].master_replica.last_ordered_3pc
        sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                                  sdk_wallet_client, MISSING_REQS_CNT)
        looper.run(
            eventually(check_pp_out_of_sync,
                       alive_nodes, [disconnected_node],
                       last_ordered_key,
                       retryWait=1,
                       timeout=expectedPoolGetReadyTimeout(
                           len(txnPoolNodeSet))))

    for node in alive_nodes:
        assert node.domainLedger.size == init_ledger_size + MISSING_REQS_CNT
    # Ensure that the reconnected node has not caught up though
    assert disconnected_node.domainLedger.size == init_ledger_size

    ordering_service = disconnected_node.master_replica._ordering_service
    assert ordering_service.spylog.count(
        OrderingService._request_pre_prepare) == 0
    assert ordering_service.spylog.count(OrderingService._request_prepare) == 0
    assert ordering_service.spylog.count(OrderingService._request_commit) == 0
    assert disconnected_node.master_replica._message_req_service.spylog.count(
        MessageReqService.process_message_rep) == 0
    doOrderTimesBefore = ordering_service.spylog.count(
        OrderingService._do_order)

    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                              sdk_wallet_client, REQS_AFTER_RECONNECT_CNT)
    waitNodeDataEquality(looper, disconnected_node, *alive_nodes)

    assert ordering_service.spylog.count(
        OrderingService._request_pre_prepare) > 0
    assert ordering_service.spylog.count(OrderingService._request_prepare) > 0
    assert ordering_service.spylog.count(OrderingService._request_commit) > 0
    assert disconnected_node.master_replica._message_req_service.spylog.count(
        MessageReqService.process_message_rep) > 0
    doOrderTimesAfter = ordering_service.spylog.count(
        OrderingService._do_order)
    # Ensure that the reconnected node has ordered both the missed 3PC-batch and the new 3PC-batch
    assert doOrderTimesAfter - doOrderTimesBefore == 2

    for node in txnPoolNodeSet:
        assert node.domainLedger.size == (init_ledger_size + MISSING_REQS_CNT +
                                          REQS_AFTER_RECONNECT_CNT)

    def check_all_ordered():
        for node in txnPoolNodeSet:
            assert node.domainLedger.size == (init_ledger_size +
                                              MISSING_REQS_CNT +
                                              REQS_AFTER_RECONNECT_CNT)

    looper.run(eventually(check_all_ordered, timeout=20))
def test_slow_node_reverts_unordered_state_during_catchup(
        looper, txnPoolNodeSet, sdk_pool_handle, sdk_wallet_client):
    """
    Delay COMMITs to a node such that when it needs to catchup, it needs to
    revert some unordered state. Also till this time the node should have
    receive all COMMITs such that it will apply some of the COMMITs (
    for which it has not received txns from catchup).
    For this delay COMMITs by long, do catchup for a little older than the
    state received in LedgerStatus, once catchup completes, reset delays and
    try to process delayed COMMITs, some COMMITs will be rejected but some will
    be processed since catchup was done for older ledger.
    """
    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                              sdk_wallet_client, 3 * Max3PCBatchSize)
    nprs = getNonPrimaryReplicas(txnPoolNodeSet, 0)
    slow_node = nprs[-1].node
    other_nodes = [n for n in txnPoolNodeSet if n != slow_node]
    slow_master_replica = slow_node.master_replica

    commit_delay = 150
    catchup_rep_delay = 25

    # Delay COMMITs to one node
    slow_node.nodeIbStasher.delay(cDelay(commit_delay, 0))
    # Make the slow node receive txns for a smaller ledger so it still finds
    # the need to catchup
    delay_batches = 2
    make_a_node_catchup_twice(slow_node, other_nodes, DOMAIN_LEDGER_ID,
                              delay_batches * Max3PCBatchSize)

    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                              sdk_wallet_client, 6 * Max3PCBatchSize)
    ensure_all_nodes_have_same_data(looper, other_nodes)
    waitNodeDataInequality(looper, slow_node, *other_nodes)

    def is_catchup_needed_count():
        return len(
            getAllReturnVals(slow_node,
                             slow_node.is_catchup_needed,
                             compare_val_to=True))

    old_lcu_count = slow_node.spylog.count(slow_node.allLedgersCaughtUp)
    old_cn_count = is_catchup_needed_count()

    # `slow_node` is slow to receive CatchupRep, so that it
    # gets a chance to order COMMITs
    slow_node.nodeIbStasher.delay(cr_delay(catchup_rep_delay))

    ensure_view_change(looper, txnPoolNodeSet)

    # Check last ordered of `other_nodes` is same
    for n1, n2 in combinations(other_nodes, 2):
        lst_3pc = check_last_ordered_3pc(n1, n2)

    def chk1():
        # `slow_node` has prepared all 3PC messages which
        # `other_nodes` have ordered
        assertEquality(slow_master_replica.last_prepared_before_view_change,
                       lst_3pc)

    looper.run(eventually(chk1, retryWait=1))

    old_pc_count = slow_master_replica.spylog.count(
        slow_master_replica.can_process_since_view_change_in_progress)

    # Repair the network so COMMITs are received and processed
    slow_node.reset_delays_and_process_delayeds(COMMIT)

    def chk2():
        # COMMITs are processed for prepared messages
        assert slow_master_replica.spylog.count(
            slow_master_replica.can_process_since_view_change_in_progress
        ) > old_pc_count

    looper.run(eventually(chk2, retryWait=1, timeout=5))

    def chk3():
        # Some COMMITs were ordered but stashed and they were processed
        rv = getAllReturnVals(slow_node, slow_node.processStashedOrderedReqs)
        assert delay_batches in rv

    looper.run(eventually(chk3, retryWait=1, timeout=catchup_rep_delay + 5))

    def chk4():
        # Catchup was done once
        assert slow_node.spylog.count(
            slow_node.allLedgersCaughtUp) > old_lcu_count

    looper.run(
        eventually(chk4,
                   retryWait=1,
                   timeout=waits.expectedPoolCatchupTime(len(txnPoolNodeSet))))

    def chk5():
        # Once catchup was done, need of other catchup was not found
        assertEquality(is_catchup_needed_count(), old_cn_count)

    looper.run(eventually(chk5, retryWait=1, timeout=5))

    checkProtocolInstanceSetup(looper, txnPoolNodeSet, retryWait=1)
    ensure_all_nodes_have_same_data(looper, nodes=txnPoolNodeSet)
    sdk_send_random_and_check(looper, txnPoolNodeSet, sdk_pool_handle,
                              sdk_wallet_client, 2 * Max3PCBatchSize)
    ensure_all_nodes_have_same_data(looper, nodes=txnPoolNodeSet)
Exemple #58
0
def test_process_three_phase_msg_and_stashed_for_next_checkpoint(txnPoolNodeSet,
                                                                 looper,
                                                                 sdk_pool_handle,
                                                                 sdk_wallet_client,
                                                                 chkFreqPatched):
    """
    1. Delay checkpoints processing on the slow_node. That is checkpoint on this node
    can't be finalized.
    2. Order requests for finalize checkpoints.
    3. Check that a checkpoint is finalized on all nodes exclude the slow_node.
    4. Order a new request.
    5. Check that slow_node could not order this request and stashed all 3pc messages.
    6. Reset delays.
    7. Check that the last request is ordered on the slow_node, checkpoint is finalized
    and stashed messages were removed.
    """

    for n in txnPoolNodeSet:
        for r in n.replicas.values():
            r._checkpointer.update_watermark_from_3pc()

    slow_node = txnPoolNodeSet[-1]
    fast_nodes = txnPoolNodeSet[:-1]

    old_stashed = {inst_id: r.stasher.stash_size(STASH_WATERMARKS)
                   for inst_id, r in slow_node.replicas.items()}
    last_ordered = {inst_id: r.last_ordered_3pc
                    for inst_id, r in slow_node.replicas.items()}
    with delay_rules([slow_node.nodeIbStasher, ],
                     msg_rep_delay(types_to_delay=[PREPREPARE, PREPARE, COMMIT])):
        with delay_rules([slow_node.nodeIbStasher, ], chk_delay()):
            sdk_send_batches_of_random_and_check(looper,
                                                 txnPoolNodeSet,
                                                 sdk_pool_handle,
                                                 sdk_wallet_client,
                                                 num_reqs=1 * CHK_FREQ,
                                                 num_batches=CHK_FREQ)
            ensure_all_nodes_have_same_data(looper, nodes=txnPoolNodeSet)
            looper.run(eventually(_check_checkpoint_finalize,
                                  fast_nodes,
                                  CHK_FREQ))
            sdk_send_random_and_check(looper,
                                      txnPoolNodeSet,
                                      sdk_pool_handle,
                                      sdk_wallet_client,
                                      1)

            stashed_messages = incoming_3pc_msgs_count(len(txnPoolNodeSet))
            assert all(r.stasher.stash_size(STASH_WATERMARKS) == old_stashed[inst_id] + stashed_messages
                       for inst_id, r in slow_node.replicas.items())

            _check_batches_ordered(slow_node, last_ordered, CHK_FREQ)
            for n in fast_nodes:
                _check_batches_ordered(n, last_ordered, CHK_FREQ + 1)

        looper.run(eventually(_check_checkpoint_finalize,
                              [slow_node, ],
                              CHK_FREQ))
        looper.run(eventually(_check_batches_ordered,
                              slow_node, last_ordered, CHK_FREQ + 1))
        assert all(r.stasher.stash_size(STASH_WATERMARKS) == old_stashed[inst_id]
                   for inst_id, r in slow_node.replicas.items())
Exemple #59
0
def test_replay_new_bouncing(txnPoolNodesLooper, txnPoolNodeSet, tconf, tdir,
                             testNodeClass, tmpdir_factory,
                             node_config_helper_class, allPluginsPath,
                             some_txns_done, sdk_pool_handle, sdk_wallet_client):
    alpha = txnPoolNodeSet[0]
    old_view_no = alpha.viewNo
    other_nodes = txnPoolNodeSet[1:]
    stopping_at = time.perf_counter()
    alpha.stop()
    txnPoolNodesLooper.removeProdable(alpha)

    txnPoolNodesLooper.run(eventually(checkViewNoForNodes, other_nodes,
                                      old_view_no + 1, retryWait=1, timeout=30))

    sdk_send_random_and_check(txnPoolNodesLooper, other_nodes,
                              sdk_pool_handle,
                              sdk_wallet_client, 10)
    ensure_all_nodes_have_same_data(txnPoolNodesLooper, other_nodes)

    for node in other_nodes:
        assert alpha.name not in node.nodestack.connecteds

    nodeHa, nodeCHa = HA(*alpha.nodestack.ha), HA(*alpha.clientstack.ha)
    config_helper = PNodeConfigHelper(alpha.name, tconf, chroot=tdir)

    restarted_alpha = testNodeClass(
        alpha.name,
        config_helper=config_helper,
        config=tconf,
        ha=nodeHa,
        cliha=nodeCHa,
        pluginPaths=allPluginsPath)

    txnPoolNodesLooper.add(restarted_alpha)
    txnPoolNodeSet[0] = restarted_alpha

    restarting_at = time.perf_counter()
    print('Stopped for {}'.format(restarting_at - stopping_at))

    sdk_send_random_and_check(txnPoolNodesLooper, txnPoolNodeSet,
                              sdk_pool_handle,
                              sdk_wallet_client, 10)
    ensure_all_nodes_have_same_data(txnPoolNodesLooper, txnPoolNodeSet)

    for node in txnPoolNodeSet:
        node.stop()
        txnPoolNodesLooper.removeProdable(node)

    config = getConfigOnce()

    reload_modules_for_replay(tconf)

    replayable_node_class, basedirpath = get_replayable_node_class(
        tmpdir_factory, tdir, testNodeClass, config)

    print('-------------Replaying now---------------------')

    create_replayable_node_and_check(txnPoolNodesLooper, txnPoolNodeSet,
                                     restarted_alpha, replayable_node_class,
                                     node_config_helper_class, tconf,
                                     basedirpath, allPluginsPath)

    # For disconnections
    for node in other_nodes:
        create_replayable_node_and_check(txnPoolNodesLooper, txnPoolNodeSet,
                                         node, replayable_node_class,
                                         node_config_helper_class, tconf,
                                         basedirpath, allPluginsPath)
Exemple #60
0
def testManual(do, be, poolNodesStarted, poolTxnStewardData, philCLI,
               connectedToTest, nymAddedOut, attrAddedOut, schemaAdded,
               issuerKeyAdded, aliceCLI, newKeyringOut, aliceMap, tdir,
               syncLinkOutWithEndpoint, jobCertificateClaimMap,
               syncedInviteAcceptedOutWithoutClaims, transcriptClaimMap,
               reqClaimOut, reqClaimOut1, susanCLI, susanMap):
    eventually.slowFactor = 3

    # Create steward and add nyms and endpoint atttributes of all agents
    _, stewardSeed = poolTxnStewardData
    be(philCLI)
    do('new keyring Steward',
       expect=[
           'New keyring Steward created', 'Active keyring set to "Steward"'
       ])

    mapper = {'seed': stewardSeed.decode()}
    do('new key with seed {seed}',
       expect=['Key created in keyring Steward'],
       mapper=mapper)
    do('connect test', within=3, expect=connectedToTest)

    # Add nym and endpoint for Faber, Acme and Thrift
    agentIpAddress = "127.0.0.1"
    faberAgentPort = 5555
    acmeAgentPort = 6666
    thriftAgentPort = 7777

    faberHa = "{}:{}".format(agentIpAddress, faberAgentPort)
    acmeHa = "{}:{}".format(agentIpAddress, acmeAgentPort)
    thriftHa = "{}:{}".format(agentIpAddress, thriftAgentPort)
    faberId = 'FuN98eH2eZybECWkofW6A9BKJxxnTatBCopfUiNxo6ZB'
    acmeId = '7YD5NKn3P4wVJLesAmA1rr7sLPqW9mR1nhFdKD518k21'
    thriftId = '9jegUr9vAMqoqQQUEAiCBYNQDnUbTktQY9nNspxfasZW'
    faberPk = '5hmMA64DDQz5NzGJNVtRzNwpkZxktNQds21q3Wxxa62z'
    acmePk = 'C5eqjU7NMVMGGfGfx2ubvX5H9X346bQt5qeziVAo3naQ'
    thriftPk = 'AGBjYvyM3SFnoiDGAEzkSLHvqyzVkXeMZfKDvdpEsC2x'
    for nym, ha, pk in [(faberId, faberHa, faberPk), (acmeId, acmeHa, acmePk),
                        (thriftId, thriftHa, thriftPk)]:
        m = {
            'target': nym,
            'endpoint': json.dumps({ENDPOINT: {
                'ha': ha,
                PUBKEY: pk
            }})
        }
        do('send NYM dest={{target}} role={role}'.format(
            role=Roles.TRUST_ANCHOR.name),
           within=5,
           expect=nymAddedOut,
           mapper=m)
        do('send ATTRIB dest={target} raw={endpoint}',
           within=5,
           expect=attrAddedOut,
           mapper=m)

    # Start Faber Agent and Acme Agent

    fMap = faberMap(agentIpAddress, faberAgentPort)
    aMap = acmeMap(agentIpAddress, acmeAgentPort)
    tMap = thriftMap(agentIpAddress, thriftAgentPort)

    agentParams = [
        (FaberAgent, "Faber College", faberAgentPort, buildFaberWallet),
        (AcmeAgent, "Acme Corp", acmeAgentPort, buildAcmeWallet),
        (ThriftAgent, "Thrift Bank", thriftAgentPort, buildThriftWallet)
    ]

    for agentCls, agentName, agentPort, buildAgentWalletFunc in \
            agentParams:
        agentCls.getPassedArgs = lambda _: (agentPort, False)
        TestWalletedAgent.createAndRunAgent(agentName, agentCls,
                                            buildAgentWalletFunc(), tdir,
                                            agentPort, philCLI.looper,
                                            TestClient)

    for p in philCLI.looper.prodables:
        if p.name == 'Faber College':
            faberAgent = p
        if p.name == 'Acme Corp':
            acmeAgent = p
        if p.name == 'Thrift Bank':
            thriftAgent = p

    async def checkTranscriptWritten():
        faberId = faberAgent.wallet.defaultId
        schemaId = ID(SchemaKey("Transcript", "1.2", faberId))
        schema = await faberAgent.issuer.wallet.getSchema(schemaId)
        assert schema
        assert schema.seqId

        issuerKey = faberAgent.issuer.wallet.getPublicKey(schemaId)
        assert issuerKey  # TODO isinstance(issuerKey, PublicKey)

    async def checkJobCertWritten():
        acmeId = acmeAgent.wallet.defaultId
        schemaId = ID(SchemaKey("Job-Certificate", "0.2", acmeId))
        schema = await acmeAgent.issuer.wallet.getSchema(schemaId)
        assert schema
        assert schema.seqId

        issuerKey = await acmeAgent.issuer.wallet.getPublicKey(schemaId)
        assert issuerKey
        assert issuerKey.seqId

    philCLI.looper.run(eventually(checkTranscriptWritten, timeout=10))
    philCLI.looper.run(eventually(checkJobCertWritten, timeout=10))

    # Defining inner method for closures
    def executeGstFlow(name, userCLI, userMap, be, connectedToTest, do, fMap,
                       aMap, jobCertificateClaimMap, newKeyringOut,
                       reqClaimOut, reqClaimOut1, syncLinkOutWithEndpoint,
                       syncedInviteAcceptedOutWithoutClaims, tMap,
                       transcriptClaimMap):
        pass

        async def getPublicKey(wallet, schemaId):
            return await wallet.getPublicKey(schemaId)

        async def getClaim(schemaId):
            return await userCLI.agent.prover.wallet.getClaims(schemaId)

        # Start User cli

        be(userCLI)
        setPromptAndKeyring(do, name, newKeyringOut, userMap)
        do('connect test', within=3, expect=connectedToTest)
        # Accept faber
        do('load sample/faber-invitation.sovrin')
        syncInvite(be, do, userCLI, syncLinkOutWithEndpoint, fMap)
        do('show link faber')
        acceptInvitation(be, do, userCLI, fMap,
                         syncedInviteAcceptedOutWithoutClaims)
        # Request claim
        do('show claim Transcript')
        aliceRequestedTranscriptClaim(
            be,
            do,
            userCLI,
            transcriptClaimMap,
            reqClaimOut,
            None,  # Passing None since its not used
            None)  # Passing None since its not used

        faberSchemaId = ID(SchemaKey('Transcript', '1.2', fMap['target']))
        faberIssuerKey = userCLI.looper.run(
            getPublicKey(faberAgent.issuer.wallet, faberSchemaId))
        userFaberIssuerKey = userCLI.looper.run(
            getPublicKey(userCLI.agent.prover.wallet, faberSchemaId))
        assert faberIssuerKey == userFaberIssuerKey

        do('show claim Transcript')
        assert userCLI.looper.run(getClaim(faberSchemaId))

        # Accept acme
        do('load sample/acme-job-application.sovrin')
        syncInvite(be, do, userCLI, syncLinkOutWithEndpoint, aMap)
        acceptInvitation(be, do, userCLI, aMap,
                         syncedInviteAcceptedOutWithoutClaims)
        # Send claim
        do('show claim request Job-Application')
        do('set first_name to Alice')
        do('set last_name to Garcia')
        do('set phone_number to 123-45-6789')
        do('show claim request Job-Application')
        # Passing some args as None since they are not used in the method
        jobApplicationProofSent(be, do, userCLI, aMap, None, None, None)
        do('show claim Job-Certificate')
        # Request new available claims Job-Certificate
        jobCertClaimRequested(be, do, userCLI, None, jobCertificateClaimMap,
                              reqClaimOut1, None)

        acmeSchemaId = ID(SchemaKey('Job-Certificate', '0.2', aMap['target']))
        acmeIssuerKey = userCLI.looper.run(
            getPublicKey(acmeAgent.issuer.wallet, acmeSchemaId))
        userAcmeIssuerKey = userCLI.looper.run(
            getPublicKey(userCLI.agent.prover.wallet, acmeSchemaId))
        assert acmeIssuerKey == userAcmeIssuerKey

        do('show claim Job-Certificate')
        assert userCLI.looper.run(getClaim(acmeSchemaId))

        # Accept thrift
        do('load sample/thrift-loan-application.sovrin')
        acceptInvitation(be, do, userCLI, tMap,
                         syncedInviteAcceptedOutWithoutClaims)
        # Send claims
        bankBasicClaimSent(be, do, userCLI, tMap, None)

        thriftAcmeIssuerKey = userCLI.looper.run(
            getPublicKey(thriftAgent.issuer.wallet, acmeSchemaId))
        assert acmeIssuerKey == thriftAcmeIssuerKey
        passed = False
        try:
            bankKYCProofSent(be, do, userCLI, tMap, None)
            passed = True
        except:
            thriftFaberIssuerKey = userCLI.looper.run(
                getPublicKey(thriftAgent.issuer.wallet, faberSchemaId))
            assert faberIssuerKey == thriftFaberIssuerKey
        assert passed

    executeGstFlow("Alice", aliceCLI, aliceMap, be, connectedToTest, do, fMap,
                   aMap, jobCertificateClaimMap, newKeyringOut, reqClaimOut,
                   reqClaimOut1, syncLinkOutWithEndpoint,
                   syncedInviteAcceptedOutWithoutClaims, tMap,
                   transcriptClaimMap)

    aliceCLI.looper.runFor(3)

    executeGstFlow("Susan", susanCLI, susanMap, be, connectedToTest, do, fMap,
                   aMap, jobCertificateClaimMap, newKeyringOut, reqClaimOut,
                   reqClaimOut1, syncLinkOutWithEndpoint,
                   syncedInviteAcceptedOutWithoutClaims, tMap,
                   transcriptClaimMap)