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
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
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
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
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)