time-warp is developed to provide
a reliable networking layer with different levels of abstractions. Another
important objective of
time-warp is to provide an easy way to write and run
tests for distributed systems using emulation mode, which should be flexible
enough to support various scenarios (tunable network delays, disconnects,
other real-time conditions).
time-warp is split up into two main parts:
- Network functionality.
interfaces allow to abstract from language-specific details of implementation
of the basic functions.
This innovation allows to launch the same code both in production and testing environment, where the latter allows to emulate time, threads, networking, etc.
This layer is written on top of network-transport and provides network capabilities for the application layer. It is split up into two sub-layers: lower and upper.
This sub-layer is a direct wrapper over
package, and it provides a convenient interface which allows to initiate lightweight
connection and send/receive data on it. Please read Network Transport Layer
guide for more info.
It supports two types of connections, unidirectional and bidirectional.
Unidirectional connections allow to send a stream of bytes without waiting for peer’s response.
Upon unidirectional connection initialization, node sends
+------------------+ | UNI | +------------------+ | 'U' :: Word8 |
Word8 represents 8-bit unsigned integer value.
Bidirectional connections allow both nodes to send and receive bytes to each other.
withInOutChannel establishes connection, executes given action
with given handle to send and receive bytes on connection, and automatically
closes connection on action’s end. Its usage requires a handshake, which
contains the following steps.
First, the initiator sends a connection request, which has the following structure:
+------------------+-----------------+ | `BI_SYN` | Nonce | +------------------+-----------------+ | 'S' :: Word8 | Word64 (BE) |
Nonce is randomly generated.
Then the peer sends acknowledgement, with the following structure:
+------------------+-----------------+--------------+ | `BI_ACK` | Nonce | PeerData | +------------------+-----------------+--------------+ | 'A' :: Word8 | Word64 (BE) | Generic |
Nonce is the same nonce which came from request.
If the initiator receives the acknowledgement with correct nonce, a conversation is started.
The opposite case could take place if the node have never sent any request on
that nonce (peer made a protocol error). It could also be that the node did send
BI_SYN, but its handler for that conversation had already finished. That’s
normal, and the node should ignore this acknowledgement.
PeerData is some additional information that is sent from the peer and parsed
by the initiator.
time-warp gives you an ability to provide some binary data
during handshake which then can be used by your application in different ways.
The structure of this data is generic. Application Level
how Cardano SL uses
Before talking about upper layer, let’s describe messaging.
In order to specify different handlers for various message types, sent messages
Message interface, defining two methods:
messageName, it returns unique message identifier, which is sent along with the message itself and allows receiver to select correct handler to process this message.
formatMessage, it provides description of message, for debug purposes.
This sub-layer enables message exchange. It provides conversation style of communication. This style uses capabilities of bidirectional connection and allows to send/receive messages (one or more). For a single conversation, types of incoming and outgoing messages are fixed. In this case, the initiator node sends the message name once, and then both the initiator and the peer send required messages.
Network events processing is initiated by
node function. This function uses two important concepts: worker
Worker is some action which performs as the initiator of all
communication, being supplied with
SendActions type which provides
This function initiates conversation, executing given action with
provided and closing conversation once action completes. In turn,
recv functions to communicate with peer.
Listener is a handler for a message. Each listener remembers type of related message, and several listeners with non-overlapping message types could be defined.
Please see complete example for technical details.
time-warp doesn’t rely on any predefined serialization strategy, but rather
allows users to use their own.
To define custom serialization, a user should create special data type, the
so-called packing type, and implement
Serializable interface for it. This interface defines
packMsg, represents the way how to pack the data to raw bytestring.
unpackMsg, represents the way how to unpack the data.