From 7bb61ff0fbf26929397bfcf208cac2c8c76cb68e Mon Sep 17 00:00:00 2001 From: Arunima Chaudhuri Date: Mon, 22 Dec 2025 11:56:00 +0530 Subject: [PATCH] improve rpc msgs --- vac/raw/logos-capability-discovery.md | 374 ++++++++++++++++++++++---- 1 file changed, 322 insertions(+), 52 deletions(-) diff --git a/vac/raw/logos-capability-discovery.md b/vac/raw/logos-capability-discovery.md index 47fe0a6..8068f57 100644 --- a/vac/raw/logos-capability-discovery.md +++ b/vac/raw/logos-capability-discovery.md @@ -380,97 +380,367 @@ but full implementations SHOULD include address information. ## RPC Messages -All RPC messages MUST be sent using the libp2p Kad-dht message format -with new message types added for Logos discovery operations. +All RPC messages MUST be sent using the libp2p Kad-DHT message format +with extensions for Logos discovery operations. +Messages follow the same transport rules as base Kad-DHT: +prefixed with message length as unsigned varint, sent over libp2p streams. -### Message Types +### Base Message Structure -The following message types MUST be added to the Kad-dht `Message.MessageType` enum: +All Logos discovery messages extend the standard Kad-DHT protobuf message: ```protobuf -enum MessageType { - // ... existing Kad-dht message types ... - REGISTER = 6; - GET_ADS = 7; +message Message { + enum MessageType { + PUT_VALUE = 0; + GET_VALUE = 1; + ADD_PROVIDER = 2; + GET_PROVIDERS = 3; + FIND_NODE = 4; + PING = 5; + REGISTER = 6; // NEW: Logos discovery + GET_ADS = 7; // NEW: Logos discovery + } + + enum ConnectionType { + NOT_CONNECTED = 0; + CONNECTED = 1; + CAN_CONNECT = 2; + CANNOT_CONNECT = 3; + } + + message Peer { + bytes id = 1; + repeated bytes addrs = 2; + ConnectionType connection = 3; + } + + // Core fields + MessageType type = 1; + int32 clusterLevelRaw = 10; // NOT USED + bytes key = 2; + Record record = 3; + repeated Peer closerPeers = 8; + repeated Peer providerPeers = 9; + + // Logos discovery extensions + enum RegistrationStatus { + CONFIRMED = 0; + WAIT = 1; + REJECTED = 2; + } + + RegistrationStatus status = 11; + Ticket ticket = 12; + repeated Advertisement ads = 13; } ``` ### REGISTER Message +The REGISTER message is used by advertisers to register their advertisements with registrars. + #### REGISTER Request -Advertisers SHOULD send `REGISTER` request message to registrars -to admit the advertiser's advertisemnet for a service -into the registrar's `ad_cache`. +**Field Usage:** + +| Field | Usage | Value | +|-------|-------|-------| +| `type` | REQUIRED | `REGISTER` (6) | +| `key` | REQUIRED | `service_id_hash` (32-byte SHA-256 hash) | +| `record` | REQUIRED | Advertisement encoded as Record | +| `ticket` | OPTIONAL | Ticket from previous attempt (if retry) | +| `closerPeers` | UNUSED | Empty/not set | +| `providerPeers` | UNUSED | Empty/not set | +| `status` | UNUSED | Empty/not set | +| `ads` | UNUSED | Empty/not set | +| `clusterLevelRaw` | UNUSED | Not used (may be omitted or set to 0) | + +**Record Field Encoding:** + +The `record` field MUST encode the Advertisement as follows: +- `record.key` = `service_id_hash` (MUST match message `key` field) +- `record.value` = Serialized Advertisement protobuf containing: + - `service_id_hash` (bytes) + - `peerID` (bytes) + - `addrs` (repeated bytes) + - `signature` (bytes, Ed25519 over service_id_hash || peerID || addrs) + - `metadata` (optional bytes) + - `timestamp` (optional uint64) +- `record.timeReceived` = Empty/not set (populated by registrar on storage) + +**Ticket Field (if present):** + +When retrying registration, the `ticket` field MUST contain: +- `ad` (Advertisement) = Copy of original advertisement +- `t_init` (uint64) = Initial ticket creation timestamp (Unix seconds) +- `t_mod` (uint64) = Last modification timestamp (Unix seconds) +- `t_wait_for` (uint32) = Remaining wait time in seconds +- `signature` (bytes) = Ed25519 signature over (ad || t_init || t_mod || t_wait_for) + +**Example Request Structure:** ```protobuf -message Message { - MessageType type = 1; // REGISTER - bytes key = 2; // service_id_hash - Advertisement ad = 3; // The advertisement to register - optional Ticket ticket = 4; // Optional: ticket from previous attempt +Message { + type: REGISTER + key: + record: { + key: + value: + timeReceived: "" + } + ticket: { // Optional, only if retry + ad: + t_init: 1234567890 + t_mod: 1234567900 + t_wait_for: 300 + signature: + } } ``` -Advertisers SHOULD include the `service_id_hash` in the `key` field -and the advertisement in the `ad` field of the request. -If this is a retry attempt, advertisers SHOULD include -the latest `ticket` received from the registrar. - #### REGISTER Response -`REGISTER` response SHOULD be sent by registrars to advertisers. +**Field Usage:** + +| Field | Usage | Value | +|-------|-------|-------| +| `type` | REQUIRED | `REGISTER` (6) | +| `status` | REQUIRED | `CONFIRMED`, `WAIT`, or `REJECTED` | +| `closerPeers` | REQUIRED | List of Peer objects for advertise table | +| `ticket` | CONDITIONAL | MUST be present if status = WAIT | +| `key` | UNUSED | Empty/not set | +| `record` | UNUSED | Empty/not set | +| `providerPeers` | UNUSED | Empty/not set | +| `ads` | UNUSED | Empty/not set | +| `clusterLevelRaw` | UNUSED | Not used | + +**Status Field Values:** +- `CONFIRMED` (0): Advertisement accepted and stored in `ad_cache` +- `WAIT` (1): Advertisement not yet accepted, ticket provided with waiting time +- `REJECTED` (2): Advertisement rejected (signature invalid, duplicate, or other error) + +**Ticket Field (when status = WAIT):** + +MUST contain: +- `ad` (Advertisement) = Copy of the advertisement from request +- `t_init` (uint64) = Ticket creation timestamp (set on first attempt, preserved on retries) +- `t_mod` (uint64) = Current timestamp (updated on each response) +- `t_wait_for` (uint32) = MIN(E, t_remaining) where t_remaining is calculated remaining wait time +- `signature` (bytes) = Ed25519 signature by registrar over (ad || t_init || t_mod || t_wait_for) + +**CloserPeers Field:** + +MUST contain a list of Peer objects to help populate the advertiser's `AdvT(service_id_hash)` table. Each Peer object SHOULD include: +- `id` (bytes) = Peer ID +- `addrs` (repeated bytes) = Multiaddrs of the peer +- `connection` (ConnectionType) = Optional connection status + +The registrar SHOULD return one peer from each bucket of its `RegT(service_id_hash)` table using the `GETPEERS()` algorithm. + +**Example Response Structure (WAIT):** ```protobuf -enum RegistrationStatus { - CONFIRMED = 0; // Advertisement accepted - WAIT = 1; // wait, ticket provided - REJECTED = 2; // Advertisement rejected -} - -message Message { - MessageType type = 1; // REGISTER - RegistrationStatus status = 2; - optional Ticket ticket = 3; // Provided if status = WAIT - repeated Peer closerPeers = 4; // Peers for populating advertise table +Message { + type: REGISTER + status: WAIT + ticket: { + ad: + t_init: 1234567890 + t_mod: 1234567905 + t_wait_for: 295 + signature: + } + closerPeers: [ + {id: , addrs: [], connection: CONNECTED}, + {id: , addrs: [], connection: CAN_CONNECT}, + ... + ] } ``` -Registrars SHOULD set the `status` field to indicate the result of the registration attempt. -If `status` is `WAIT`, registrars MUST provide a valid `ticket`. -Registrars SHOULD include `closerPeers` to help populate the advertiser's table. +**Example Response Structure (CONFIRMED):** + +```protobuf +Message { + type: REGISTER + status: CONFIRMED + closerPeers: [ + {id: , addrs: []}, + {id: , addrs: []}, + ... + ] +} +``` ### GET_ADS Message +The GET_ADS message is used by discoverers to retrieve advertisements for a specific service from registrars. + #### GET_ADS Request -Discoverers send `GET_ADS` request message to registrars -to get advertisements for a particular service. +**Field Usage:** + +| Field | Usage | Value | +|-------|-------|-------| +| `type` | REQUIRED | `GET_ADS` (7) | +| `key` | REQUIRED | `service_id_hash` to look up | +| `record` | UNUSED | Empty/not set | +| `closerPeers` | UNUSED | Empty/not set | +| `providerPeers` | UNUSED | Empty/not set | +| `status` | UNUSED | Empty/not set | +| `ticket` | UNUSED | Empty/not set | +| `ads` | UNUSED | Empty/not set | +| `clusterLevelRaw` | UNUSED | Not used | + +**Example Request Structure:** ```protobuf -message Message { - MessageType type = 1; // GET_ADS - bytes key = 2; // service_id_hash to look up +Message { + type: GET_ADS + key: } ``` -Discoverers SHOULD include the `service_id_hash` they are searching for in the `key` field. - #### GET_ADS Response -Registrars SHOULD respond to discoverer's `GET_ADS` request -using the following response structure. +**Field Usage:** + +| Field | Usage | Value | +|-------|-------|-------| +| `type` | REQUIRED | `GET_ADS` (7) | +| `ads` | REQUIRED | List of Advertisement objects (up to `F_return` = 10) | +| `closerPeers` | REQUIRED | List of Peer objects for search table | +| `key` | UNUSED | Empty/not set | +| `record` | UNUSED | Empty/not set | +| `providerPeers` | UNUSED | Empty/not set | +| `status` | UNUSED | Empty/not set | +| `ticket` | UNUSED | Empty/not set | +| `clusterLevelRaw` | UNUSED | Not used | + +**Ads Field:** + +MUST contain up to `F_return` (default: 10) Advertisement objects retrieved from the registrar's `ad_cache`. Each Advertisement MUST include: +- `service_id_hash` (bytes) = Hash of the service protocol ID +- `peerID` (bytes) = Peer ID of the advertiser +- `addrs` (repeated bytes) = Multiaddrs of the advertiser +- `signature` (bytes) = Ed25519 signature over (service_id_hash || peerID || addrs) +- `metadata` (optional bytes) = Service-specific metadata +- `timestamp` (optional uint64) = Unix timestamp when ad was created + +Discoverers MUST verify the `signature` field of each advertisement before accepting it. + +**CloserPeers Field:** + +MUST contain a list of Peer objects to help populate the discoverer's `DiscT(service_id_hash)` table. Each Peer object SHOULD include: +- `id` (bytes) = Peer ID +- `addrs` (repeated bytes) = Multiaddrs of the peer +- `connection` (ConnectionType) = Optional connection status + +The registrar SHOULD return one peer from each bucket of its `RegT(service_id_hash)` table using the `GETPEERS()` algorithm. + +**Example Response Structure:** ```protobuf -message Message { - MessageType type = 1; // GET_ADS - repeated Advertisement ads = 2; // Up to F_return advertisements - repeated Peer closerPeers = 3; // Peers for populating search table +Message { + type: GET_ADS + ads: [ + { + service_id_hash: , + peerID: , + addrs: [, ], + signature: , + timestamp: 1234567890 + }, + { + service_id_hash: , + peerID: , + addrs: [], + signature: , + metadata: + }, + ... // up to F_return total + ] + closerPeers: [ + {id: , addrs: []}, + {id: , addrs: []}, + ... + ] } ``` -Registrars MUST return up to `F_return` advertisements for the requested service. -Registrars SHOULD include `closerPeers` to help populate the discoverer's search table. +### Message Validation Requirements + +#### REGISTER Request Validation + +Registrars MUST validate incoming REGISTER requests: + +1. **Type field**: MUST be `REGISTER` (6) +2. **Key field**: MUST be 32 bytes (valid SHA-256 hash) +3. **Record field**: MUST be present and properly formatted: + - `record.key` MUST equal message `key` field + - `record.value` MUST contain valid serialized Advertisement +4. **Advertisement validation**: + - `service_id_hash` MUST be 32 bytes + - `peerID` MUST be valid libp2p peer ID + - `addrs` MUST contain at least one valid multiaddr + - `signature` MUST be valid Ed25519 signature over (service_id_hash || peerID || addrs) +5. **Ticket validation** (if present): + - `ticket.signature` MUST be valid and issued by this registrar + - `ticket.ad` MUST match current request's advertisement + - Current time MUST be within registration window: `ticket.t_mod + ticket.t_wait_for ≤ NOW() ≤ ticket.t_mod + ticket.t_wait_for + δ` +6. **Duplicate check**: Advertisement MUST NOT already exist in `ad_cache` + +If any validation fails, registrar MUST respond with `status = REJECTED`. + +#### GET_ADS Request Validation + +Registrars MUST validate incoming GET_ADS requests: + +1. **Type field**: MUST be `GET_ADS` (7) +2. **Key field**: MUST be 32 bytes (valid SHA-256 hash) + +If validation fails, registrar MAY return empty response or close stream. + +#### Advertisement Signature Verification + +Discoverers MUST verify each advertisement signature before accepting: + +```text +VERIFY_SIGNATURE(ad): + message = ad.service_id_hash || ad.peerID || ad.addrs + public_key = DERIVE_PUBLIC_KEY(ad.peerID) + assert(Ed25519_VERIFY(public_key, message, ad.signature)) +``` + +If signature verification fails, the advertisement MUST be discarded. + +### Stream Management + +Following base Kad-DHT behavior: + +- Implementations MAY reuse streams for multiple sequential requests +- Implementations MUST handle multiple requests on a single incoming stream +- On any error, the stream SHOULD be reset +- Each message MUST be prefixed with its length as unsigned varint per [multiformats unsigned-varint spec](https://github.com/multiformats/unsigned-varint) + +### Error Handling + +Implementations SHOULD handle the following error cases: + +- **Invalid message format**: Close stream, optionally log error +- **Signature verification failure**: For REGISTER, respond with `status = REJECTED`. For GET_ADS advertisements, discard invalid ads but continue processing valid ones +- **Timeout**: Close stream after implementation-defined timeout period +- **Cache full**: For REGISTER, issue ticket with appropriate waiting time +- **Unknown service_id_hash**: For GET_ADS, return empty `ads` list but include `closerPeers` + +### Backwards Compatibility + +Logos discovery extends Kad-DHT without breaking existing functionality: + +- Existing Kad-DHT message types (FIND_NODE, GET_VALUE, etc.) continue to work unchanged +- Nodes not supporting Logos discovery can ignore REGISTER and GET_ADS messages +- The protocol identifier distinguishes Logos-capable nodes from standard Kad-DHT nodes ## Sequence Diagram @@ -1148,7 +1418,7 @@ Incentivization mechanisms are beyond the scope of this RFC. [1] [DISC-NG: Robust Service Discovery in the Ethereum Global Network](https://ieeexplore.ieee.org/document/10629017) -[2] [libp2p Kademlia DHT specification](https://github.com/libp2p/specs/blob/master/kad-dht/README.md) +[2] [libp2p Kademlia DHT specification](https://github.com/libp2p/specs/blob/e87cb1c32a666c2229d3b9bb8f9ce1d9cfdaa8a9/kad-dht/README.md) [3] [Go implementation](https://github.com/libp2p/go-libp2p-kad-dht)