Node - SkycoinProject/skywire GitHub Wiki

Overview

A Skywire node is an integral part of the Skywire network and is represented by a key pair (using the secp256k1 curve). It handles Transports to remote nodes, sets up routes and loops (via Routing Rules and interaction with the Setup Node), and manages Apps.

Applications

Each App is it's own executable that communicates with an App Node using a pair of POSIX pipes. Piped connection is setup on App startup and inherited by a forked App process using file descriptor 3 and 4. Setup process for a forked App is handled by the app package.

    [Skywire Node]
    /      |     \
   /       |      \
[App 1] [App 2] [App 3]

App Programming Interface

App programming interface exposes methods for Apps to connect to a piped connection, perform handshakes and exchange data with remote nodes.

App interface exposes the following methods:

// Addr implements net.Addr for App connections.
type Addr struct {
	PubKey transport.PubKey `json:"pk"`
	Port   uint16           `json:"port"`
}

// LoopAddr stores addressing parameters of a loop package.
type LoopAddr struct {
	Port   uint16 `json:"port"`
	Remote Addr   `json:"remote"`
}

// Packet represents message exchanged between App and Node.
type Packet struct {
	Addr    *LoopAddr
	Payload []byte
}

// Config defines configuration parameters for an App
type Config struct {
	AppName         string
	AppVersion      string
	ProtocolVersion string
}

// Setup sets up an app using default pair of pipes and performs handshake.
func Setup(config *Config) (*App, error) {}

// Accept awaits for incoming loop confirmation request from a Node and
// returns net.Conn for a received loop.
func (app *App) Accept() (net.Conn, error) {}

// Dial sends create loop request to a Node and returns net.Conn for created loop.
func (app *App) Dial(raddr *Addr) (net.Conn, error) {}

// Receive reads a single app packet.
func (app *App) Receive() (addr *Addr, payload []byte, err error) {}

// Close implements io.Closer for App.
func (app *App) Close() error {}

App to Node Communication protocol

Communication between Node and an App happens over the piped connection using binary multiplexed protocol.

The following is the format of an App Packet:

| Packet Len | Type   | Message ID | JSON Body |
| 2 bytes    | 1 byte | 1 byte     | ~         |
  • Packet Len specifies the total packet length in bytes (exclusive of the Packet Len field).
  • Type specifies the App Packet Type.
  • Message ID specifies multiplexing ID of a message, response for this message should contain the same ID.
  • JSON Body is the packet body (in JSON format) that is unique depending on the packet type.

App Packet Types Summary:

Type Name
0x00 Init
0x01 CreateLoop
0x02 ConfirmLoop
0x03 Send
0x04 Close
0xfe ResponseFailure
0xff ResponseSuccess

App Server

The AppServer handles communication between local and remote Apps. It also manages and administers local Apps. It interacts with a Router and identifies loops via the App routing rule retrieved from the routing table. The App rule is structured as follows;

| expiry  | r-type | resp-rid | remote-pk | remote-port | local-port |
| 8 bytes | 1 byte | 4 bytes  | 33 bytes  | 2 bytes     | 2 bytes    |

The App Server not only forwards Packets between Apps and remote entities, but it can also be instructed by the end user to send Quit signals to the Apps. Apps can also request to open Loops with remote/local Apps.

Router

The Router uses the Transport Manager and the Routing Table internally, and is responsible for the handling incoming Packets (either from external nodes via transports, or internally via the AppServer), and also the process of setting up routes.

Regarding the Route setup process, a router interacts with a trusted Setup Nodes.

Every transport created or accepted by the Transport Manager is handled by the Router. All incoming packets are cross-referenced against Routing Table and either forwarded to next Node or to a local App.

Routing Table

A Routing Table is unique for a given Node's public key. It is a key-value store in which the key is the Route ID and the value is the Routing Rule for the given Route ID.

There are two types of Routing Rules: App and Forward.

  • App rules are identified by their unique <r-type> value of 0x00. A packet which contains a Route ID that associates with a App rule is to be sent to a local App.
  • Forward rules are identified by their unique <r-type> value of 0x01. A packet which contains a Route ID that associates with a Forward rule is to be forwarded.
Action Key (Route ID) Value (Routing Rule)
App <rid>
4 bytes
<expiry><r-type><resp-rid><loop-data>
48 bytes
Forward <rid>
4 bytes
<expiry><r-type><next-rid><next-tid>
29 bytes
  • <rid> is the Route ID uint32 key (represented by 4 bytes) that is used to obtain the routing rules for the Packet.
  • <expiry> contains the epoch time (8 bytes) of when the rule is to be discarded (or becomes invalid).
  • <r-type> specifies the type of Routing Rule (1 byte). Currently there are two possible routing rule types; App (0x00) and Forward (0x01).
  • <resp-rid> is the Route ID (4 bytes) that is the Route ID key for the reserve Route of the loop.
  • <loop-data> identifies and classifies the loop. It contains the following sub-fields; <remote-pk><remote-port><local-port>.
    • <remote-pk> is the remote edge public key in which this route/loop is associated with. It is represented by 33 bytes.
    • <remote-port> is the remote port in which this route/loop is associated with. It is represented by 2 bytes.
    • <local-port> is the local port in which this route/loop is associated with. It is represented by 2 bytes.
  • <next-rid> is the Route ID that is to replace the <rid> before the Packet is to be forwarded.
  • <next-tid> represents the transport which the packet is to be forwarded to. A Transport ID is 16 bytes long.

Every time a Skywire Node receives a packet, it performs the following steps:

  1. Obtain the <rid> from the Packet, and uses this value to obtain a routing rule entry from the routing table. If no routing rule is found, or the routing rule has already expired (via checking the <expiry> field), the Packet is then discarded.
  2. Obtains the <r-type> value to determine how the packet is to be dealt with. If the <r-type> value is 0x00, the packet is then to be sent to the local App Server with the Routing Rule. If the <r-type> value is 0x01, the packet is to be forwarded; continue on to step 3.
  3. Obtain the <next-rid> from the Routing Rule and replace the <rid> from the Route ID field of the Packet.
  4. Forward the Packet to the referenced transport specified within <next-tid>.

Transport Management

The transport is responsible for managing and logging transports.

As the TransportManager needs to interact with the Transport Discovery and other Skywire Nodes, it has access to the local node's public and private key identity.

Transport Manager Procedures

The transport manager is responsible for keeping track of established transports (via the transport.Entry and the transport.Status structures). The transport.Entry structure describes and identifies transports, while transport.Status keeps track of whether the transport is up or down (based on the perspective of the local node).

If the Transport Manager wishes to confirm transport information, it can query the Transport Discovery via the GET /transports/edge:<public-key> endpoint. Note that it is expected of the Transport Manager to call this endpoint on startup.

When a transport is "closed" it is only considered "down", not "destroyed".

The following highlights detailed startup and shutdown procedures of a Transport Manager;

Startup:

On startup, the TransportManager Calls the Transport Discovery to ensure that it is up to date. Then it needs to attempt to establish (or re-establish) transports to the relevant remote nodes.

When re-establishing a Transport, the transport.Entry used should be that also previously stored in the Transport Discovery.

Once connected, the TransportManager updates it's Status of the given Transport and set is_up to true.

The startup logic is triggered when Start is called.

Shutdown:

On shutdown, the first step is to update the Transport Statuses to "down" via the Transport Discovery. Then Transports to remote nodes is to be closed (with a timeout, in which after, the transport in question is forcefully closed).

Loops

A loop is a set of two routes that connect two applications running on different Skywire nodes. It comprises a forward (A-B) and a return (B-A) route.

Each loop is symmetrically encrypted with Noise. Specifically, we use the KK fundamental handshake pattern. The first noise message should be provided by initiator in the request to create a new loop, this messages will be setup to responder in a loop confirmation request. Responder should send second noise message which will be returned to initiator in a loop confirmation request.

⚠️ **GitHub.com Fallback** ⚠️