Ejemplo n.º 1
0
class Client(PlenumClient):
    def __init__(self,
                 name: str,
                 nodeReg: Dict[str, HA] = None,
                 ha: Union[HA, Tuple[str, int]] = None,
                 peerHA: Union[HA, Tuple[str, int]] = None,
                 basedirpath: str = None,
                 config=None,
                 sighex: str = None):
        config = config or getConfig()
        super().__init__(name, nodeReg, ha, basedirpath, config, sighex)
        self.graphStore = self.getGraphStore()
        self.autoDiscloseAttributes = False
        self.requestedPendingTxns = False
        self.hasAnonCreds = bool(peerHA)
        if self.hasAnonCreds:
            self.peerHA = peerHA if isinstance(peerHA, HA) else HA(*peerHA)
            stackargs = dict(name=self.stackName,
                             ha=peerHA,
                             main=True,
                             auto=AutoMode.always)
            self.peerMsgRoutes = []
            self.peerMsgRouter = Router(*self.peerMsgRoutes)
            self.peerStack = SimpleStack(stackargs,
                                         msgHandler=self.handlePeerMessage)
            self.peerStack.sign = self.sign
            self.peerInbox = deque()
        self._observers = {}  # type Dict[str, Callable]
        self._observerSet = set(
        )  # makes it easier to guard against duplicates

    def handlePeerMessage(self, msg):
        """
        Use the peerMsgRouter to pass the messages to the correct
         function that handles them

        :param msg: the P2P client message.
        """
        return self.peerMsgRouter.handle(msg)

    def _getOrientDbStore(self):
        return OrientDbStore(user=self.config.OrientDB["user"],
                             password=self.config.OrientDB["password"],
                             dbName=self.name,
                             storageType=pyorient.STORAGE_TYPE_PLOCAL)

    def getReqRepStore(self):
        if self.config.ReqReplyStore == "orientdb":
            return ClientReqRepStoreOrientDB(self._getOrientDbStore())
        else:
            return ClientReqRepStoreFile(self.name, self.basedirpath)

    def getGraphStore(self):
        return IdentityGraph(self._getOrientDbStore()) if \
            self.config.ClientIdentityGraph else None

    def getTxnLogStore(self):
        return ClientTxnLog(self.name, self.basedirpath)

    def handleOneNodeMsg(self, wrappedMsg, excludeFromCli=None) -> None:
        msg, sender = wrappedMsg
        # excludeGetTxns = (msg.get(OP_FIELD_NAME) == REPLY and
        #                   msg[f.RESULT.nm].get(TXN_TYPE) == GET_TXNS)
        excludeReqAcks = msg.get(OP_FIELD_NAME) == REQACK
        excludeReqNacks = msg.get(OP_FIELD_NAME) == REQNACK
        excludeReply = msg.get(OP_FIELD_NAME) == REPLY
        excludeFromCli = excludeFromCli or excludeReqAcks or excludeReqNacks \
                         or excludeReply
        super().handleOneNodeMsg(wrappedMsg, excludeFromCli)
        if OP_FIELD_NAME not in msg:
            logger.error("Op absent in message {}".format(msg))

    def postReplyRecvd(self, identifier, reqId, frm, result, numReplies):
        reply = super().postReplyRecvd(identifier, reqId, frm, result,
                                       numReplies)
        if reply:
            for name in self._observers:
                try:
                    self._observers[name](name, reqId, frm, result, numReplies)
                except Exception as ex:
                    # TODO: All errors should not be shown on CLI, or maybe we
                    # show errors with different color according to the
                    # severity. Like an error occurring due to node sending
                    # a malformed message should not result in an error message
                    # being shown on the cli since the clients would anyway
                    # collect enough replies from other nodes.
                    logger.debug("Observer threw an exception", exc_info=ex)
            if isinstance(self.reqRepStore, ClientReqRepStoreOrientDB):
                self.reqRepStore.setConsensus(identifier, reqId)
            if result[TXN_TYPE] == NYM:
                if self.graphStore:
                    self.addNymToGraph(result)
            elif result[TXN_TYPE] == ATTRIB:
                if self.graphStore:
                    self.graphStore.addAttribTxnToGraph(result)
            elif result[TXN_TYPE] == GET_NYM:
                if self.graphStore:
                    if DATA in result and result[DATA]:
                        self.addNymToGraph(json.loads(result[DATA]))
            elif result[TXN_TYPE] == GET_TXNS:
                if DATA in result and result[DATA]:
                    data = json.loads(result[DATA])
                    self.reqRepStore.setLastTxnForIdentifier(
                        result[f.IDENTIFIER.nm], data[LAST_TXN])
                    if self.graphStore:
                        for txn in data[TXNS]:
                            if txn[TXN_TYPE] == NYM:
                                self.addNymToGraph(txn)
                            elif txn[TXN_TYPE] == ATTRIB:
                                try:
                                    self.graphStore.addAttribTxnToGraph(txn)
                                except pyorient.PyOrientCommandException as ex:
                                    fault(
                                        ex, "An exception was raised while "
                                        "adding attribute")

            elif result[TXN_TYPE] == CLAIM_DEF:
                if self.graphStore:
                    self.graphStore.addClaimDefTxnToGraph(result)
            elif result[TXN_TYPE] == ISSUER_KEY:
                if self.graphStore:
                    self.graphStore.addIssuerKeyTxnToGraph(result)
                    # else:
                    #    logger.debug("Unknown type {}".format(result[TXN_TYPE]))

    def requestConfirmed(self, identifier: str, reqId: int) -> bool:
        if isinstance(self.reqRepStore, ClientReqRepStoreOrientDB):
            return self.reqRepStore.requestConfirmed(identifier, reqId)
        else:
            return self.txnLog.hasTxnWithReqId(identifier, reqId)

    def hasConsensus(self, identifier: str, reqId: int) -> Optional[str]:
        if isinstance(self.reqRepStore, ClientReqRepStoreOrientDB):
            return self.reqRepStore.hasConsensus(identifier, reqId)
        else:
            return super().hasConsensus(identifier, reqId)

    def addNymToGraph(self, txn):
        origin = txn.get(f.IDENTIFIER.nm)
        if txn.get(ROLE) == SPONSOR:
            if not self.graphStore.hasSteward(origin):
                try:
                    self.graphStore.addNym(None, nym=origin, role=STEWARD)
                except pyorient.PyOrientCommandException as ex:
                    logger.trace("Error occurred adding nym to graph")
                    logger.trace(traceback.format_exc())
        self.graphStore.addNymTxnToGraph(txn)

    def getTxnById(self, txnId: str):
        if self.graphStore:
            txns = list(self.graphStore.getResultForTxnIds(txnId).values())
            return txns[0] if txns else {}
        else:
            # TODO: Add support for fetching reply by transaction id
            # serTxn = self.reqRepStore.getResultForTxnId(txnId)
            pass
            # TODO Add merkleInfo as well

    def getTxnsByNym(self, nym: str):
        raise NotImplementedError

    def getTxnsByType(self, txnType):
        if self.graphStore:
            edgeClass = getEdgeByTxnType(txnType)
            if edgeClass:
                cmd = "select from {}".format(edgeClass)
                result = self.graphStore.client.command(cmd)
                if result:
                    return [r.oRecordData for r in result]
            return []
        else:
            txns = self.txnLog.getTxnsByType(txnType)
            # TODO: Fix ASAP
            if txnType == CLAIM_DEF:
                for txn in txns:
                    txn[DATA] = json.loads(txn[DATA].replace(
                        "\'", '"').replace('"{', '{').replace('}"', '}'))
                    txn[NAME] = txn[DATA][NAME]
                    txn[VERSION] = txn[DATA][VERSION]
            return txns

    # TODO: Just for now. Remove it later
    def doAttrDisclose(self, origin, target, txnId, key):
        box = libnacl.public.Box(b58decode(origin), b58decode(target))

        data = json.dumps({TXN_ID: txnId, SKEY: key})
        nonce, boxedMsg = box.encrypt(data.encode(), pack_nonce=False)

        op = {
            TARGET_NYM: target,
            TXN_TYPE: DISCLO,
            NONCE: b58encode(nonce),
            DATA: b58encode(boxedMsg)
        }
        self.submit(op, identifier=origin)

    def doGetAttributeTxn(self, identifier, attrName):
        op = {
            TARGET_NYM: identifier,
            TXN_TYPE: GET_ATTR,
            DATA: json.dumps({"name": attrName})
        }
        self.submit(op, identifier=identifier)

    @staticmethod
    def _getDecryptedData(encData, key):
        data = bytes(bytearray.fromhex(encData))
        rawKey = bytes(bytearray.fromhex(key))
        box = libnacl.secret.SecretBox(rawKey)
        decData = box.decrypt(data).decode()
        return json.loads(decData)

    def hasNym(self, nym):
        if self.graphStore:
            return self.graphStore.hasNym(nym)
        else:
            for txn in self.txnLog.getTxnsByType(NYM):
                if txn.get(TXN_TYPE) == NYM:
                    return True
            return False

    def _statusChanged(self, old, new):
        super()._statusChanged(old, new)

    def start(self, loop):
        super().start(loop)
        if self.hasAnonCreds and self.status not in Status.going():
            self.peerStack.start()

    async def prod(self, limit) -> int:
        # s = await self.nodestack.service(limit)
        # if self.isGoing():
        #     await self.nodestack.serviceLifecycle()
        # self.nodestack.flushOutBoxes()
        s = await super().prod(limit)
        if self.hasAnonCreds:
            return s + await self.peerStack.service(limit)
        else:
            return s

    def registerObserver(self, observer: Callable, name=None):
        if not name:
            name = uuid.uuid4()
        if name in self._observers or observer in self._observerSet:
            raise RuntimeError("Observer {} already registered".format(name))
        self._observers[name] = observer
        self._observerSet.add(observer)

    def deregisterObserver(self, name):
        if name not in self._observers:
            raise RuntimeError("Observer {} not registered".format(name))
        self._observerSet.remove(self._observers[name])
        del self._observers[name]

    def hasObserver(self, name):
        return name in self._observerSet
Ejemplo n.º 2
0
class Client(PlenumClient):
    def __init__(self,
                 name: str = None,
                 nodeReg: Dict[str, HA] = None,
                 ha: Union[HA, Tuple[str, int]] = None,
                 peerHA: Union[HA, Tuple[str, int]] = None,
                 basedirpath: str = None,
                 config=None,
                 sighex: str = None):
        config = config or getConfig()
        super().__init__(name, nodeReg, ha, basedirpath, config, sighex)
        self.autoDiscloseAttributes = False
        self.requestedPendingTxns = False
        self.hasAnonCreds = bool(peerHA)
        if self.hasAnonCreds:
            self.peerHA = peerHA if isinstance(peerHA, HA) else HA(*peerHA)

            stackargs = dict(name=self.stackName,
                             ha=peerHA,
                             main=True,
                             auth_mode=AuthMode.ALLOW_ANY.value)

            self.peerMsgRoutes = []
            self.peerMsgRouter = Router(*self.peerMsgRoutes)
            self.peerStack = self.peerStackClass(
                stackargs, msgHandler=self.handlePeerMessage)
            self.peerStack.sign = self.sign
            self.peerInbox = deque()
        self._observers = {}  # type Dict[str, Callable]
        self._observerSet = set(
        )  # makes it easier to guard against duplicates

    @property
    def peerStackClass(self):
        if config.UseZStack:
            return SimpleZStack
        return SimpleRStack

    def handlePeerMessage(self, msg):
        """
        Use the peerMsgRouter to pass the messages to the correct
         function that handles them

        :param msg: the P2P client message.
        """
        return self.peerMsgRouter.handle(msg)

    def getReqRepStore(self):
        return ClientReqRepStoreFile(self.name, self.basedirpath)

    def getTxnLogStore(self):
        return ClientTxnLog(self.name, self.basedirpath)

    def handleOneNodeMsg(self, wrappedMsg, excludeFromCli=None) -> None:
        msg, sender = wrappedMsg
        # excludeGetTxns = (msg.get(OP_FIELD_NAME) == REPLY and
        #                   msg[f.RESULT.nm].get(TXN_TYPE) == GET_TXNS)
        excludeReqAcks = msg.get(OP_FIELD_NAME) == REQACK
        excludeReqNacks = msg.get(OP_FIELD_NAME) == REQNACK
        excludeReply = msg.get(OP_FIELD_NAME) == REPLY
        excludeReject = msg.get(OP_FIELD_NAME) == REJECT
        excludeFromCli = excludeFromCli or excludeReqAcks or excludeReqNacks \
                         or excludeReply or excludeReject
        super().handleOneNodeMsg(wrappedMsg, excludeFromCli)
        if OP_FIELD_NAME not in msg:
            logger.error("Op absent in message {}".format(msg))

    def postReplyRecvd(self, identifier, reqId, frm, result, numReplies):
        reply = super().postReplyRecvd(identifier, reqId, frm, result,
                                       numReplies)
        if reply:
            for name in self._observers:
                try:
                    self._observers[name](name, reqId, frm, result, numReplies)
                except Exception as ex:
                    # TODO: All errors should not be shown on CLI, or maybe we
                    # show errors with different color according to the
                    # severity. Like an error occurring due to node sending
                    # a malformed message should not result in an error message
                    # being shown on the cli since the clients would anyway
                    # collect enough replies from other nodes.
                    logger.debug("Observer threw an exception", exc_info=ex)

    def requestConfirmed(self, identifier: str, reqId: int) -> bool:
        return self.txnLog.hasTxnWithReqId(identifier, reqId)

    def hasConsensus(self, identifier: str, reqId: int) -> Optional[str]:
        return super().hasConsensus(identifier, reqId)

    def getTxnsByNym(self, nym: str):
        raise NotImplementedError

    def getTxnsByType(self, txnType):
        txns = self.txnLog.getTxnsByType(txnType)
        if txnType == SCHEMA:
            for txn in txns:
                txn[DATA] = json.loads(txn[DATA].replace("\'", '"').replace(
                    '"{', '{').replace('}"', '}'))
                txn[NAME] = txn[DATA][NAME]
                txn[VERSION] = txn[DATA][VERSION]
        return txns

    # TODO: Just for now. Remove it later
    def doAttrDisclose(self, origin, target, txnId, key):
        box = libnacl.public.Box(b58decode(origin), b58decode(target))

        data = json.dumps({TXN_ID: txnId, SKEY: key})
        nonce, boxedMsg = box.encrypt(data.encode(), pack_nonce=False)

        op = {
            TARGET_NYM: target,
            TXN_TYPE: DISCLO,
            NONCE: b58encode(nonce),
            DATA: b58encode(boxedMsg)
        }
        self.submit(op, identifier=origin)

    def doGetAttributeTxn(self, identifier, attrName):
        op = {
            TARGET_NYM: identifier,
            TXN_TYPE: GET_ATTR,
            DATA: json.dumps({"name": attrName})
        }
        self.submit(op, identifier=identifier)

    @staticmethod
    def _getDecryptedData(encData, key):
        data = bytes(bytearray.fromhex(encData))
        rawKey = bytes(bytearray.fromhex(key))
        box = libnacl.secret.SecretBox(rawKey)
        decData = box.decrypt(data).decode()
        return json.loads(decData)

    def hasNym(self, nym):
        for txn in self.txnLog.getTxnsByType(NYM):
            if txn.get(TXN_TYPE) == NYM:
                return True
        return False

    def _statusChanged(self, old, new):
        super()._statusChanged(old, new)

    def start(self, loop):
        super().start(loop)
        if self.hasAnonCreds and self.status not in Status.going():
            self.peerStack.start()

    async def prod(self, limit) -> int:
        s = await super().prod(limit)
        if self.hasAnonCreds:
            return s + await self.peerStack.service(limit)
        else:
            return s

    def registerObserver(self, observer: Callable, name=None):
        if not name:
            name = uuid.uuid4()
        if name in self._observers or observer in self._observerSet:
            raise RuntimeError("Observer {} already registered".format(name))
        self._observers[name] = observer
        self._observerSet.add(observer)

    def deregisterObserver(self, name):
        if name not in self._observers:
            raise RuntimeError("Observer {} not registered".format(name))
        self._observerSet.remove(self._observers[name])
        del self._observers[name]

    def hasObserver(self, name):
        return name in self._observerSet
Ejemplo n.º 3
0
class Client(PlenumClient):
    def __init__(self,
                 name: str=None,
                 nodeReg: Dict[str, HA]=None,
                 ha: Union[HA, Tuple[str, int]]=None,
                 peerHA: Union[HA, Tuple[str, int]]=None,
                 basedirpath: str=None,
                 config=None,
                 sighex: str=None):
        config = config or getConfig()
        super().__init__(name,
                         nodeReg,
                         ha,
                         basedirpath,
                         config,
                         sighex)
        self.autoDiscloseAttributes = False
        self.requestedPendingTxns = False
        self.hasAnonCreds = bool(peerHA)
        if self.hasAnonCreds:
            self.peerHA = peerHA if isinstance(peerHA, HA) else HA(*peerHA)

            stackargs = dict(name=self.stackName,
                             ha=peerHA,
                             main=True,
                             auth_mode=AuthMode.ALLOW_ANY.value)

            self.peerMsgRoutes = []
            self.peerMsgRouter = Router(*self.peerMsgRoutes)
            self.peerStack = self.peerStackClass(stackargs,
                                          msgHandler=self.handlePeerMessage)
            self.peerStack.sign = self.sign
            self.peerInbox = deque()
        self._observers = {}  # type Dict[str, Callable]
        self._observerSet = set()  # makes it easier to guard against duplicates

    @property
    def peerStackClass(self):
        if config.UseZStack:
            return SimpleZStack
        return SimpleRStack

    def handlePeerMessage(self, msg):
        """
        Use the peerMsgRouter to pass the messages to the correct
         function that handles them

        :param msg: the P2P client message.
        """
        return self.peerMsgRouter.handle(msg)

    def getReqRepStore(self):
        return ClientReqRepStoreFile(self.name, self.basedirpath)

    def getTxnLogStore(self):
        return ClientTxnLog(self.name, self.basedirpath)

    def handleOneNodeMsg(self, wrappedMsg, excludeFromCli=None) -> None:
        msg, sender = wrappedMsg
        # excludeGetTxns = (msg.get(OP_FIELD_NAME) == REPLY and
        #                   msg[f.RESULT.nm].get(TXN_TYPE) == GET_TXNS)
        excludeReqAcks = msg.get(OP_FIELD_NAME) == REQACK
        excludeReqNacks = msg.get(OP_FIELD_NAME) == REQNACK
        excludeReply = msg.get(OP_FIELD_NAME) == REPLY
        excludeReject = msg.get(OP_FIELD_NAME) == REJECT
        excludeFromCli = excludeFromCli or excludeReqAcks or excludeReqNacks \
                         or excludeReply or excludeReject
        super().handleOneNodeMsg(wrappedMsg, excludeFromCli)
        if OP_FIELD_NAME not in msg:
            logger.error("Op absent in message {}".format(msg))

    def postReplyRecvd(self, identifier, reqId, frm, result, numReplies):
        reply = super().postReplyRecvd(identifier, reqId, frm, result, numReplies)
        if reply:
            for name in self._observers:
                try:
                    self._observers[name](name, reqId, frm, result, numReplies)
                except Exception as ex:
                    # TODO: All errors should not be shown on CLI, or maybe we
                    # show errors with different color according to the
                    # severity. Like an error occurring due to node sending
                    # a malformed message should not result in an error message
                    # being shown on the cli since the clients would anyway
                    # collect enough replies from other nodes.
                    logger.debug("Observer threw an exception", exc_info=ex)

    def requestConfirmed(self, identifier: str, reqId: int) -> bool:
        return self.txnLog.hasTxnWithReqId(identifier, reqId)

    def hasConsensus(self, identifier: str, reqId: int) -> Optional[str]:
        return super().hasConsensus(identifier, reqId)

    def getTxnsByNym(self, nym: str):
        raise NotImplementedError

    def getTxnsByType(self, txnType):
        txns = self.txnLog.getTxnsByType(txnType)
        if txnType == SCHEMA:
            for txn in txns:
                txn[DATA] = json.loads(txn[DATA].replace("\'", '"')
                                       .replace('"{', '{')
                                       .replace('}"', '}'))
                txn[NAME] = txn[DATA][NAME]
                txn[VERSION] = txn[DATA][VERSION]
        return txns

    # TODO: Just for now. Remove it later
    def doAttrDisclose(self, origin, target, txnId, key):
        box = libnacl.public.Box(b58decode(origin), b58decode(target))

        data = json.dumps({TXN_ID: txnId, SKEY: key})
        nonce, boxedMsg = box.encrypt(data.encode(), pack_nonce=False)

        op = {
            TARGET_NYM: target,
            TXN_TYPE: DISCLO,
            NONCE: b58encode(nonce),
            DATA: b58encode(boxedMsg)
        }
        self.submit(op, identifier=origin)

    def doGetAttributeTxn(self, identifier, attrName):
        op = {
            TARGET_NYM: identifier,
            TXN_TYPE: GET_ATTR,
            DATA: json.dumps({"name": attrName})
        }
        self.submit(op, identifier=identifier)

    @staticmethod
    def _getDecryptedData(encData, key):
        data = bytes(bytearray.fromhex(encData))
        rawKey = bytes(bytearray.fromhex(key))
        box = libnacl.secret.SecretBox(rawKey)
        decData = box.decrypt(data).decode()
        return json.loads(decData)

    def hasNym(self, nym):
        for txn in self.txnLog.getTxnsByType(NYM):
            if txn.get(TXN_TYPE) == NYM:
                return True
        return False

    def _statusChanged(self, old, new):
        super()._statusChanged(old, new)

    def start(self, loop):
        super().start(loop)
        if self.hasAnonCreds and self.status not in Status.going():
            self.peerStack.start()

    async def prod(self, limit) -> int:
        s = await super().prod(limit)
        if self.hasAnonCreds:
            return s + await self.peerStack.service(limit)
        else:
            return s

    def registerObserver(self, observer: Callable, name=None):
        if not name:
            name = uuid.uuid4()
        if name in self._observers or observer in self._observerSet:
            raise RuntimeError("Observer {} already registered".format(name))
        self._observers[name] = observer
        self._observerSet.add(observer)

    def deregisterObserver(self, name):
        if name not in self._observers:
            raise RuntimeError("Observer {} not registered".format(name))
        self._observerSet.remove(self._observers[name])
        del self._observers[name]

    def hasObserver(self, name):
        return name in self._observerSet
Ejemplo n.º 4
0
class Client(PlenumClient):
    anoncredsAreSetUp = False

    def __init__(self,
                 name: str=None,
                 nodeReg: Dict[str, HA]=None,
                 ha: Union[HA, Tuple[str, int]]=None,
                 peerHA: Union[HA, Tuple[str, int]]=None,
                 basedirpath: str=None,
                 config=None,
                 sighex: str=None):
        self.config = config or getConfig()
        self.setupAnoncreds()

        basedirpath = basedirpath or os.path.join(self.config.CLI_NETWORK_DIR, self.config.NETWORK_NAME)
        super().__init__(name,
                         nodeReg,
                         ha,
                         basedirpath,
                         config=config,
                         sighex=sighex)
        self.autoDiscloseAttributes = False
        self.requestedPendingTxns = False
        self.hasAnonCreds = bool(peerHA)
        if self.hasAnonCreds:
            self.peerHA = peerHA if isinstance(peerHA, HA) else HA(*peerHA)

            stackargs = dict(name=self.stackName,
                             ha=peerHA,
                             main=True,
                             auth_mode=AuthMode.ALLOW_ANY.value)

            self.peerMsgRoutes = []
            self.peerMsgRouter = Router(*self.peerMsgRoutes)
            self.peerStack = self.peerStackClass(
                stackargs, msgHandler=self.handlePeerMessage)
            self.peerStack.sign = self.sign
            self.peerInbox = deque()

        # To let client send this transactions to just one node
        self._read_only_requests = {GET_NYM,
                                    GET_ATTR,
                                    GET_CLAIM_DEF,
                                    GET_SCHEMA}

    @property
    def peerStackClass(self):
        return SimpleZStack

    def setupAnoncreds(self):
        if self.anoncredsAreSetUp is False:
            writeAnonCredPlugin(os.path.expanduser(self.config.CLI_BASE_DIR))
            # This is to setup anoncreds wallet related custom jsonpickle handlers to
            # serialize/deserialize it properly
            setUpJsonpickle()
            WALLET_RAW_MIGRATORS.append(migrate_indy_wallet_raw)
            self.anoncredsAreSetUp = True

    def handlePeerMessage(self, msg):
        """
        Use the peerMsgRouter to pass the messages to the correct
         function that handles them

        :param msg: the P2P client message.
        """
        return self.peerMsgRouter.handle(msg)

    def getReqRepStore(self):
        return ClientReqRepStoreFile(self.ledger_dir)

    def getTxnLogStore(self):
        return ClientTxnLog(self.ledger_dir)

    def handleOneNodeMsg(self, wrappedMsg, excludeFromCli=None) -> None:
        msg, sender = wrappedMsg
        # excludeGetTxns = (msg.get(OP_FIELD_NAME) == REPLY and
        #                   msg[f.RESULT.nm].get(TXN_TYPE) == GET_TXNS)
        excludeReqAcks = msg.get(OP_FIELD_NAME) == REQACK
        excludeReqNacks = msg.get(OP_FIELD_NAME) == REQNACK
        excludeReply = msg.get(OP_FIELD_NAME) == REPLY
        excludeReject = msg.get(OP_FIELD_NAME) == REJECT
        excludeFromCli = excludeFromCli or excludeReqAcks or excludeReqNacks \
            or excludeReply or excludeReject
        super().handleOneNodeMsg(wrappedMsg, excludeFromCli)
        if OP_FIELD_NAME not in msg:
            logger.error("Op absent in message {}".format(msg))

    def requestConfirmed(self, key) -> bool:
        return self.txnLog.hasTxnWithReqId(key)

    def hasConsensus(self, identifier: str, reqId: int) -> Optional[str]:
        return super().hasConsensus(identifier, reqId)

    def prepare_for_state(self, result):
        request_type = result[TYPE]
        if request_type == GET_NYM:
            return domain.prepare_get_nym_for_state(result)
        if request_type == GET_ATTR:
            attr_type, path, value, hashed_value, value_bytes = \
                domain.prepare_get_attr_for_state(result)
            return path, value_bytes
        if request_type == GET_CLAIM_DEF:
            return domain.prepare_get_claim_def_for_state(result)
        if request_type == GET_SCHEMA:
            return domain.prepare_get_schema_for_state(result)
        raise ValueError("Cannot make state key for "
                         "request of type {}"
                         .format(request_type))

    def getTxnsByType(self, txnType):
        return self.txnLog.getTxnsByType(txnType)

    # TODO: Just for now. Remove it later
    def doAttrDisclose(self, origin, target, txnId, key):
        box = libnacl.public.Box(b58decode(origin), b58decode(target))

        data = json.dumps({TXN_ID: txnId, SKEY: key})
        nonce, boxedMsg = box.encrypt(data.encode(), pack_nonce=False)

        op = {
            TARGET_NYM: target,
            TXN_TYPE: DISCLO,
            NONCE: b58encode(nonce).decode("utf-8"),
            DATA: b58encode(boxedMsg).decode("utf-8")
        }
        self.submit(op, identifier=origin)

    def doGetAttributeTxn(self, identifier, attrName):
        op = {
            TARGET_NYM: identifier,
            TXN_TYPE: GET_ATTR,
            DATA: json.dumps({"name": attrName})
        }
        self.submit(op, identifier=identifier)

    @staticmethod
    def _getDecryptedData(encData, key):
        data = bytes(bytearray.fromhex(encData))
        rawKey = bytes(bytearray.fromhex(key))
        box = libnacl.secret.SecretBox(rawKey)
        decData = box.decrypt(data).decode()
        return json.loads(decData)

    def hasNym(self, nym):
        for txn in self.txnLog.getTxnsByType(NYM):
            if get_type(txn) == NYM:
                return True
        return False

    def _statusChanged(self, old, new):
        super()._statusChanged(old, new)

    def start(self, loop):
        super().start(loop)
        if self.hasAnonCreds and self.status not in Status.going():
            self.peerStack.start()

    async def prod(self, limit) -> int:
        s = await super().prod(limit)
        if self.hasAnonCreds:
            return s + await self.peerStack.service(limit)
        else:
            return s
Ejemplo n.º 5
0
class Client(PlenumClient):
    def __init__(self,
                 name: str = None,
                 nodeReg: Dict[str, HA] = None,
                 ha: Union[HA, Tuple[str, int]] = None,
                 peerHA: Union[HA, Tuple[str, int]] = None,
                 basedirpath: str = None,
                 config=None,
                 sighex: str = None):
        config = config or getConfig()
        super().__init__(name, nodeReg, ha, basedirpath, config, sighex)
        self.autoDiscloseAttributes = False
        self.requestedPendingTxns = False
        self.hasAnonCreds = bool(peerHA)
        if self.hasAnonCreds:
            self.peerHA = peerHA if isinstance(peerHA, HA) else HA(*peerHA)

            stackargs = dict(name=self.stackName,
                             ha=peerHA,
                             main=True,
                             auth_mode=AuthMode.ALLOW_ANY.value)

            self.peerMsgRoutes = []
            self.peerMsgRouter = Router(*self.peerMsgRoutes)
            self.peerStack = self.peerStackClass(
                stackargs, msgHandler=self.handlePeerMessage)
            self.peerStack.sign = self.sign
            self.peerInbox = deque()

        # To let client send this transactions to just one node
        self._read_only_requests = {
            GET_NYM, GET_ATTR, GET_CLAIM_DEF, GET_SCHEMA
        }

    @property
    def peerStackClass(self):
        if config.UseZStack:
            return SimpleZStack
        return SimpleRStack

    def handlePeerMessage(self, msg):
        """
        Use the peerMsgRouter to pass the messages to the correct
         function that handles them

        :param msg: the P2P client message.
        """
        return self.peerMsgRouter.handle(msg)

    def getReqRepStore(self):
        return ClientReqRepStoreFile(self.name, self.basedirpath)

    def getTxnLogStore(self):
        return ClientTxnLog(self.name, self.basedirpath)

    def handleOneNodeMsg(self, wrappedMsg, excludeFromCli=None) -> None:
        msg, sender = wrappedMsg
        # excludeGetTxns = (msg.get(OP_FIELD_NAME) == REPLY and
        #                   msg[f.RESULT.nm].get(TXN_TYPE) == GET_TXNS)
        excludeReqAcks = msg.get(OP_FIELD_NAME) == REQACK
        excludeReqNacks = msg.get(OP_FIELD_NAME) == REQNACK
        excludeReply = msg.get(OP_FIELD_NAME) == REPLY
        excludeReject = msg.get(OP_FIELD_NAME) == REJECT
        excludeFromCli = excludeFromCli or excludeReqAcks or excludeReqNacks \
            or excludeReply or excludeReject
        super().handleOneNodeMsg(wrappedMsg, excludeFromCli)
        if OP_FIELD_NAME not in msg:
            logger.error("Op absent in message {}".format(msg))

    def requestConfirmed(self, identifier: str, reqId: int) -> bool:
        return self.txnLog.hasTxnWithReqId(identifier, reqId)

    def hasConsensus(self, identifier: str, reqId: int) -> Optional[str]:
        return super().hasConsensus(identifier, reqId)

    def prepare_for_state(self, result):
        request_type = result[TYPE]
        if request_type == GET_NYM:
            return domain.prepare_get_nym_for_state(result)
        if request_type == GET_ATTR:
            path, value, hashed_value, value_bytes = \
                domain.prepare_get_attr_for_state(result)
            return path, value_bytes
        if request_type == GET_CLAIM_DEF:
            return domain.prepare_get_claim_def_for_state(result)
        if request_type == GET_SCHEMA:
            return domain.prepare_get_schema_for_state(result)
        raise ValueError("Cannot make state key for "
                         "request of type {}".format(request_type))

    def getTxnsByType(self, txnType):
        return self.txnLog.getTxnsByType(txnType)

    # TODO: Just for now. Remove it later
    def doAttrDisclose(self, origin, target, txnId, key):
        box = libnacl.public.Box(b58decode(origin), b58decode(target))

        data = json.dumps({TXN_ID: txnId, SKEY: key})
        nonce, boxedMsg = box.encrypt(data.encode(), pack_nonce=False)

        op = {
            TARGET_NYM: target,
            TXN_TYPE: DISCLO,
            NONCE: b58encode(nonce),
            DATA: b58encode(boxedMsg)
        }
        self.submit(op, identifier=origin)

    def doGetAttributeTxn(self, identifier, attrName):
        op = {
            TARGET_NYM: identifier,
            TXN_TYPE: GET_ATTR,
            DATA: json.dumps({"name": attrName})
        }
        self.submit(op, identifier=identifier)

    @staticmethod
    def _getDecryptedData(encData, key):
        data = bytes(bytearray.fromhex(encData))
        rawKey = bytes(bytearray.fromhex(key))
        box = libnacl.secret.SecretBox(rawKey)
        decData = box.decrypt(data).decode()
        return json.loads(decData)

    def hasNym(self, nym):
        for txn in self.txnLog.getTxnsByType(NYM):
            if txn.get(TXN_TYPE) == NYM:
                return True
        return False

    def _statusChanged(self, old, new):
        super()._statusChanged(old, new)

    def start(self, loop):
        super().start(loop)
        if self.hasAnonCreds and self.status not in Status.going():
            self.peerStack.start()

    async def prod(self, limit) -> int:
        s = await super().prod(limit)
        if self.hasAnonCreds:
            return s + await self.peerStack.service(limit)
        else:
            return s