def canProcessPrePrepare(self, pp: PrePrepare, sender: str) -> bool: """ Decide whether this replica is eligible to process a PRE-PREPARE, based on the following criteria: - this replica is non-primary replica - the request isn't in its list of received PRE-PREPAREs - the request is waiting to for PRE-PREPARE and the digest value matches :param pp: a PRE-PREPARE msg to process :param sender: the name of the node that sent the PRE-PREPARE msg :return: True if processing is allowed, False otherwise """ # TODO: Check whether it is rejecting PRE-PREPARE from previous view # PRE-PREPARE should not be sent from non primary if not self.isMsgFromPrimary(pp, sender): raise SuspiciousNode(sender, Suspicions.PPR_FRM_NON_PRIMARY, pp) # A PRE-PREPARE is being sent to primary if self.isPrimaryForMsg(pp) is True: raise SuspiciousNode(sender, Suspicions.PPR_TO_PRIMARY, pp) # A PRE-PREPARE is sent that has already been received if (pp.viewNo, pp.ppSeqNo) in self.prePrepares: raise SuspiciousNode(sender, Suspicions.DUPLICATE_PPR_SENT, pp) key = (pp.identifier, pp.reqId) # A PRE-PREPARE is sent that does not match request digest if (key in self.reqsPendingPrePrepare and self.reqsPendingPrePrepare[key] != pp.digest): raise SuspiciousNode(sender, Suspicions.PPR_DIGEST_WRONG, pp) return True
def isValidCommit(self, commit: Commit, sender: str) -> bool: """ Return whether the COMMIT specified is valid. :param commit: the COMMIT to validate :return: True if `request` is valid, False otherwise """ primaryStatus = self.isPrimaryForMsg(commit) ppReqs = self.sentPrePrepares if primaryStatus else self.prePrepares key = (commit.viewNo, commit.ppSeqNo) if key not in ppReqs: self.enqueueCommit(commit, sender) return False if (key not in self.prepares and key not in self.preparesWaitingForPrePrepare): logger.debug( "{} rejecting COMMIT{} due to lack of prepares".format( self, key)) # raise SuspiciousNode(sender, Suspicions.UNKNOWN_CM_SENT, commit) return False elif self.commits.hasCommitFrom(commit, sender): raise SuspiciousNode(sender, Suspicions.DUPLICATE_CM_SENT, commit) elif commit.digest != self.getDigestFor3PhaseKey(ThreePhaseKey(*key)): raise SuspiciousNode(sender, Suspicions.CM_DIGEST_WRONG, commit) elif key in ppReqs and commit.ppTime != ppReqs[key][1]: raise SuspiciousNode(sender, Suspicions.CM_TIME_WRONG, commit) else: return True
def test_process_pre_prepare_with_incorrect_audit_txn_root(orderer_with_requests, state_roots, txn_roots, multi_sig, fake_requests): if not orderer_with_requests.is_master: return handler = Mock() orderer_with_requests._bus.subscribe(RaisedSuspicion, handler) pre_prepare_params = create_pre_prepare_params(state_root=state_roots[DOMAIN_LEDGER_ID], ledger_id=DOMAIN_LEDGER_ID, txn_root=txn_roots[DOMAIN_LEDGER_ID], bls_multi_sig=multi_sig, view_no=orderer_with_requests.view_no, inst_id=0, pool_state_root=state_roots[POOL_LEDGER_ID], # INVALID! audit_txn_root="HSai3sMHKeAva4gWMabDrm1yNhezvPHfXnGyHf2ex1L4", reqs=fake_requests, pp_seq_no=1) pre_prepare = PrePrepare(*pre_prepare_params) _register_pp_ts(orderer_with_requests, pre_prepare, orderer_with_requests.primary_name) orderer_with_requests.process_preprepare(pre_prepare, orderer_with_requests.primary_name) check_suspicious(handler, RaisedSuspicion(inst_id=orderer_with_requests._data.inst_id, ex=SuspiciousNode(orderer_with_requests.primary_name, Suspicions.PPR_AUDIT_TXN_ROOT_HASH_WRONG, pre_prepare)))
def test_suspicious_on_wrong_list_of_primaries(orderer_with_requests, state_roots, txn_roots, multi_sig, fake_requests): if not orderer_with_requests.is_master: return handler = Mock() orderer_with_requests._bus.subscribe(RaisedSuspicion, handler) pre_prepare_params = create_pre_prepare_params(state_root=state_roots[DOMAIN_LEDGER_ID], ledger_id=DOMAIN_LEDGER_ID, txn_root=txn_roots[DOMAIN_LEDGER_ID], bls_multi_sig=multi_sig, view_no=orderer_with_requests.view_no, inst_id=0, pool_state_root=state_roots[POOL_LEDGER_ID], audit_txn_root=state_roots[AUDIT_LEDGER_ID], reqs=fake_requests, pp_seq_no=1, primaries=["Some", "Other", "Primaries"]) pre_prepare = PrePrepare(*pre_prepare_params) _register_pp_ts(orderer_with_requests, pre_prepare, orderer_with_requests.primary_name) orderer_with_requests.process_preprepare(pre_prepare, orderer_with_requests.primary_name) check_suspicious(handler, RaisedSuspicion(inst_id=orderer_with_requests._data.inst_id, ex=SuspiciousNode(orderer_with_requests.primary_name, Suspicions.PPR_WITH_WRONG_PRIMARIES, pre_prepare)))
def test_validate_prepare_from_primary(o, prepare): handler = Mock() o._bus.subscribe(RaisedSuspicion, handler) o._validate_prepare(prepare, PRIMARY_NAME) check_suspicious( handler, RaisedSuspicion(inst_id=o._data.inst_id, ex=SuspiciousNode(PRIMARY_NAME, Suspicions.PR_FRM_PRIMARY, prepare)))
def isValidPrepare(self, prepare: Prepare, sender: str) -> bool: """ Return whether the PREPARE specified is valid. :param prepare: the PREPARE to validate :param sender: the name of the node that sent the PREPARE :return: True if PREPARE is valid, False otherwise """ key = (prepare.viewNo, prepare.ppSeqNo) primaryStatus = self.isPrimaryForMsg(prepare) ppReqs = self.sentPrePrepares if primaryStatus else self.prePrepares # If a non primary replica and receiving a PREPARE request before a # PRE-PREPARE request, then proceed # PREPARE should not be sent from primary if self.isMsgFromPrimary(prepare, sender): raise SuspiciousNode(sender, Suspicions.PR_FRM_PRIMARY, prepare) # If non primary replica if primaryStatus is False: if self.prepares.hasPrepareFrom(prepare, sender): raise SuspiciousNode(sender, Suspicions.DUPLICATE_PR_SENT, prepare) # If PRE-PREPARE not received for the PREPARE, might be slow network if key not in ppReqs: self.enqueuePrepare(prepare, sender) return False elif prepare.digest != self.requests.digest(ppReqs[key][0]): raise SuspiciousNode(sender, Suspicions.PR_DIGEST_WRONG, prepare) elif prepare.ppTime != ppReqs[key][1]: raise SuspiciousNode(sender, Suspicions.PR_TIME_WRONG, prepare) else: return True # If primary replica else: if self.prepares.hasPrepareFrom(prepare, sender): raise SuspiciousNode(sender, Suspicions.DUPLICATE_PR_SENT, prepare) # If PRE-PREPARE was not sent for this PREPARE, certainly # malicious behavior elif key not in ppReqs: raise SuspiciousNode(sender, Suspicions.UNKNOWN_PR_SENT, prepare) elif prepare.digest != self.requests.digest(ppReqs[key][0]): raise SuspiciousNode(sender, Suspicions.PR_DIGEST_WRONG, prepare) elif prepare.ppTime != ppReqs[key][1]: raise SuspiciousNode(sender, Suspicions.PR_TIME_WRONG, prepare) else: return True
def test_validate_prepare_wrong_state_root(o, pre_prepare, prepare): handler = Mock() o._bus.subscribe(RaisedSuspicion, handler) o.process_preprepare(pre_prepare, PRIMARY_NAME) prepare = updateNamedTuple(prepare, stateRootHash=generate_state_root()) o._validate_prepare(prepare, NON_PRIMARY_NAME) check_suspicious( handler, RaisedSuspicion(inst_id=o._data.inst_id, ex=SuspiciousNode(NON_PRIMARY_NAME, Suspicions.PR_STATE_WRONG, prepare)))
def test_validate_prepare_wrong_digest(o, pre_prepare, prepare): handler = Mock() o._bus.subscribe(RaisedSuspicion, handler) o.process_preprepare(pre_prepare, PRIMARY_NAME) prepare = updateNamedTuple(prepare, digest='fake_digest') o._validate_prepare(prepare, NON_PRIMARY_NAME) check_suspicious( handler, RaisedSuspicion(inst_id=o._data.inst_id, ex=SuspiciousNode(NON_PRIMARY_NAME, Suspicions.PR_DIGEST_WRONG, prepare)))
def test_validate_duplicate_prepare(o, pre_prepare, prepare): handler = Mock() o._bus.subscribe(RaisedSuspicion, handler) o.process_preprepare(pre_prepare, PRIMARY_NAME) o.process_prepare(prepare, NON_PRIMARY_NAME) o._validate_prepare(prepare, NON_PRIMARY_NAME) check_suspicious( handler, RaisedSuspicion(inst_id=o._data.inst_id, ex=SuspiciousNode(NON_PRIMARY_NAME, Suspicions.DUPLICATE_PR_SENT, prepare)))
def isValidCommit(self, commit: Commit, sender: str): """ Return whether the COMMIT specified is valid. :param commit: the COMMIT to validate :return: True if `request` is valid, False otherwise """ primaryStatus = self.isPrimaryForMsg(commit) ppReqs = self.sentPrePrepares if primaryStatus else self.prePrepares key = (commit.viewNo, commit.ppSeqNo) if (key not in self.prepares and key not in self.preparesWaitingForPrePrepare): raise SuspiciousNode(sender, Suspicions.UNKNOWN_CM_SENT, commit) elif self.commits.hasCommitFrom(commit, sender): raise SuspiciousNode(sender, Suspicions.DUPLICATE_CM_SENT, commit) elif commit.digest != self.getDigestFromPrepare(*key): raise SuspiciousNode(sender, Suspicions.CM_DIGEST_WRONG, commit) elif key in ppReqs and commit.ppTime != ppReqs[key][1]: raise SuspiciousNode(sender, Suspicions.CM_TIME_WRONG, commit) else: return True
def canProcessPrePrepare(self, pp: PrePrepare, sender: str): """ Decide whether this replica is eligible to process a PRE-PREPARE, based on the following criteria: - this replica is non-primary replica - the request isn't in its list of received PRE-PREPAREs - the request is waiting to for PRE-PREPARE and the digest value matches :param pp: a PRE-PREPARE msg to process :param sender: the name of the node that sent the PRE-PREPARE msg :return: True if processing is allowed, False otherwise """ # PRE-PREPARE should not be sent from non primary if not self.isMsgFromPrimary(pp, sender): raise SuspiciousNode(sender, Suspicions.PPR_FRM_NON_PRIMARY, pp) # A PRE-PREPARE is being sent to primary if self.isPrimaryForMsg(pp) is True: raise SuspiciousNode(sender, Suspicions.PPR_TO_PRIMARY, pp) if (pp.viewNo, pp.ppSeqNo) in self.prePrepares: raise SuspiciousNode(sender, Suspicions.DUPLICATE_PPR_SENT, pp) #TODO: Fix this, how to check last preprepare seq num # if self.prePrepares: # lastProcessedPrePrepareSeqNo = max([key[1] for key in self.prePrepares.keys()]) # if pp.ppSeqNo > lastProcessedPrePrepareSeqNo + 1: # raise SuspiciousNode(sender, Suspicions.WRONG_PPSEQ_NO, pp) key = (pp.identifier, pp.reqId) if (key in self.reqsPendingPrePrepare and self.reqsPendingPrePrepare[key] != pp.digest): raise SuspiciousNode(sender, Suspicions.PPR_DIGEST_WRONG, pp) return True
def test_validate_prepare_no_preprepare(o, prepare): # must sent PrePrepare before processing the Prepare if o.name == PRIMARY_NAME: handler = Mock() o._bus.subscribe(RaisedSuspicion, handler) o._validate_prepare(prepare, NON_PRIMARY_NAME) check_suspicious( handler, RaisedSuspicion(inst_id=o._data.inst_id, ex=SuspiciousNode(NON_PRIMARY_NAME, Suspicions.UNKNOWN_PR_SENT, prepare))) # PrePrepare can be delayed, so just enqueue else: o._validate_prepare(prepare, NON_PRIMARY_NAME) assert (prepare, NON_PRIMARY_NAME ) in o.preparesWaitingForPrePrepare[prepare.viewNo, prepare.ppSeqNo]
def test_process_pre_prepare_with_ordered_request(orderer, pre_prepare): handler = Mock() orderer._bus.subscribe(RaisedSuspicion, handler) orderer.db_manager.stores[SEQ_NO_DB_LABEL] = FakeSomething(get_by_full_digest=lambda req: 'sample', get_by_payload_digest=lambda req: (1, 1)) orderer._non_finalised_reqs = lambda a: pre_prepare.reqIdr def request_propagates(reqs): assert False, "Requested propagates for: {}".format(reqs) orderer._bus.subscribe(RequestPropagates, request_propagates) orderer.process_preprepare(pre_prepare, orderer.primary_name) check_suspicious(handler, RaisedSuspicion(inst_id=orderer._data.inst_id, ex=SuspiciousNode(orderer.primary_name, Suspicions.PPR_WITH_ORDERED_REQUEST, pre_prepare)))
def malicious_behaviors(suspicion_code, node_name): if suspicion_code == Suspicions.PPR_STATE_WRONG: raise SuspiciousNode(node_name, suspicion_code, "It looks like ViewChange should be forced " "because Pre-Prepare message has incorrect state trie root")