Beispiel #1
0
    def verifyMerkleProof(*replies: Tuple[Reply]) -> bool:
        """
        Verifies the correctness of the merkle proof provided in the reply from
        the node. Returns True if verified to be correct, throws an exception
        otherwise.

        :param replies: One or more replies for which Merkle Proofs have to be
        verified
        :raises ProofError: The proof is invalid
        :return: True
        """
        sth = namedtuple("sth", ["tree_size", "sha256_root_hash"])
        verifier = MerkleVerifier()
        serializer = JsonSerializer()
        for r in replies:
            seqNo = r[f.RESULT.nm][F.seqNo.name]
            rootHash = base64.b64decode(
                r[f.RESULT.nm][F.rootHash.name].encode())
            auditPath = [
                base64.b64decode(a.encode())
                for a in r[f.RESULT.nm][F.auditPath.name]
            ]
            filtered = (
                (k, v) for (k, v) in r[f.RESULT.nm].iteritems()
                if k not in [F.auditPath.name, F.seqNo.name, F.rootHash.name])
            result = serializer.serialize(dict(filtered))
            verifier.verify_leaf_inclusion(
                result, seqNo - 1, auditPath,
                sth(tree_size=seqNo, sha256_root_hash=rootHash))
        return True
Beispiel #2
0
    def verifyMerkleProof(*replies: Tuple[Reply]) -> bool:
        """
        Verifies the correctness of the merkle proof provided in the reply from
        the node. Returns True if verified to be correct, throws an exception
        otherwise.

        :param replies: One or more replies for which Merkle Proofs have to be
        verified
        :raises ProofError: The proof is invalid
        :return: True
        """
        sth = namedtuple("sth", ["tree_size", "sha256_root_hash"])
        verifier = MerkleVerifier()
        serializer = JsonSerializer()
        for r in replies:
            seqNo = r[f.RESULT.nm][F.seqNo.name]
            rootHash = base64.b64decode(r[f.RESULT.nm][F.rootHash.name].encode())
            auditPath = [base64.b64decode(
                a.encode()) for a in r[f.RESULT.nm][F.auditPath.name]]
            filtered = ((k, v) for (k, v) in r[f.RESULT.nm].iteritems()
                        if k not in
                        [F.auditPath.name, F.seqNo.name, F.rootHash.name])
            result = serializer.serialize(dict(filtered))
            verifier.verify_leaf_inclusion(result, seqNo-1,
                                           auditPath,
                                           sth(tree_size=seqNo,
                                               sha256_root_hash=rootHash))
        return True
Beispiel #3
0
def testJsonSerializer():
    sz = JsonSerializer()
    m1 = {
        'integer': 36,
        'name': 'Foo',
        'surname': 'Bar',
        'float': 14.8639,
        'index': 1,
        'index_start_at': 56,
        'email': '*****@*****.**',
        'fullname': 'Foo Bar',
        'bool': False
    }
    m1s = '{"bool":false,"email":"*****@*****.**","float":14.8639,"fullname":"Foo Bar","index":1,"index_start_at":56,"integer":36,"name":"Foo","surname":"Bar"}'

    m2 = {
        'latitude': 31.351883,
        'longitude': -97.466179,
        'tags': ['foo', 'bar', 'baz', 'alice', 'bob', 'carol', 'dave']
    }
    m2s = '{"latitude":31.351883,"longitude":-97.466179,"tags":["foo","bar","baz","alice","bob","carol","dave"]}'

    m3 = {
        'name':
        'Alice Bob',
        'website':
        'example.com',
        'friends': [{
            'id': 0,
            'name': 'Dave'
        }, {
            'id': 1,
            'name': 'Carol'
        }, {
            'id': 2,
            'name': 'Dave'
        }]
    }
    m3s = '{"friends":[{"id":0,"name":"Dave"},{"id":1,"name":"Carol"},{"id":2,"name":"Dave"}],"name":"Alice Bob","website":"example.com"}'

    assert sz.serialize(m1) == m1s.encode()
    assert sz.serialize(m1, toBytes=False) == m1s
    assert sz.serialize(m2) == m2s.encode()
    assert sz.serialize(m2, toBytes=False) == m2s
    assert sz.serialize(m3) == m3s.encode()
    assert sz.serialize(m3, toBytes=False) == m3s

    assert sz.deserialize(m1s) == m1
    assert sz.deserialize(m1s.encode()) == m1
    assert sz.deserialize(m2s) == m2
    assert sz.deserialize(m2s.encode()) == m2
    assert sz.deserialize(m3s) == m3
    assert sz.deserialize(m3s.encode()) == m3
Beispiel #4
0
def testJsonSerializer():
    sz = JsonSerializer()
    m1 = {'integer': 36, 'name': 'Foo', 'surname': 'Bar', 'float': 14.8639,
          'index': 1, 'index_start_at': 56, 'email': '*****@*****.**',
          'fullname': 'Foo Bar', 'bool': False}
    m1s = '{"bool":false,"email":"*****@*****.**","float":14.8639,"fullname":"Foo Bar","index":1,"index_start_at":56,"integer":36,"name":"Foo","surname":"Bar"}'

    m2 = {'latitude': 31.351883, 'longitude': -97.466179, 
          'tags': ['foo', 'bar', 'baz', 'alice', 'bob',
                   'carol', 'dave']}
    m2s = '{"latitude":31.351883,"longitude":-97.466179,"tags":["foo","bar","baz","alice","bob","carol","dave"]}'

    m3 = {'name': 'Alice Bob', 'website': 'example.com', 'friends': [
      {
        'id': 0,
        'name': 'Dave'
      },
      {
        'id': 1,
        'name': 'Carol'
      },
      {
        'id': 2,
        'name': 'Dave'
      }]}
    m3s = '{"friends":[{"id":0,"name":"Dave"},{"id":1,"name":"Carol"},{"id":2,"name":"Dave"}],"name":"Alice Bob","website":"example.com"}'

    assert sz.serialize(m1) == m1s.encode()
    assert sz.serialize(m1, toBytes=False) == m1s
    assert sz.serialize(m2) == m2s.encode()
    assert sz.serialize(m2, toBytes=False) == m2s
    assert sz.serialize(m3) == m3s.encode()
    assert sz.serialize(m3, toBytes=False) == m3s

    assert sz.deserialize(m1s) == m1
    assert sz.deserialize(m1s.encode()) == m1
    assert sz.deserialize(m2s) == m2
    assert sz.deserialize(m2s.encode()) == m2
    assert sz.deserialize(m3s) == m3
    assert sz.deserialize(m3s.encode()) == m3
Beispiel #5
0
class PoolRequestHandler(RequestHandler):
    def __init__(self, ledger: Ledger, state: State, domainState: State):
        super().__init__(ledger, state)
        self.domainState = domainState
        self.stateSerializer = JsonSerializer()

    def validate(self, req: Request, config=None):
        typ = req.operation.get(TXN_TYPE)
        error = None
        if typ == NODE:
            nodeNym = req.operation.get(TARGET_NYM)
            if self.getNodeData(nodeNym, isCommitted=False):
                error = self.authErrorWhileUpdatingNode(req)
            else:
                error = self.authErrorWhileAddingNode(req)
        if error:
            raise UnauthorizedClientRequest(req.identifier, req.reqId, error)

    def apply(self, req: Request):
        typ = req.operation.get(TXN_TYPE)
        if typ == NODE:
            txn = reqToTxn(req)
            (start, end), _ = self.ledger.appendTxns([txn])
            self.updateState(txnsWithSeqNo(start, end, [txn]))
            return txn
        else:
            logger.debug(
                'Cannot apply request of type {} to state'.format(typ))
            return None

    def updateState(self, txns, isCommitted=False):
        for txn in txns:
            nodeNym = txn.get(TARGET_NYM)
            data = txn.get(DATA, {})
            existingData = self.getNodeData(nodeNym, isCommitted=isCommitted)
            # Node data did not exist in state, so this is a new node txn,
            # hence store the author of the txn (steward of node)
            if not existingData:
                existingData[f.IDENTIFIER.nm] = txn.get(f.IDENTIFIER.nm)
            existingData.update(data)
            self.updateNodeData(nodeNym, existingData)

    def authErrorWhileAddingNode(self, request):
        origin = request.identifier
        operation = request.operation
        data = operation.get(DATA, {})
        error = self.dataErrorWhileValidating(data, skipKeys=False)
        if error:
            return error

        isSteward = self.isSteward(origin, isCommitted=False)
        if not isSteward:
            return "{} is not a steward so cannot add a new node".format(
                origin)
        if self.stewardHasNode(origin):
            return "{} already has a node".format(origin)
        if self.isNodeDataConflicting(operation.get(DATA, {})):
            return "existing data has conflicts with " \
                   "request data {}".format(operation.get(DATA))

    def authErrorWhileUpdatingNode(self, request):
        # Check if steward of the node is updating it and its data does not
        # conflict with any existing node's data
        origin = request.identifier
        operation = request.operation
        isSteward = self.isSteward(origin, isCommitted=False)
        if not isSteward:
            return "{} is not a steward so cannot update a node".format(origin)

        nodeNym = operation.get(TARGET_NYM)
        if not self.isStewardOfNode(origin, nodeNym, isCommitted=False):
            return "{} is not a steward of node {}".format(origin, nodeNym)

        data = operation.get(DATA, {})
        return self.dataErrorWhileValidatingUpdate(data, nodeNym)

    def getNodeData(self, nym, isCommitted: bool = True):
        key = nym.encode()
        data = self.state.get(key, isCommitted)
        return json.loads(data.decode()) if data else {}

    def updateNodeData(self, nym, data):
        key = nym.encode()
        val = self.stateSerializer.serialize(data)
        self.state.set(key, val)

    def isSteward(self, nym, isCommitted: bool = True):
        return DomainRequestHandler.isSteward(self.domainState, nym,
                                              isCommitted)

    @lru_cache(maxsize=64)
    def isStewardOfNode(self, stewardNym, nodeNym, isCommitted=True):
        nodeData = self.getNodeData(nodeNym, isCommitted=isCommitted)
        return nodeData and nodeData[f.IDENTIFIER.nm] == stewardNym

    def stewardHasNode(self, stewardNym) -> bool:
        # Cannot use lru_cache since a steward might have a node in future and
        # unfortunately lru_cache does not allow single entries to be cleared
        # TODO: Modify lru_cache to clear certain entities
        for nodeNym, nodeData in self.state.as_dict.items():
            nodeData = json.loads(nodeData.decode())
            if nodeData.get(f.IDENTIFIER.nm) == stewardNym:
                return True
        return False

    @staticmethod
    def dataErrorWhileValidating(data, skipKeys):
        reqKeys = {NODE_IP, NODE_PORT, CLIENT_IP, CLIENT_PORT, ALIAS}
        if not skipKeys and not reqKeys.issubset(set(data.keys())):
            return 'Missing some of {}'.format(reqKeys)

        nip = data.get(NODE_IP, 'nip')
        np = data.get(NODE_PORT, 'np')
        cip = data.get(CLIENT_IP, 'cip')
        cp = data.get(CLIENT_PORT, 'cp')
        if (nip, np) == (cip, cp):
            return 'node and client ha cannot be same'

    def isNodeDataSame(self, nodeNym, newData, isCommitted=True):
        nodeInfo = self.getNodeData(nodeNym, isCommitted=isCommitted)
        nodeInfo.pop(f.IDENTIFIER.nm, None)
        return nodeInfo == newData

    def isNodeDataConflicting(self, data, nodeNym=None):
        # Check if node's ALIAS or IPs or ports conflicts with other nodes,
        # also, the node is not allowed to change its alias.

        # Check ALIAS change
        nodeData = {}
        if nodeNym:
            nodeData = self.getNodeData(nodeNym, isCommitted=False)
            if nodeData.get(ALIAS) != data.get(ALIAS):
                return True
            else:
                # Preparing node data for check coming next
                nodeData.pop(f.IDENTIFIER.nm, None)
                nodeData.pop(SERVICES, None)
                nodeData.update(data)

        for otherNode, otherNodeData in self.state.as_dict.items():
            otherNode = otherNode.decode()
            otherNodeData = json.loads(otherNodeData.decode())
            otherNodeData.pop(f.IDENTIFIER.nm, None)
            otherNodeData.pop(SERVICES, None)
            if not nodeNym or otherNode != nodeNym:
                # The node's ip, port and alias shuuld be unique
                bag = set()
                for d in (nodeData, otherNodeData):
                    bag.add(d.get(ALIAS))
                    bag.add((d.get(NODE_IP), d.get(NODE_PORT)))
                    bag.add((d.get(CLIENT_IP), d.get(CLIENT_PORT)))

                list(
                    map(lambda x: bag.remove(x)
                        if x in bag else None, (None, (None, None))))

                if (not nodeData and len(bag) != 3) or (nodeData
                                                        and len(bag) != 6):
                    return True

    def dataErrorWhileValidatingUpdate(self, data, nodeNym):
        error = self.dataErrorWhileValidating(data, skipKeys=True)
        if error:
            return error

        if self.isNodeDataSame(nodeNym, data, isCommitted=False):
            return "node already has the same data as requested"

        if self.isNodeDataConflicting(data, nodeNym):
            return "existing data has conflicts with " \
                   "request data {}".format(data)