diff --git a/docs.json b/docs.json index e90a96633..cd6a20b22 100644 --- a/docs.json +++ b/docs.json @@ -729,6 +729,14 @@ "foundations/shards", "foundations/limits", "foundations/config", + { + "group": "Network protocols", + "pages": [ + "foundations/network/adnl", + "foundations/network/adnl-tcp", + "foundations/network/adnl-udp" + ] + }, { "group": "Web3 services", "pages": [ @@ -1395,27 +1403,27 @@ }, { "source": "/v3/documentation/network/protocols/adnl/overview", - "destination": "https://old-docs.ton.org/v3/documentation/network/protocols/adnl/overview", + "destination": "/foundations/network/adnl", "permanent": true }, { "source": "/v3/documentation/network/protocols/adnl/low-level", - "destination": "https://old-docs.ton.org/v3/documentation/network/protocols/adnl/low-level", + "destination": "/foundations/network/adnl", "permanent": true }, { "source": "/learn/overviews/adnl", - "destination": "https://old-docs.ton.org/learn/overviews/adnl", + "destination": "/foundations/network/adnl", "permanent": true }, { "source": "/v3/documentation/network/protocols/adnl/tcp", - "destination": "https://old-docs.ton.org/v3/documentation/network/protocols/adnl/tcp", + "destination": "/foundations/network/adnl-tcp", "permanent": true }, { "source": "/v3/documentation/network/protocols/adnl/udp", - "destination": "https://old-docs.ton.org/v3/documentation/network/protocols/adnl/udp", + "destination": "/foundations/network/adnl-udp", "permanent": true }, { diff --git a/foundations/network/adnl-tcp.mdx b/foundations/network/adnl-tcp.mdx new file mode 100644 index 000000000..4f8f67fa2 --- /dev/null +++ b/foundations/network/adnl-tcp.mdx @@ -0,0 +1,247 @@ +--- +title: "ADNL TCP - liteserver communication" +sidebarTitle: "ADNL TCP" +--- + +import { Aside } from "/snippets/aside.jsx"; + +ADNL over TCP is used for communication with liteservers. + +## Packet structure + +Each ADNL TCP packet (except the handshake) has the following structure: + +| Field | Size | Description | +| ---------- | ----------------------- | ------------------------------------------------ | +| `size` | 4 bytes (little-endian) | Total packet size `N`, excluding this field | +| `nonce` | 32 bytes | Random bytes protecting against checksum attacks | +| `payload` | `N - 64` bytes | Actual data | +| `checksum` | 32 bytes | SHA-256 of `nonce \|\| payload` | + +The entire packet, including the size field, is encrypted with AES-CTR. + +After decryption, verify that the checksum matches by computing it independently. The handshake is an exception — see [Handshake](#handshake) below. + +## Connection establishment + +Prerequisites: + +- Server IP, port, and public key from the [global config](https://ton-blockchain.github.io/global.config.json) - the IP is a 32-bit integer and the public key is base64-encoded; +- A freshly generated Ed25519 private and public key pair. + +### Handshake + +1. The client performs a key agreement protocol (x25519) using its private key and the server's public key, selecting the key representation based on its `type_id`, to derive a `secret`. + +1. The client generates AES-CTR session parameters for both transmit (client -> server) and receive (server -> client) directions. Each direction uses a 32-byte key and a 16-byte nonce. The parameters are serialized into a 160-byte buffer: + + | Parameter | Size | + | ---------- | -------- | + | `rx_key` | 32 bytes | + | `tx_key` | 32 bytes | + | `rx_nonce` | 16 bytes | + | `tx_nonce` | 16 bytes | + | `padding` | 64 bytes | + + Fill the entire 160-byte buffer with random bytes. If any part is predictable, an attacker can perform an active man-in-the-middle attack using compromised AES-CTR session parameters. + +1. The client encrypts the session parameters (`aes_params`) using AES-256-CTR with a 128-bit big-endian counter. The encryption key and nonce are derived from the SHA-256 hash of `aes_params` and the ECDH `secret` — see [Handshake encryption](#handshake-encryption) for the exact derivation. + +1. The client sends a 256-byte handshake packet with the following structure: + + | Field | Size | Description | + | ------------------------------------------- | --------- | ------------------------------------- | + | Server key ID (`receiver_address`) | 32 bytes | Server peer identity | + | Client Ed25519 public key (`sender_public`) | 32 bytes | Client public key | + | `SHA-256(aes_params)` | 32 bytes | Integrity proof of session parameters | + | [`E(aes_params)`](#handshake-encryption) | 160 bytes | Encrypted session parameters | + +1. The server decrypts the session parameters and performs these checks: + + - It possesses the private key corresponding to `receiver_address`. + - `SHA-256(aes_params) == SHA-256(D(E(aes_params)))`. + + If any check fails, the server drops the connection. If all checks pass, the server sends an empty datagram to confirm ownership of the private key. + +After this exchange the connection is established. Data is serialized using [TL (Type Language)](https://core.telegram.org/mtproto/TL). + +### Cipher assignment + +Both sides derive two permanent AES-CTR ciphers from the 160-byte session parameters: + +| Cipher | Key (bytes) | Initialization vector (bytes) | Used for | +| -------- | ----------- | ----------------------------- | -------------------------------- | +| Cipher A | 0-31 | 64-79 | Server encrypts, client decrypts | +| Cipher B | 32-63 | 80-95 | Client encrypts, server decrypts | + +## Security considerations + +- **Handshake padding.** The 64-byte padding field in `aes_params` is unused in current implementations. It may have been intended to support future migration to alternative encryption primitives. +- **Session key derivation.** The encryption key is derived from both the static `secret` and `SHA-256(aes_params)`. Since `aes_params` is random for each session, this results in a unique encryption key per connection. However, the concatenation-based derivation combines subarrays of the secret and the hash, which is considered suboptimal by modern standards. +- **Datagram nonce.** In CTR mode, AES operates as a stream cipher, making bit-flipping attacks possible if the plaintext is known. The per-packet `nonce` mitigates this: even if an attacker reconstructs the key stream and computes a valid `SHA-256(buffer)`, they cannot forge a valid hash without knowing the random nonce. + +## Ping and pong + +Send a ping packet every 5 seconds to keep the connection alive. Without pings, the server terminates the connection during idle periods. + +Ping TL schema: `tcp.ping random_id:long = tcp.Pong` with schema ID `9a2b084d`, derived as the CRC32 of the schema string, little-endian. + +Example ADNL ping packet: + +| Field | Size | Value | +| ---------- | -------- | ------------------------ | +| `size` | 4 bytes | 76 (64 + 4 + 8) | +| `nonce` | 32 bytes | random | +| schema ID | 4 bytes | `9a2b084d` | +| request ID | 8 bytes | random uint64 | +| `checksum` | 32 bytes | SHA-256(nonce + payload) | + +Wait for [`tcp.pong`](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/ton_api.tl#L23) with matching `random_id`. + +## Liteserver queries + +All blockchain queries are wrapped in two layers: + +- ADNL query: `adnl.message.query query_id:int256 query:bytes = adnl.Message` (ID `7af98bb4`) +- Lite query: `liteServer.query data:bytes = Object` (ID `df068c79`) + +The specific liteserver method is serialized inside `data:bytes` of the lite query, which is itself serialized inside `query:bytes` of the ADNL query. + +### `getMasterchainInfo` + +The masterchain block is required as an input for many subsequent requests. + +TL schema: `liteServer.getMasterchainInfo = liteServer.MasterchainInfo` (ID `2ee6b589`). + +Packet layout: + +```text +74000000 -> packet size (116) +5fb13e11977cb5cff0fbf7f23f674d734cb7c4bf01322c5e6b928c5d8ea09cfd -> nonce + 7af98bb4 -> adnl.message.query + 77c1545b96fa136b8e01cc08338bec47e8a43215492dda6d4d7e286382bb00c4 -> query_id + 0c -> array size (12) + df068c79 -> liteServer.query + 04 -> array size (4) + 2ee6b589 -> getMasterchainInfo + 000000 -> padding (align to 8) + 000000 -> padding (align to 16) +ac2253594c86bd308ed631d57a63db4ab21279e9382e416128b58ee95897e164 -> sha256 +``` + +The response is wrapped in `adnl.message.answer` (ID `1684ac0f`) and contains [`liteServer.masterchainInfo`](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/lite_api.tl#L30), which includes `last:tonNode.blockIdExt`, `state_root_hash:int256`, and `init:tonNode.zeroStateIdExt`. + +Example response: + +```text +20010000 -> packet size (288) +5558b3227092e39782bd4ff9ef74bee875ab2b0661cf17efdfcd4da4e53e78e6 -> nonce + 1684ac0f -> adnl.message.answer + 77c1545b96fa136b8e01cc08338bec47e8a43215492dda6d4d7e286382bb00c4 -> query_id + b8 -> array size + 81288385 -> liteServer.masterchainInfo + ffffffff -> workchain (int) + 0000000000000080 -> shard (long) + 27405801 -> seqno (int) + e585a47bd5978f6a4fb2b56aa2082ec9deac33aaae19e78241b97522e1fb43d4 -> root_hash + 876851b60521311853f59c002d46b0bd80054af4bce340787a00bd04e0123517 -> file_hash + 8b4d3b38b06bb484015faf9821c3ba1c609a25b74f30e1e585b8c8e820ef0976 -> state_root_hash + ffffffff -> workchain (int) + 17a3a92992aabea785a7a090985a265cd31f323d849da51239737e321fb05569 -> root_hash + 5e994fcf4d425c0a6ce6a792594b7173205f740a39cd56f537defd28b48a0f6e -> file_hash + 000000 -> padding +520c46d1ea4daccdf27ae21750ff4982d59a30672b3ce8674195e8a23e270d21 -> sha256 +``` + +### `runSmcMethod` + +Call a smart contract get method using: + +```tl +liteServer.runSmcMethod mode:# id:tonNode.blockIdExt account:liteServer.accountId method_id:long params:bytes + = liteServer.RunMethodResult +``` + +Fields: + +- `mode` – `uint32` bitmask controlling which response fields are present. Set bit 2 to receive `result`. +- `id` – masterchain block from `getMasterchainInfo`. +- `account` – [`liteServer.accountId`](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/lite_api.tl#L27) with workchain and address. +- `method_id` – CRC16 (XMODEM table) of the method name, with bit 17 set. See [calculation example](https://github.com/xssnick/tonutils-go/blob/88f83bc3554ca78453dd1a42e9e9ea82554e3dd2/ton/runmethod.go#L16). +- `params` – [stack](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/crypto/block/block.tlb#L783) serialized in [BoC](/foundations/serialization/cells#bag-of-cells), containing arguments. + +With `mode = 4` (only `result`), the response includes: + +- `exit_code` – `0` on success, or an exception code otherwise. +- `result` – stack in BoC format with returned values. + +The stack is parsed according to the [`VmStackValue` TL-B schema](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/crypto/block/block.tlb#L766). Stack elements are stored in reverse order using `vm_stk_cons`, where the first reference points to the rest of the stack and the second contains the current value. + + + +### `getAccountState` + +Retrieve account data (balance, code, storage) using [`getAccountState`](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/tl/generate/scheme/lite_api.tl#L68): + +```tl +liteServer.accountState id:tonNode.blockIdExt shardblk:tonNode.blockIdExt shard_proof:bytes proof:bytes state:bytes + = liteServer.AccountState; +``` + +The `state` field contains a [BoC](/foundations/serialization/cells#bag-of-cells) with the account's [TL-B structure](https://github.com/ton-blockchain/ton/blob/ad736c6bc3c06ad54dc6e40d62acbaf5dae41584/crypto/block/block.tlb#L232): + +```tlb +account_none$0 = Account; +account$1 addr:MsgAddressInt storage_stat:StorageInfo storage:AccountStorage = Account; +``` + +Parse it by reading prefix bits to determine the variant (`account_none$0` vs `account$1`), then read fields sequentially: address, storage info, and the balance from `AccountStorage > CurrencyCollection > grams:Grams`, which is a `VarUInteger 16`. + +For a complete parsing walkthrough, see [TL-B documentation](/languages/tl-b/overview). + +## Key ID calculation + +The key ID is the SHA-256 hash of the serialized TL schema. For Ed25519 keys: + +```tl +pub.ed25519 key:int256 = PublicKey -- ID c6b41348 +``` + +The key ID is computed as `SHA-256([0xC6, 0xB4, 0x13, 0x48] || public_key)`, where the input consists of the 4-byte TL constructor ID and the 32-byte public key. + +Other key types: + +```tl +pub.aes key:int256 = PublicKey -- ID d4adbc2d +pub.overlay name:bytes = PublicKey -- ID cb45ba34 +pub.unenc data:bytes = PublicKey -- ID 0a451fb6 +pk.aes key:int256 = PrivateKey -- ID 3751e8a5 +``` + +[Implementation example](https://github.com/xssnick/tonutils-go/blob/2b5e5a0e6ceaf3f28309b0833cb45de81c580acc/liteclient/crypto.go#L16). + +## Handshake encryption + +The 160-byte session parameters are encrypted using an AES-CTR cipher derived from the SHA-256 hash of the 160 bytes and the [ECDH `secret`](#ecdh-shared-key): + +```text +key = secret[0..16] || hash[16..32] // 16 bytes each +nonce = hash[0..4] || secret[20..32] // 4 + 12 bytes +``` + +[Implementation example](https://github.com/xssnick/tonutils-go/blob/2b5e5a0e6ceaf3f28309b0833cb45de81c580acc/liteclient/connection.go#L361). + +## ECDH shared key + +The shared key is computed from one party's private key and the other party's public key using Elliptic-curve Diffie-Hellman. In practice, ADNL uses x25519 (ECDH over Curve25519). + +[Implementation example](https://github.com/xssnick/tonutils-go/blob/2b5e5a0e6ceaf3f28309b0833cb45de81c580acc/liteclient/crypto.go#L32). + +## See also + +- [ADNL specification](/foundations/network/adnl) +- [ADNL UDP](/foundations/network/adnl-udp) +- [TL-B documentation](/languages/tl-b/overview) +- [Cell and BoC serialization](/foundations/serialization/cells) diff --git a/foundations/network/adnl-udp.mdx b/foundations/network/adnl-udp.mdx new file mode 100644 index 000000000..b4711d48b --- /dev/null +++ b/foundations/network/adnl-udp.mdx @@ -0,0 +1,202 @@ +--- +title: "ADNL 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 [DHT](https://old-docs.ton.org/v3/documentation/network/protocols/dht/overview) and [RLDP](https://old-docs.ton.org/v3/documentation/network/protocols/rldp). + +Unlike [ADNL over TCP](/foundations/network/adnl-tcp), the UDP implementation uses channels for ongoing communication and establishes the connection simultaneously with the first data exchange. + +## Connection initialization + +To connect to a node, obtain its public key and address from the [global config](https://ton-blockchain.github.io/global.config.json). For example, from the `dht.nodes` section: + +```json +{ + "@type": "dht.node", + "id": { + "@type": "pub.ed25519", + "key": "fZnkoIAxrTd4xeBgVpZFRm5SvVvSx7eN3Vbe8c83YMk=" + }, + "addr_list": { + "addrs": [ + { + "@type": "adnl.address.udp", + "ip": 1091897261, + "port": 15813 + } + ] + } +} +``` + +Then: + +- Decode the Ed25519 public key from base64. +- Convert the IP value to dotted-decimal format; for example, `1091897261` → `65.21.7.173`. +- Combine the IP address with the port: `65.21.7.173:15813`. +- Establish a UDP connection to the resulting address. + +## Channel creation + +To establish a communication channel, send two messages in the initial packet: + +1. First message is to create a channel (`adnl.message.createChannel`): + + ```tl + adnl.message.createChannel key:int256 date:int = adnl.Message + ``` + + Generate a new Ed25519 keypair specifically for the channel. Use the public key as the `key` parameter and the current Unix timestamp as `date`. Store the private key for later use. + + Serialized example: + + ```text + bbc373e6 -- TL ID adnl.message.createChannel + d59d8e3991be20b54dde8b78b3af18b379a62fa30e64af361c75452f6af019d7 -- key + 555c8763 -- date + ``` + +1. Query (`adnl.message.query`): + + ```tl + adnl.message.query query_id:int256 query:bytes = adnl.Message + ``` + + The actual request (for example, `dht.getSignedAddressList`, which has TL ID `ed4879a9`) is serialized inside `query:bytes`. + + Serialized example: + + ```text + 7af98bb4 -- TL ID adnl.message.query + d7be82afbc80516ebca39784b8e2209886a69601251571444514b7f17fcd8875 -- query_id (random) + 04 ed4879a9 000000 -- query (bytes: length 4, payload, 3 bytes padding) + ``` + +## Packet structure + +All communication uses `adnl.packetContents` packets with the following TL structure: + +```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 +``` + +### Build the initial packet + +For the first packet (before the channel is established): + +1. Serialize `adnl.packetContents` with the messages, address list, seqno, and other fields. +1. Sign the serialized bytes with the client's Ed25519 private key. +1. Re-serialize the packet with the signature and set flag bit 11 to indicate its presence. +1. Compute SHA-256 of the serialized content. +1. Encrypt the content using the [ECDH shared key](/foundations/network/adnl-tcp#ecdh-shared-key) derived from the client's private key and the peer's public key. + +The final packet sent over UDP: + +| Field | Size | Description | +| -------------------- | -------- | -------------------------------------------------------------------------------------- | +| Peer key ID | 32 bytes | [Key ID](/foundations/network/adnl-tcp#key-id-calculation) of the server's Ed25519 key | +| Client public key | 32 bytes | Client's Ed25519 public key | +| SHA-256 content hash | 32 bytes | Hash of content before encryption | +| Encrypted content | variable | Encrypted `adnl.packetContents` | + +### Process the response + +The response has a similar structure: + +| Field | Size | Description | +| -------------------- | -------- | ------------------------------------------- | +| Client key ID | 32 bytes | Key ID of client's key (confirms recipient) | +| Server public key | 32 bytes | For shared key derivation | +| SHA-256 content hash | 32 bytes | Hash of content before encryption | +| Encrypted content | variable | Encrypted `adnl.packetContents` | + +Deserialization steps: + +1. Verify the key ID matches one of the client's keys. +1. Derive the shared key from the server's public key and the client's private key to decrypt the packet's content. +1. Verify the SHA-256 hash of the decrypted data; they must match. +1. Deserialize the content using the `adnl.packetContents` schema. + +The server responds with two messages: + +- `adnl.message.confirmChannel` confirms channel creation and provides the server's channel public key. +- `adnl.message.answer` is the response to the query. + +## Channel key derivation + +After receiving `adnl.message.confirmChannel`, compute the [ECDH shared key](/foundations/network/adnl-tcp#ecdh-shared-key) from the client's channel private key and the server's channel public key. + +Derive two directional keys from the shared key: + +```text +Key 1: shared_key (as-is) +Key 2: shared_key (reversed byte order) +``` + +To determine which key encrypts outgoing messages and which decrypts incoming messages, compare the [key IDs](/foundations/network/adnl-tcp#key-id-calculation) of both parties' public keys as `uint256`: + +| Condition | Outgoing encryption | Incoming decryption | +| ---------------------- | ------------------- | ------------------- | +| Server ID \< Client ID | Key 1 | Key 2 | +| Server ID > Client ID | Key 2 | Key 1 | + +[Implementation example](https://github.com/xssnick/tonutils-go/blob/46dbf5f820af066ab10c5639a508b4295e5aa0fb/adnl/adnl.go#L502). + +## Communication in a channel + +Channel packets use the same `adnl.packetContents` structure but include only `message`, `seqno`, and `confirm_seqno` (no signature, public keys, or address lists). + +The packet sent over UDP: + +| Field | Size | Description | +| -------------------------- | -------- | --------------------------------- | +| Outgoing encryption key ID | 32 bytes | `pub.aes` key ID | +| SHA-256 content hash | 32 bytes | Hash of content before encryption | +| Encrypted content | variable | Encrypted `adnl.packetContents` | + +## Other message types + +### `adnl.message.part` + +Use to send messages that are too large for a single UDP datagram: + +```tl +adnl.message.part + hash:int256 -- SHA-256 of the original complete message + total_size:int -- original message size + offset:int -- offset from the beginning + data:bytes -- piece of the original message + = adnl.Message; +``` + +Collect all parts, concatenate by offset, and process the result as a regular message. + +### `adnl.message.custom` + +```tl +adnl.message.custom data:bytes = adnl.Message; +``` + +Use when higher-level protocol logic does not fit the request-response pattern. Processing is entirely delegated to the higher level. For example, [RLDP](https://old-docs.ton.org/v3/documentation/network/protocols/rldp) uses custom messages because a single response may correspond to multiple requests. + +## See also + +- [Original article by Oleg Baranov](https://github.com/xssnick/ton-deep-doc/blob/master/ADNL-UDP-Internal.md) diff --git a/foundations/network/adnl.mdx b/foundations/network/adnl.mdx new file mode 100644 index 000000000..0e331bacd --- /dev/null +++ b/foundations/network/adnl.mdx @@ -0,0 +1,58 @@ +--- +title: "Abstract Datagram Network Layer" +sidebarTitle: "ADNL" +--- + +import { Aside } from "/snippets/aside.jsx"; + +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 [RLDP](https://old-docs.ton.org/v3/documentation/network/protocols/rldp) and [DHT](https://old-docs.ton.org/v3/documentation/network/protocols/dht/overview) 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 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 `type_id` is a little-endian `uint32` constructor ID that specifies the type of the TL-serialized object. The corresponding private key must be known to receive and decrypt messages sent to a given address. For example, the `type_id` `0x4813b4c6` corresponds to the Ed25519 public key type. + + + +### 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. + +## Client-server protocol (ADNL over TCP) + +ADNL over TCP is used by clients to communicate with liteservers. The client performs an x25519 key agreement with the server, generates AES-CTR session parameters, and sends them inside a handshake packet. After the exchange, both sides use two permanent AES-CTR ciphers to encrypt datagrams with SHA-256 integrity checks. + +See [ADNL TCP](/foundations/network/adnl-tcp) for the full handshake flow, packet structure, liteserver query examples, and security considerations. + +## 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 via ECDH from the peers' keys. + +See [ADNL UDP](/foundations/network/adnl-udp) for channel establishment, packet structure, and message types. + +## See also + +- [ADNL TCP - liteserver communication](/foundations/network/adnl-tcp) +- [ADNL UDP - internode communication](/foundations/network/adnl-udp) +- [ADNL implementation in TON](https://github.com/ton-blockchain/ton/tree/master/adnl) +- [The Open Network Whitepaper, p. 80](https://ton.org/whitepaper.pdf)