Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/linter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ jobs:

- name: Run CSpell on changed files
# This action also annotates the PR
uses: streetsidesoftware/cspell-action@9cd41bb518a24fefdafd9880cbab8f0ceba04d28 # 8.3.0
uses: streetsidesoftware/cspell-action@de2a73e963e7443969755b648a1008f77033c5b2 # 8.4.0
with:
check_dot_files: explicit
suggestions: true
Expand Down
1 change: 1 addition & 0 deletions content/foundations/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"shards",
"limits",
"config",
"network",
"web3",
"proofs",
"system",
Expand Down
314 changes: 314 additions & 0 deletions content/foundations/network/adnl-tcp.mdx

Large diffs are not rendered by default.

276 changes: 276 additions & 0 deletions content/foundations/network/adnl-udp.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,276 @@
---
title: "ADNL over UDP: internode communication"
sidebarTitle: "ADNL UDP"
---

ADNL over UDP is the protocol used by TON nodes to communicate with each other. It serves as the foundation for higher-level protocols such as the [distributed hash table (DHT)](https://github.com/ton-blockchain/ton/tree/v2026.06/dht) and [Reliable Large Datagram Protocol (RLDP)](https://github.com/ton-blockchain/ton/tree/v2026.06/rldp2).

Unlike [ADNL over TCP](/foundations/network/adnl-tcp), the UDP implementation uses channels for ongoing communication and establishes the connection when the first data exchange occurs. Initial packets use public-key encryption and identity signatures, while established channels use directional symmetric keys.

## Transport limits

TON applies these limits before or during packet construction:

| Limit | Size | Purpose |
| ------------------- | ----------: | --------------------------------------------------------------- |
| ADNL message MTU | 1,024 bytes | Maximum `adnl.Message` size without fragmentation |
| UDP transport MTU | 1,440 bytes | Maximum UDP payload passed to the network manager |
| Reassembled message | 8,192 bytes | Maximum serialized message accepted through `adnl.message.part` |

ADNL over UDP remains an unreliable datagram protocol — although sequence numbers reject duplicates and stale packets, the missing packages are not retransmitted.

## Peer endpoint discovery

A sender needs the peer's full public key, 256-bit ADNL short identifier, and a usable endpoint. Static bootstrap entries are available in the [`dht.nodes` section of the global configuration](https://ton-blockchain.github.io/global.config.json). Nodes can subsequently update peer information through address lists and the DHT.

For `adnl.address.udp`, `ip` is an unsigned 32-bit IPv4 value and `port` is a 16-bit UDP port encoded in a TL `int` field.

For example, a `dht.nodes` entry can contain:

```json
{
"@type": "dht.node",
"id": {
"@type": "pub.ed25519",
"key": "fZnkoIAxrTd4xeBgVpZFRm5SvVvSx7eN3Vbe8c83YMk="
},
"addr_list": {
"addrs": [
{
"@type": "adnl.address.udp",
"ip": 1091897261,
"port": 15813
}
]
}
}
```

To connect to a node, [lite-client](#interacting-with-ton-nodes):

1. Takes its public `key`, `ip` address, and the `port`.
1. Decodes the Ed25519 public key from base64 `key`.
1. Converts the `ip` value to dotted-decimal format. For example, `1091897261` becomes `65.21.7.173`.
1. Combines the IP address with the port. For example, `65.21.7.173:15813`.
1. Establishes a UDP connection with the resulting `IP:PORT` address.

## Packet contents

UDP envelopes encrypt a boxed `adnl.packetContents` object:

```tl
adnl.packetContents
rand1:bytes -- random 7 or 15 bytes
flags:# -- bit flags for field presence
from:flags.0?PublicKey -- sender's public key
from_short:flags.1?adnl.id.short -- sender's ID
message:flags.2?adnl.Message -- single message
messages:flags.3?(vector adnl.Message) -- multiple messages
address:flags.4?adnl.addressList -- sender's address list
priority_address:flags.5?adnl.addressList -- priority address list
seqno:flags.6?long -- packet sequence number
confirm_seqno:flags.7?long -- last received seqno
recv_addr_list_version:flags.8?int -- address version
recv_priority_addr_list_version:flags.9?int -- priority address version
reinit_date:flags.10?int -- connection reinitialization date
dst_reinit_date:flags.10?int -- peer's reinitialization date
signature:flags.11?bytes -- ed25519 signature
rand2:bytes -- random 7 or 15 bytes
= adnl.PacketContents
```

`rand1` and `rand2` are independently generated 7-byte or 15-byte values. A packet can contain either `message` or `messages`, but not both. Both reinitialization dates are present when flag bit 10 is set.

## Envelope before channel establishment

A UDP packet sent before channel establishment has this wire layout:

| Field | Size | Description |
| ---------------------------- | -------: | ------------------------------------------------------ |
| Destination ADNL short ID | 32 bytes | SHA-256 of the destination identity's boxed public key |
| Ephemeral Ed25519 public key | 32 bytes | Fresh key generated by the public-key encryptor |
| Plaintext hash | 32 bytes | SHA-256 of the serialized `adnl.packetContents` |
| Encrypted packet contents | Variable | AES-CTR encryption of `adnl.packetContents` |

The last 3 fields are the output of TON's Ed25519 encryptor. It derives a shared secret from the ephemeral private key and the destination identity public key. The destination uses its identity private key and the transmitted ephemeral public key to derive the same secret.

The sender signs the boxed packet contents with the source identity private key. Signature calculation clears `signature` and flag bit 11, serializes the remaining object, and signs those bytes. The sender then inserts the signature and serializes the object again before encryption.

The receiver performs these checks before delivering messages:

1. Decrypts the envelope with the private key selected by the destination short ID.
1. Verifies the plaintext SHA-256 value.
1. Parses `adnl.packetContents` and validates its flags.
1. Resolves the source public key and verifies the packet signature.
1. Applies re-initialization, sequence-number, acknowledgment, and address-list checks.

<Callout type="note">
[Ed25519 envelope implementation in the TON monorepo](https://github.com/ton-blockchain/ton/blob/v2026.06/keys/encryptor.cpp#L31-L110) defines the exact key and initialization-vector derivation.
</Callout>

## Channel negotiation

Peers negotiate channels with these messages:

```tl
adnl.message.createChannel
key:int256
date:int
= adnl.Message;

adnl.message.confirmChannel
key:int256
peer_key:int256
date:int
= adnl.Message;
```

There, `key` is a freshly generated Ed25519 channel public key and `date` is its Unix creation time. In `confirmChannel`, `peer_key` must equal the public key proposed by the peer — confirmations for another or superseded key are rejected.

A typical first packet contains `createChannel` and an application message such as `adnl.message.query`. The application message is not required to be a query — channel negotiation is appended to whichever messages are queued for the peer.

The numeric constructor ID of `adnl.message.createChannel` is `0xe673c3bb`, and its little-endian wire bytes are `bb c3 73 e6`. Constructor IDs elsewhere on this page follow the same numeric-ID and wire-byte distinction.

Serialized `createChannel` example:

```text
bbc373e6 -- createChannel constructor wire bytes
d59d8e3991be20b54dde8b78b3af18b379a62fa30e64af361c75452f6af019d7 -- key
555c8763 -- date
```

An accompanying query uses this schema:

```tl
adnl.message.query query_id:int256 query:bytes = adnl.Message;
```

The actual request, such as `dht.getSignedAddressList`, is serialized inside `query`. Its sample encoding is:

```text
7af98bb4 -- adnl.message.query constructor wire bytes
d7be82afbc80516ebca39784b8e2209886a69601251571444514b7f17fcd8875 -- random query_id
04 ed4879a9 000000 -- query: 4-byte payload and 3-byte padding
```

For this exchange, the peer responds with `adnl.message.confirmChannel`, which provides its channel public key, and `adnl.message.answer`, which carries the query response.

## Channel key derivation

Each peer derives a 32-byte shared secret from its channel private key and the other peer's channel public key. Two AES keys are then defined:

```text
shared_key = shared secret
reversed_key = shared secret with its 32 bytes reversed
```

Direction is selected by comparing the peers' ADNL short identity IDs as unsigned 256-bit values. The comparison does not use channel-key IDs.

| Condition | Outgoing encryption key | Incoming decryption key |
| ----------------------------- | ----------------------- | ----------------------- |
| Local ADNL ID \< peer ADNL ID | `reversed_key` | `shared_key` |
| Local ADNL ID > peer ADNL ID | `shared_key` | `reversed_key` |

The incoming and outgoing channel IDs are the ADNL short IDs of their respective AES keys.

<Callout type="note">
[Channel implementation in the TON monorepo](https://github.com/ton-blockchain/ton/blob/v2026.06/adnl/adnl-channel.cpp#L30-L64) defines the derivation and comparison.
</Callout>

## Channel packet envelope

An established channel packet has this wire layout:

| Field | Size | Description |
| ------------------------- | -------: | ----------------------------------------------- |
| Outgoing channel ID | 32 bytes | ADNL short ID of the directional AES key |
| Plaintext hash | 32 bytes | SHA-256 of the serialized `adnl.packetContents` |
| Encrypted packet contents | Variable | AES-CTR encryption of `adnl.packetContents` |

Channel packets omit the source identity and signature because possession of the directional key authenticates the sender. They normally omit reinitialization dates. They can still contain a single message or message vector, sequence fields, address lists, priority address lists, and received-version fields.

## Sequence and session state

- `seqno` identifies a packet within the current reinitialization epoch. TON tracks the highest received number plus a 64-packet mask. It rejects duplicates and packets more than 63 positions behind the highest accepted number.

- `confirm_seqno` acknowledges the highest packet number accepted from the peer. A value greater than the highest locally sent sequence number is invalid. Acknowledgment updates state but does not cause ADNL to retransmit missing packets.

- `reinit_date` identifies the sender's epoch, and `dst_reinit_date` states which destination epoch the sender has observed. A newer valid peer epoch resets channel, sequence, acknowledgment, address-version, and partial-message state. Reinitialization metadata is carried in signed packets sent outside a channel.

## Address-list propagation

Packets can advertise ordinary and priority address lists. The `recv_addr_list_version` fields tell the peer which versions have already been received, allowing it to omit unchanged lists.

```tl
adnl.addressList
addrs:(vector adnl.Address)
version:int
reinit_date:int
priority:int
expire_at:int
= adnl.AddressList;
```

Supported address variants include IPv4 UDP, IPv6 UDP, ADNL tunnels, reverse connectivity, and IPv4 QUIC endpoints. Nodes reject address lists whose serialized representation exceeds the implementation limit.

## Message types

### Query and answer, `adnl.message.query`

```tl
adnl.message.query query_id:int256 query:bytes = adnl.Message;
adnl.message.answer query_id:int256 answer:bytes = adnl.Message;
```

`query_id` correlates one answer with one outstanding query. Higher-level protocol data is serialized inside `query` or `answer`.

### Part, `adnl.message.part`

```tl
adnl.message.part
hash:int256
total_size:int
offset:int
data:bytes
= adnl.Message;
```

The blockchain fragments the complete boxed serialization of an `adnl.Message`, not only its application payload. Reassembly has these constraints:

- The first accepted part has offset `0`.
- Parts for a message arrive contiguously and in order.
- Only one hash is assembled per peer at a time.
- `total_size` does not exceed 8,192 bytes.
- The final SHA-256 value equals `hash`.
- The reassembled bytes parse as one boxed `adnl.Message`.

### Custom, `adnl.message.custom`

```tl
adnl.message.custom data:bytes = adnl.Message;
```

`custom` delegates the payload to a subscribed higher-level protocol. RLDP uses custom messages because its transfer protocol does not follow a single-request, single-response exchange.

### No-operation, `adnl.message.nop`

```tl
adnl.message.nop = adnl.Message;
```

`nop` carries no application data.

### Reinitialization, `adnl.message.reinit`

```tl
adnl.message.reinit date:int = adnl.Message;
```

`reinit` asks the peer to adopt a newer session epoch — stale dates have no effect.

## See also

- [ADNL packet schema in the TON monorepo](https://github.com/ton-blockchain/ton/blob/v2026.06/tl/generate/scheme/ton_api.tl#L77-L119)
- [UDP peer state machine in the TON monorepo](https://github.com/ton-blockchain/ton/blob/v2026.06/adnl/adnl-peer.cpp)
- [ADNL overview](/foundations/network/adnl)
- [ADNL over TCP](/foundations/network/adnl-tcp)
61 changes: 61 additions & 0 deletions content/foundations/network/adnl.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
---
title: "Abstract Datagram Network Layer"
sidebarTitle: "ADNL"
---

The Abstract Datagram Network Layer (ADNL) is the core networking protocol of the TON network. It is a peer-to-peer, unreliable datagram protocol that operates over both [UDP](https://en.wikipedia.org/wiki/User_Datagram_Protocol) and [TCP](https://en.wikipedia.org/wiki/Transmission_Control_Protocol).

Higher-level protocols such as [Reliable Large Datagram Protocol (RLDP)](https://github.com/ton-blockchain/ton/tree/v2026.06/rldp2) and the [distributed hash table (DHT)](https://github.com/ton-blockchain/ton/tree/v2026.06/dht) are built on top of ADNL.

## ADNL address

Each participant in the network has a 256-bit ADNL address. ADNL allows sending and receiving [datagrams](https://en.wikipedia.org/wiki/Datagram) using only these addresses, hiding the underlying IP addresses and ports.

An ADNL address is derived as:

```text
address = SHA-256(type_id || public_key)
```

The hash input is the complete boxed TL serialization of the public key, and the `type_id` is a `uint32` constructor ID serialized in little-endian order. For example, the Ed25519 public-key constructor has numeric ID `0x4813b4c6`, so its wire bytes are `c6 b4 13 48` followed by the 32-byte public key. The corresponding private key must be known to receive and decrypt messages sent to a given address.

<Callout
type="note"
title="Key format conversion"
>
ADNL generates, stores, and transmits Ed25519 keys over the network. For the [X25519 key agreement](https://en.wikipedia.org/wiki/Curve25519), TON internally converts the Ed25519 public key to a Montgomery public coordinate and derives an X25519 scalar from the Ed25519 private seed. Do not generate an unrelated X25519 key pair and relabel it as Ed25519.

- [Shared-secret implementation in the TON monorepo](https://github.com/ton-blockchain/ton/blob/v2026.06/crypto/Ed25519.cpp#L299-L370)
- [Conversion example in Kotlin](https://github.com/andreypfau/curve25519-kotlin/blob/f008dbc2c0ebc3ed6ca5d3251ffb7cf48edc91e2/src/commonMain/kotlin/curve25519/MontgomeryPoint.kt#L39)
</Callout>

## Peer identity

Each peer must have at least one identity. A peer may have multiple identities, but this is not required. Each identity consists of a key pair used for [Diffie-Hellman key exchange](https://en.wikipedia.org/wiki/Diffie%E2%80%93Hellman_key_exchange) between peers.

## Neighbor tables

A TON ADNL node maintains a neighbor table with information about known nodes, including their abstract addresses, public keys, IP addresses, and UDP ports. The node updates this table over time as it discovers new peers from query responses and removes outdated entries.

Address lists can advertise IPv4 UDP, IPv6 UDP, ADNL tunnel, reverse-connectivity, and IPv4 QUIC endpoints. Each list also carries `version`, `reinit_date`, `priority`, and `expire_at` fields. The version fields let peers omit address lists that the receiver has already observed.

## P2P protocol (ADNL over UDP)

ADNL over UDP is used by TON nodes to communicate with each other. Communication begins simultaneously with the first data exchange: the initiator sends a create-channel message and the peer confirms creation. Encryption keys for the channel are derived through Elliptic-curve Diffie-Hellman (ECDH) from the peers' channel keys.

UDP packets sent outside a channel are encrypted to the destination identity and signed by the source identity. After channel establishment, peers use directional symmetric keys and omit the per-packet signature.

Sequence numbers suppress duplicate and old packets, while acknowledgments report the highest accepted sequence number. ADNL does not retransmit missing datagrams. Protocols that require reliable or larger transfers use a higher-level protocol such as [RLDP](https://github.com/ton-blockchain/ton/tree/v2026.06/rldp2).

See [ADNL UDP](/foundations/network/adnl-udp) for channel establishment, packet structure, and message types.

## Client-server protocol (ADNL over TCP)

ADNL over TCP is used by clients to communicate with [liteservers](/nodes/overview). The client generates random AES-CTR session parameters and encrypts them to the server identity with a fresh ephemeral key. After the exchange, both sides use two AES-CTR cipher states to encrypt framed messages with SHA-256 integrity checks. The liteserver then authenticates the client's independently generated Ed25519 identity over the encrypted connection.

See [ADNL TCP](/foundations/network/adnl-tcp) for the full handshake flow, packet structure, liteserver query examples, and security considerations.

## See also

- [ADNL implementation in TON monorepo](https://github.com/ton-blockchain/ton/tree/v2026.06/adnl)
- [ADNL in The Open Network whitepaper](/foundations/whitepapers/ton/#3-1-abstract-datagram-network-layer)
9 changes: 9 additions & 0 deletions content/foundations/network/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"$schema": "../../../meta-schema.json",
"title": "Network protocols",
"pages": [
"adnl",
"adnl-tcp",
"adnl-udp"
]
}
Loading