Reliable Large Datagram Protocol (RLDP)
With the introduction of the sub-second upgrade, blockchain nodes now use QUIC instead of RLDP for internal node communication.
The Reliable Large Datagram Protocol (RLDP) runs on top of ADNL UDP and transfers data that does not fit in one ADNL datagram. It uses Forward Error Correction (FEC) to recover from packet loss. Its newer version, RLDP2, also uses acknowledgment packets, loss detection, pacing, and congestion control.
RLDP has been used throughout TON infrastructure for downloading blocks from other nodes, transferring data, and accessing TON Sites and TON Storage.
The rldp-http-proxy in the TON monorepo uses RLDP2 for accessing TON Sites.
Protocol
The TL schema retains structures for RLDP and RLDP2:
fec.raptorQ data_size:int symbol_size:int symbols_count:int = fec.Type;
fec.roundRobin data_size:int symbol_size:int symbols_count:int = fec.Type;
fec.online data_size:int symbol_size:int symbols_count:int = fec.Type;
rldp2.messagePart transfer_id:int256 fec_type:fec.Type part:int total_size:long seqno:int data:bytes = rldp2.MessagePart;
rldp2.confirm transfer_id:int256 part:int max_seqno:int received_mask:int received_count:int = rldp2.MessagePart;
rldp2.complete transfer_id:int256 part:int = rldp2.MessagePart;
rldp.messagePart transfer_id:int256 fec_type:fec.Type part:int total_size:long seqno:int data:bytes = rldp.MessagePart;
rldp.confirm transfer_id:int256 part:int seqno:int = rldp.MessagePart;
rldp.complete transfer_id:int256 part:int = rldp.MessagePart;
rldp.message id:int256 data:bytes = rldp.Message;
rldp.query query_id:int256 max_answer_size:long timeout:int data:bytes = rldp.Message;
rldp.answer query_id:int256 data:bytes = rldp.Message;The message-part, confirmation, and completion structures are serialized as custom ADNL messages and transmitted over ADNL UDP. RLDP2 reuses rldp.message, rldp.query, and rldp.answer for the decoded payload.
rldp.messagecarries a one-way application payload.rldp.querycarries a request, aquery_id, a maximum answer size, and a UNIX timeout.rldp.answercarries the application response and the request'squery_id. The querying endpoint rejects response data larger thanmax_answer_size.
The RLDP2 connection defaults to a 7,680-byte inbound limit. The send_query_ex registers max_answer_size as the limit for the answer transfer.
Transfer mechanism
- The sender generates a random
transfer_id. - The sender splits the payload into parts and creates a RaptorQ encoder for each part. The reference RLDP2 implementation uses parts of up to 2,000,000 bytes and 768-byte symbols.
- The sender transmits encoded symbols in
rldp2.messagePart. - The receiver reports the greatest received sequence number, a receive mask, and a total receive count in
rldp2.confirm. - The sender uses this feedback to estimate round-trip time, bandwidth, and loss. It paces symbols and adjusts its congestion window with a BBR-style controller.
- The sender stops transmitting a part after
rldp2.completearrives or the transfer times out.
The receiver decodes each part after collecting enough independent symbols, concatenates the parts in order, and deserializes the result as rldp.message, rldp.query, or rldp.answer. An answer uses the request's transfer_id with every bit inverted.
Forward Error Correction
The TL schema defines RoundRobin, Online, and RaptorQ FEC types. The reference RLDP2 implementation accepts only RaptorQ — its FEC parser rejects the other constructors.
RaptorQ
RaptorQ divides data into fixed-size source symbols. An encoder can derive additional repair symbols from the same source data.
After receiving enough independent source and repair symbols, the decoder reconstructs the original part without requesting specific lost symbols.
RLDP2 sends an initial set of source and repair symbols, then uses acknowledgments and loss estimates to decide how many additional symbols to send. The receiver sends rldp2.complete after decoding a part.
RLDP-HTTP
TON Sites use HTTP over RLDP. The host runs a standard HTTP web server with rldp-http-proxy alongside it. Incoming requests from the TON network arrive through RLDP2, and the proxy forwards them to the local HTTP server. On the client side, a local proxy, such as Tonutils Proxy, converts outgoing HTTP requests into RLDP.
HTTP over RLDP uses these TL structures:
http.header name:string value:string = http.Header;
http.payloadPart data:bytes trailer:(vector http.header) last:Bool = http.PayloadPart;
http.response http_version:string status_code:int reason:string headers:(vector http.header) no_payload:Bool = http.Response;
http.proxy.capabilities capabilities:long = http.proxy.Capabilities;
http.request id:int256 method:string url:string http_version:string headers:(vector http.header) = http.Response;
http.getNextPayloadPart id:int256 seqno:int max_chunk_size:int = http.PayloadPart;
http.proxy.getCapabilities capabilities:long = http.proxy.Capabilities;The request-response flow:
- The client sends
http.requestviarldp.query. - If the request has a payload, the server sends
http.getNextPayloadPartqueries to the client.- The client responds with
http.payloadPartchunks and incrementsseqnountillastis true.
- The client responds with
- The server sends
http.response. - If
no_payloadis false, the client sendshttp.getNextPayloadPartqueries to the server using the same chunked mechanism.
The proxy uses bit 0 (value 1) in the http.proxy.getCapabilities and http.proxy.capabilities exchange to advertise RLDP2 support. It requests payload chunks of up to 2,095,104 bytes and caps larger max_chunk_size values at that size.
TON Site request example
This example demonstrates requesting foundation.ton, assuming the ADNL address has been resolved via DNS and a connection has been established over ADNL UDP. The proxy requires the peer to advertise RLDP2 support.
GET request
The client fills the following structure:
http.request id:int256 method:string url:string http_version:string headers:(vector http.header) = http.Response;The client serializes http.request:
e191b161 -- TL ID http.request
116505dac8a9a3cdb464f9b5dd9af78594f23f1c295099a9b50c8245de471194 -- id (random)
03 474554 -- method = "GET"
16 687474703a2f2f666f756e646174696f6e2e746f6e2f 00 -- url = "http://foundation.ton/"
08 485454502f312e31 000000 -- http_version = "HTTP/1.1"
01000000 -- headers (1 entry)
04 486f7374 000000 -- name = "Host"
0e 666f756e646174696f6e2e746f6e 00 -- value = "foundation.ton"The client then wraps the serialized http.request in rldp.query and serializes it. The following partial snippet shows the field layout. The placeholders represent values that the client supplies before serialization:
694d798a -- TL ID rldp.query
<QUERY_ID> -- 32 random bytes
0040000000000000 -- max_answer_size = 16 KiB (16,384 bytes)
<TIMEOUT> -- future UNIX timestamp encoded as a TL int
<SERIALIZED_HTTP_REQUEST> -- TL bytes valueRaptorQ encoding
- The sender uses the 768-byte symbol size used by the reference RLDP2 implementation.
- For this one-part request, the sender sets
data_sizeandtotal_sizeto the serializedrldp.querylength. The RaptorQ encoder handles the final short symbol. - The sender creates a RaptorQ encoder.
Packet transmission
The sender transmits encoded symbols in rldp2.messagePart:
rldp2.messagePart transfer_id:int256 fec_type:fec.Type part:int total_size:long seqno:int data:bytes = rldp2.MessagePart;transfer_id– randomint256, same for all parts of this transfer.fec_type–fec.raptorQwith the part's byte length,symbol_size = 768, and the encoder's source-symbol count.part– zero-based part index. Start with 0.total_size– complete serializedrldp.querysize across all parts.seqno– starts at 0, incremented for each packet.data– encoded symbol (768 bytes).
The sender wraps each structure in adnl.message.custom and transmits it over ADNL UDP. It processes rldp2.confirm feedback and continues according to its loss-recovery and pacing logic until rldp2.complete arrives.
Response processing
The response arrives as rldp2.messagePart messages inside adnl.message.custom. Its transfer_id is the request transfer ID with all 256 bits inverted.
- The receiver extracts FEC parameters (
data_size,symbol_size,symbols_count) from the first received message. - The receiver initializes a RaptorQ decoder.
- The receiver feeds each received symbol and its
seqnointo the decoder. - The receiver sends
rldp2.confirmas symbols arrive. - The receiver attempts decoding when the decoder reports that enough symbols are available. On success, it sends
rldp2.completefor that part.
At this point, the decoded rldp.answer contains an http.response with the status code and headers, but not necessarily the body.
-
If
no_payloadistrue, the response has no body and the request is complete. -
If
no_payloadisfalse, the client fetches the body separately:- The client sends an
http.getNextPayloadPartrequest with incrementingseqno. The proxy requests at most 2,095,104 bytes per chunk. - The client wraps each request in a new
rldp.querytransfer. - The client repeats the request until the returned
http.payloadParthaslastset totrue.
- The client sends an