def add_proposal(self, p, proto=None):
        assert isinstance(p, Proposal)
        assert proto is None or isinstance(proto, HDCProtocol)

        def check(valid):
            if not valid:
                self.tracked_protocol_failures.append(InvalidProposalEvidence(None, p))
                log.warn('invalid proposal', p=p)
                raise InvalidProposalError()
            return True

        self.log('cm.add_proposal', p=p)
        if p.height < self.height:
            self.log('proposal from the past')
            return

        if not check(self.contract.isvalidator(p.sender) and self.contract.isproposer(p)):
            return
        self.ready_validators.add(p.sender)

        if not check(p.lockset.is_valid):
            return
        if not check(p.lockset.height == p.height or p.round == 0):
            return
        if not check(p.round - p.lockset.round == 1 or p.round == 0):
            return

        # proposal is valid
        if proto is not None:  # inactive proto is False
            self.synchronizer.on_proposal(p, proto)

        for v in p.lockset:
            self.add_vote(v)  # implicitly checks their validity
        if isinstance(p, BlockProposal):
            if not check(p.block.number == p.height):
                return
            if not check(p.lockset.has_noquorum or p.round == 0):
                return
            # validation
            if p.height > self.height:
                self.log('proposal from the future, not in sync', p=p)
                return  # note: we are not broadcasting this, as we could not validate
            blk = self.chainservice.link_block(p.block)
            if not check(blk):
                # safeguard for forks:
                # if there is a quorum on a block which can not be applied: panic!
                ls = self.heights[p.height].last_quorum_lockset
                if ls and ls.has_quorum == p.blockhash:
                    raise ForkDetectedEvidence(proto, (self.head, p, ls))
                    sys.exit(1)
                return
            p._mutable = True
            p._cached_rlp = None
            p.block = blk  # block linked to chain
            self.log('successfully linked block')
            self.add_block_proposal(p)  # implicitly checks the votes validity
        else:
            assert isinstance(p, VotingInstruction)
            assert p.lockset.round == 1 - p.round and p.height == p.lockset.height
            assert p.round > 0
            assert p.lockset.has_quorum_possible
            assert not p.lockset.has_quorum
            if not check(p.lockset.has_quorum_possible and not p.lockset.has_quorum):
                return
        is_valid = self.heights[p.height].add_proposal(p)
        return is_valid  # can be broadcasted
dataset/ETHPy150Open HydraChain/hydrachain/hydrachain/consensus/manager.py/ConsensusManager.add_proposal/swapped operands of `-`