def test_serialize_from_objects(self): """ Tests that serialize/deserialize are inverse operations with from_objects factory function """ sk, vk = wallet.new() message = self._default_msg() env = Envelope.create_from_message(message=message, signing_key=sk, verifying_key=vk) self.assertEqual(env, Envelope.from_bytes(env.serialize()))
def create(cls, envelope: Envelope = None, envelope_binary: bytes = None, **kwargs): assert not ( envelope and envelope_binary ), "Either envelope or envelope_binary should be passed in (not both)" cmd = reactor_capnp.ReactorCommand.new_message() if envelope: assert isinstance( envelope, Envelope), "'envelope' kwarg must be an Envelope instance" cmd.envelope.data = envelope.serialize() if envelope_binary: assert isinstance(envelope_binary, bytes), "'envelope_binary' must be bytes" cmd.envelope.data = envelope_binary cmd.init('kwargs', len(kwargs)) for i, key in enumerate(kwargs): cmd.kwargs[i].key, cmd.kwargs[i].value = str(key), str(kwargs[key]) return cls.from_data(cmd)
def _validate_envelope(self, envelope_binary: bytes, header: str) -> Union[None, Envelope]: # TODO return/raise custom exceptions in this instead of just logging stuff and returning none # Deserialize envelope env = None try: env = Envelope.from_bytes(envelope_binary) except Exception as e: self.log.error("Error deserializing envelope: {}".format(e)) return None # Check seal if not env.verify_seal(): self.log.error("Seal could not be verified for envelope {}".format(env)) return None # If header is not none (meaning this is a ROUTE msg with an ID frame), then verify that the ID frame is # the same as the vk on the seal if header and (header != env.seal.verifying_key): self.log.error("Header frame {} does not match seal's vk {}\nfor envelope {}" .format(header, env.seal.verifying_key, env)) return None # Make sure we haven't seen this message before if env.meta.uuid in Executor._recently_seen: self.log.debug("Duplicate envelope detect with UUID {}. Ignoring.".format(env.meta.uuid)) return None # TODO -- checks timestamp to ensure this envelope is recv'd in a somewhat reasonable time (within N seconds) # If none of the above checks above return None, this envelope should be good return env
def request(self, url: str, reply_uuid: str, envelope: Envelope, timeout=0): self.log.spam("requesting /w reply uuid {} and env {}".format( reply_uuid, envelope)) assert url in self.dealers, "Attempted to make request to url {} that is not in self.dealers {}"\ .format(url, self.dealers) reply_uuid = int(reply_uuid) timeout = float(timeout) self.log.spam( "Composing request to url {}\ntimeout: {}\nenvelope: {}".format( url, timeout, envelope)) if timeout > 0: assert reply_uuid not in self.expected_replies, "Reply UUID is already in expected replies" self.log.spam("Adding timeout of {} for reply uuid {}".format( timeout, reply_uuid)) self.expected_replies[reply_uuid] = self.loop.call_later( timeout, self._timeout, url, envelope, reply_uuid) self.dealers[url][_SOCKET].send_multipart([envelope.serialize()])
def send_pub(self, filter: str, envelope: bytes): assert isinstance(filter, str), "'id' arg must be a string" assert isinstance(envelope, bytes), "'envelope' arg must be bytes" assert len(self.pubs) > 0, "Attempted to publish data but publisher socket(s) is not configured" for url in self.pubs: self.log.debug("Publishing to URL {} with envelope: {}".format(url, Envelope.from_bytes(envelope))) # self.log.info("Publishing to... {}".format(url)) self.pubs[url].send_multipart([filter.encode(), envelope])
def send_pub(self, url: str, filter: str, data: bytes): assert isinstance( filter, str), "'filter' arg must be a string not {}".format(filter) assert isinstance(data, bytes), "'envelope' arg must be bytes" assert url in self.pubs, "Attempted to pub to URL {} that is not in self.pubs {}".format( url, self.pubs) self.log.spam("Publishing to URL {} with envelope: {}".format( url, Envelope.from_bytes(data))) self.pubs[url].send_multipart([filter.encode(), data])
def _package_msg(self, msg: MessageBase) -> Envelope: """ Convenience method to package a message into an envelope :param msg: The MessageBase instance to package :return: An Envelope instance """ assert type(msg) is not Envelope, "Attempted to package a 'message' that is already an envelope" assert issubclass(type(msg), MessageBase), "Attempted to package a message that is not a MessageBase subclass" return Envelope.create_from_message(message=msg, signing_key=self.signing_key, verifying_key=self.verifying_key)
def _recv_reply_env(self, header: str, envelope: Envelope): self.log.debug("Recv REPLY envelope with header {} and envelope {}".format(header, envelope)) reply_uuid = envelope.meta.uuid if reply_uuid in self.expected_replies: self.log.debug("Removing reply with uuid {} from expected replies".format(reply_uuid)) self.expected_replies[reply_uuid].cancel() del(self.expected_replies[reply_uuid]) self.call_on_mp(callback=StateInput.INPUT, header=header, envelope_binary=envelope.serialize())
def test_create_from_message(self): """ Tests create_from_message with valid args (no vk passed) creates an envelope valid signature and expected fields """ sk, vk = wallet.new() msg = self._default_msg() env = Envelope.create_from_message(message=msg, signing_key=sk) self.assertTrue(env.verify_seal()) self.assertEqual(env.message, msg) self.assertEqual(env.seal.verifying_key, vk)
def test_create_with_envelope(self): """ Tests creating a message with an envelope produces an object with the expected properties """ sk, vk = wallet.new() tx = StandardTransactionBuilder.random_tx() sender = 'me' env = Envelope.create_from_message(message=tx, signing_key=sk) cmd = ReactorCommand.create_cmd('some_cls', 'some_func', envelope=env) self.assertTrue(ReactorCommand.envelope, env)
def _package_reply(self, reply: MessageBase, req_env: Envelope) -> Envelope: """ Convenience method to create a reply envelope. The difference between this func and _package_msg, is that in the reply envelope the UUID must be the hash of the original request's uuid (not some randomly generated int) :param reply: The reply message (an instance of MessageBase) :param req_env: The original request envelope (an instance of Envelope) :return: An Envelope instance """ self.log.debug("Creating REPLY envelope with msg type {} for request envelope {}".format(type(reply), req_env)) request_uuid = req_env.meta.uuid reply_uuid = EnvelopeAuth.reply_uuid(request_uuid) return Envelope.create_from_message(message=reply, signing_key=self.signing_key, verifying_key=self.verifying_key, uuid=reply_uuid)
def test_create_from_objects(self): """ Test that create returns an object with expected properties """ seal = self._default_seal() meta = self._default_meta() message = self._default_msg() env = Envelope._create_from_objects(seal=seal, meta=meta, message=message.serialize()) self.assertEqual(env.seal, seal) self.assertEqual(env.meta, meta) self.assertEqual(env.message, message)
def test_lazy_serialize(self): """ Tests that creating an envelope from_bytes does not try to repack the underlying struct when serialize is called. This is b/c the serialize() func should be 'cached' with the binary data passed into from_bytes """ sk, vk = wallet.new() message = self._default_msg() env = Envelope.create_from_message(message=message, signing_key=sk, verifying_key=vk) env_binary = env.serialize() clone = Envelope.from_bytes(env_binary) clone._data = MagicMock() clone_binary = clone.serialize( ) # this should not pack the capnp struct (_data) again # The Envelope Kitchen Sink lol -- make sure no serialization related API is called clone._data.as_builder.assert_not_called() clone._data.to_bytes_packed.assert_not_called() clone._data.to_bytes.assert_not_called()
def send_pub_env(self, filter: str, envelope: Envelope, protocol: str = 'tcp', port: int = DEFAULT_PUB_PORT, ip: str = ''): """ Publish envelope with filter frame 'filter'. :param filter: A string to use as the filter frame :param envelope: An instance of Envelope """ ip = ip or self.ip url = self._build_url(protocol=protocol, port=port, ip=ip, vk='') self.manager.executors['SubPubExecutor'].send_pub( url=url, filter=filter, data=envelope.serialize())
def test_validate_bad_seal(self): meta = self._default_meta() message = self._default_msg() sk, vk = wallet.new() sk2, vk2 = wallet.new() signature = EnvelopeAuth.seal(signing_key=sk, meta=meta, message=message) seal = Seal.create(signature=signature, verifying_key=vk2) env = Envelope._create_from_objects(seal=seal, meta=meta, message=message.serialize()) self.assertFalse(env.verify_seal())
def test_verify_seal(self): """ Tests verify seal with a valid signature """ meta = self._default_meta() message = self._default_msg() sk, vk = wallet.new() signature = EnvelopeAuth.seal(signing_key=sk, meta=meta, message=message) seal = Seal.create(signature=signature, verifying_key=vk) env = Envelope.create_from_message(message=message, signing_key=sk, verifying_key=vk) self.assertTrue(env.verify_seal())
def test_validate_envelope(self): """ Tests validate envelope function """ meta = self._default_meta() message = self._default_msg() sk, vk = wallet.new() signature = EnvelopeAuth.seal(signing_key=sk, meta=meta, message=message) seal = Seal.create(signature=signature, verifying_key=vk) env = Envelope._create_from_objects(seal=seal, meta=meta, message=message.serialize()) env.validate()
def test_verify_seal_invalid(self): """ Tests verifying a seal with an invalid signature """ meta = self._default_meta() message = self._default_msg() sk, vk = wallet.new() sk_prime = 'A' * 64 signature = EnvelopeAuth.seal(signing_key=sk_prime, meta=meta, message=message) seal = Seal.create(signature=signature, verifying_key=vk) env = Envelope._create_from_objects(seal=seal, meta=meta, message=message.serialize()) env.verify_seal()
def test_create_from_message_bad_keypair(self): """ test create_from_message passing in sk does and incorrect vk """ sk, vk = wallet.new() sk1, vk1 = wallet.new() msg = self._default_msg() sender = 'dat boi' env = Envelope.create_from_message(message=msg, signing_key=sk, verifying_key=vk) # no error self.assertEqual(env.seal.verifying_key, vk) self.assertRaises(Exception, Envelope.create_from_message, msg, sk, sender, vk1) self.assertNotEqual(env.seal.verifying_key, vk1)
def _recv_request_env(self, header: str, envelope: Envelope): self.log.debug("Recv REQUEST envelope with header {} and envelope {}".format(header, envelope)) self.call_on_mp(callback=StateInput.REQUEST, header=header, envelope_binary=envelope.serialize())
def _recv_pub_env(self, header: str, envelope: Envelope): self.log.debug("Recv'd pub envelope with header {} and env {}".format(header, envelope)) self.call_on_mp(callback=StateInput.INPUT, envelope_binary=envelope.serialize())
def envelope(self): if self._data.envelope.which() == 'unset': return None else: return Envelope.from_bytes(self._data.envelope.data)