Tutorial: How Anki Vector communicates? Part 2
Part 2: Secure Connections: How to securely exchange messages in a distributed system
In the last article in this tutorial, we looked at protocol buffers. To recap, protocol buffers define the message format by which two distributed systems based on two different environments (such as operating systems/ programming languages) can talk to each other. Now, let us try to understand how a secure connection can be established to the Anki Vector robot to facilitate message delivery via protocol buffers. First, let us revisit the client-server paradigm of communications between two systems.
Client-Server Model of Distributed computing
In the client-server model, the server provides a certain service which is always readily available. In other words, it is always waiting to serve a customer, akin to your local departmental store whose doors are always open and the cashier is always ready to serve a customer. The client avails of the service that the server provides. In order to avail the service, the client must connect to the port where the server is listening, and send his request, and wait for the server’s response. Multiple clients can simultaneously connect to the server and request the service. Btw, in case it was not clear, in the context of our discussion, the Anki Vector is the server, and the laptop aiming to connect to Anki Vector is the client. The following diagram summarizes the architecture of client server communications.
gRPC
Now, let us visit how a secure connection can be created from the client to the server. The secure connections are created and managed using gRPC (Google Remote Procedure Call). gRPC uses protocol buffers as its underlying messaging format, and creates a framework by which a client can invoke an API of the server seamlessly, almost as if it is calling its own routine. All subtleties like the client-server network transaction, differences in environments and operating systems between the client and the server get hidden with the use of gRPC. Thus, gRPC vastly simplifies the programming required for a distributed transaction by abstracting the key technical details that are required for such a transaction. One of the key technical details is that of connection management. Now let us see how the Anki Vector SDK establishes a secure gRPC connection to Anki Vector.
gRPC Secure Connections
Let us quickly look at the code from the Anki Vector SDK. The detailed code of how a connection is set up is in this file. Let us look at some small snippets of code. We will then dig into the individual instructions and try to make a sense of how this all works.
with open(self.cert_file, 'rb') as cert:
trusted_certs = cert.read()
# Pin the robot certificate for opening the channel
channel_credentials = \ aiogrpc.ssl_channel_credentials(root_certificates=trusted_certs)
# Add authorization header for all the calls
call_credentials = aiogrpc.access_token_call_credentials(self._guid)
credentials = aiogrpc.composite_channel_credentials(channel_credentials, call_credentials)
self._logger.info(f"Connecting to {self.host} for {self.name} using {self.cert_file}")
self._channel = aiogrpc.secure_channel(self.host, credentials,
options=(("grpc.ssl_target_name_override", self.name,),))
# Verify the connection to Vector is able to be established (client-side)
try:
# Explicitly grab _channel._channel to test the underlying grpc channel directly
grpc.channel_ready_future(self._channel._channel).
result(timeout=timeout) # pylint: disable=protected-access
except grpc.FutureTimeoutError as e:
raise VectorNotFoundException() from e
self._interface = client.ExternalInterfaceStub(self._channel)
Now, let us look at this code in more detail…
Understanding the code
The first step is to load the certificates for secure connections. When you first set up the Anki SDK on your laptop to work with a Anki Vector, a certificate is generated. As an example, I have two Vector robots, and my certificate files look as follows:
ls -ltr ~/.anki_vector/Vector-*.cert
-rw------- 1 amitabhabanerjee staff 1216 Apr 18 2020 /Users/amitabhabanerjee/.anki_vector/Vector-G4C7-0080230c.cert -rw------- 1 amitabhabanerjee staff 1216 Jun 7 2020 /Users/amitabhabanerjee/.anki_vector/Vector-J6A8-00902161.cert
Vector-G4C7 and Vector-J6A8 are the two Vectors I own.
The following two statements load the certificate set in cert_file. The Vector SDK uses the aiogrpc library which provides an asyncio wrapper for grpc (If you want to learn about asynchronous operations in Python, read this. More on why asynchronous operations need to be supported over grpc later).
with open(self.cert_file, 'rb') as cert:
trusted_certs = cert.read()
# Pin the robot certificate for opening the channel
channel_credentials = \
aiogrpc.ssl_channel_credentials(root_certificates = \
trusted_certs)
The next four statements set up a SSL connection between my laptop and Vector. SSL connections work using public/private key cryptography. This ensures that all data exchanged between my laptop and Vector is encrypted. As an example, if I access Vector’s video feed, the encryption ensures that no one can hack into my home network and view the video feed. An elementary introduction to SSL can be found here.
# Add authorization header for all the calls
call_credentials=\
aiogrpc.access_token_call_credentials(self._guid)
credentials =\ aiogrpc.composite_channel_credentials(channel_credentials,
call_credentials) self._logger.info(f"Connecting to {self.host} for {self.name} using {self.cert_file}")
self._channel =\
aiogrpc.secure_channel(self.host, credentials,
options=\(("grpc.ssl_target_name_override", self.name,),))
The secure_channel() command establishes a secure channel between my laptop and the Vector robot with all messages from/to the Vector SDK encrypted/decrypted using the provided certificate.
The next two instructions set the channel ready for asynchronous instructions and set an interface to talk to the channel.
grpc.channel_ready_future(self._channel._channel)
self._interface = client.ExternalInterfaceStub(self._channel)
Remember from the previous part, that ExternetInterfaceStub is created by compiling externel_interface.proto and provides a stub with which the Vector SDK can communicate using gRPC.
Once we have this _interface, communicating with Anki Vector becomes very easy. We simply have to grab this interface and call the RPC predefined in external_integration.py (More details on how RPCs are defined later). The code looks like:
conn = anki_vector.connection.Connection("Vector-XXXX",
"XX.XX.XX.XX:443", "/path/to/file.cert", "<guid>")
conn.connect()
# Run your commands
async def play_animation():
# Run your commands
anim = \
anki_vector.messaging.protocol.Animation(name=
"anim_pounce_success_02")
anim_request = \
anki_vector.messaging.protocol.PlayAnimationRequest(
animation=anim)
return await conn.grpc_interface.PlayAnimation(anim_request)
# This needs to be run in an asyncio loop
conn.run_coroutine(play_animation()).result()
In the above code a protocol buffer is first created for Animation in the following line:
anki_vector.messaging.protocol.Animation(name= "anim_pounce_success_02")
And then we invoke the RPC call to PlayAnimation. We then wait for this routine to be executed on Vector and return back a success. On the side of Vector, the RPC will invoke the set of actions required to execute the animation. Once the animation is played out, Vector’s routine stops and the RPC is terminated. You can see how easy the code becomes with the use of protocol buffers and gRPC. The instructions here are seemingly unaware of all that goes underneath the covers for a distributed transaction and all those details are abstracted away.
In the next part, we will cover the question of how a RPC is declared. We will consider this in the next part. Please consider subscribing to my channel to get regular updates. I promise that you will get a lot of learning material in exchange for the cost of a lunch.