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