Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TIP-548: Node Discovery via DNS #548

Closed
317787106 opened this issue May 22, 2023 · 11 comments
Closed

TIP-548: Node Discovery via DNS #548

317787106 opened this issue May 22, 2023 · 11 comments

Comments

@317787106
Copy link
Contributor

317787106 commented May 22, 2023

TIP: 548
Title: Node Discovery via DNS
Author: 317787106@qq.com
Status: Final
Type: Standards Track
Category: Networking
Created: 2023-05-22

Simple Summary

This document describes a scheme for authenticated, dynamically updated libp2p node lists retrievable via DNS.

Abstract

We build two merkle trees using known links and nodes from DHT, and publish it on dns server, so everyone can discover nodes free while no need to run DHT.

Motivation

Many java-tron clients contain hard-coded active node lists. Updating those lists requires a process restart. The current lists are small, giving the client little choice of initial entry point into the java-tron p2p network. It is better to maintain larger node lists containing hundreds of nodes and update them regularly to meet the requirements of the network.

The scheme described here is a replacement for client active node lists with equivalent security and many additional benefits. Large lists populated by traversing the node discovery DHT can serve as a fallback option for nodes that can’t join the DHT due to restrictive network policy. DNS-based node lists may also be useful to java-tron peering providers because their users can configure the client to use the provider’s list.

Specification

A ‘node list’ is a list of Endpoint of arbitrary length as this:

message Endpoint {
  bytes address = 1;
  int32 port = 2;
  bytes nodeId = 3;
  bytes addressIpv6 = 4;
}

Lists may refer to other lists using links. The entire list is signed using a secp256k1 private key. The corresponding public key must be known to the client in order to verify the list.

To refer to a DNS node list, clients use a URL with ‘tree://’ scheme. The URL contains the DNS name on which the list can be found as well as the public key that signed the list. The public key is contained in the username part of the URL and is the base32 encoding (RFC-4648) of the compressed 32-byte binary public key.

Example: Suppose to generate a random private key 0xb71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291, then we get a tree:

tree://APFGGTFOBVE2ZNAB3CSMNNX6RRK3ODIRLP2AA5U4YFAA6MSYZUYTQ@nodes.example.org

This URL refers to a node list at the DNS name ‘nodes.example.org’ and is signed by the public key 0xca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be00812904767bf5ccd1fc7f.

DNS Record Structure

Let’s serialize a protobuf object and encode it with Base64 to generate a tree root. protobuf is that:

message DnsRoot {
  message TreeRoot {
    bytes eRoot = 1;
    bytes lRoot = 2;
    int32 seq = 3;
  }
  TreeRoot treeRoot = 1;
  bytes signature = 2;
}

The nodes in a list are encoded as a merkle tree for distribution via the DNS protocol. Entries of the merkle tree are contained in DNS TXT records. The root of the tree is a TXT record with the following content:

tree-root-v1:CjgKGkpYUjRWM0M3VDZQTkNWR1k1SkhQVE5YN0RJEhpHNzYzTTUzTU9QWVdVVkpTVzZDR0UyN0dFNBJXbWJkTGtHRk8wbWRRRmdCYlVFVEx1VGxsbUEtNnpEYXZqUWpUMTJXU0phVmZmMUxrMlFkVDBBOGE2Umw0WFpNMHZDRzFzeVUzMm1LR3VDeTY1Nzl0OXhz

where

  • eRoot and lRoot refer to the root hashes of subtrees containing nodes and links subtrees
  • seq is the tree’s updated sequence number, a decimal integer
  • signature is a 65-byte secp256k1 EC signature over the keccak256 hash of the treeRoot content, encoded as URL-safe base64 (RFC-4648)

Further TXT records on subdomains map hashes to one of three entry types. The subdomain name of any entry is the base32 encoding of the (abbreviated) keccak256 hash of its text content.

  • tree-branch:....., is an intermediate tree entry containing hashes of subtree entries.
  • tree://@ is a leaf pointing to a different list located at another fully qualified domain name. Note that this format matches the URL encoding. This type of entry may only appear in the subtree pointed to by lRoot.
  • nodes: is a leaf containing one or more node records. The node record is encoded as a URL-safe base64 string. Note that this type of entry matches EndPoint or repeated EndPoints. It may only appear in the eRoot subtree.

No particular ordering or structure is defined for the tree. Whenever the tree is updated, its sequence number should increase. The content of any TXT record should be small enough to fit into the 512-byte limit imposed on UDP DNS packets. This limits the number of hashes that can be placed into an entree-branch entry.

Suppose we have a serialized treeRoot as the following, response to the above tree-root-v1:

[treeRoot {
  eRoot: "JXR4V3C7T6PNCVGY5JHPTNX7DI"
  lRoot: "G763M53MOPYWUVJSW6CGE27GE4"
  seq: 0
 }
 signature: "mbdLkGFO0mdQFgBbUETLuTllmA-6zDavjQjT12WSJaVff1Lk2QdT0A8a6Rl4XZM0vCG1syU32mKGuCy6579t9xs"
]

Encode it using Base64 we get "CjgKGkpYUjRWM0M3VDZQTkNWR1k1SkhQVE5YN0RJEhpHNzYzTTUzTU9QWVdVVkpTVzZDR0UyN0dFNBJXbWJkTGtHRk8wbWRRRmdCYlVFVEx1VGxsbUEtNnpEYXZqUWpUMTJXU0phVmZmMUxrMlFkVDBBOGE2Umw0WFpNMHZDRzFzeVUzMm1LR3VDeTY1Nzl0OXhz", we can also decode it using Base64 to get above DnsRoot in turn.

Example in zone file format:

; name                        ttl     class type  content
nodes.example.org             60      IN    TXT   tree-root-v1:CjgKGkpYUjRWM0M3VDZQTkNWR1k1SkhQVE5YN0RJEhpHNzYzTTUzTU9QWVdVVkpTVzZDR0UyN0dFNBJXbWJkTGtHRk8wbWRRRmdCYlVFVEx1VGxsbUEtNnpEYXZqUWpUMTJXU0phVmZmMUxrMlFkVDBBOGE2Umw0WFpNMHZDRzFzeVUzMm1LR3VDeTY1Nzl0OXhz
G763M53MOPYWUVJSW6CGE27GE4    86900   IN    TXT   tree-branch:
LAHEXJDXOPZSS2TDVXTJACCB6Q    86900   IN    TXT   tree-branch:OX22LN2ZUGOPGIPGBUQH35KZU4,XTGCXXQHPK3VUZPQHC6CGJDR3Q,BQLJLB6P5CRXHI37BRVWBWWACY,X4FURUK4SHXW3GVE6XBO3DFD5Y,SIUYMSVBYYXCE6HVW5TSGOFKVQ,2RKY3FUYIQBV4TFIDU7S42EIEU,KSEEGRTUGR4GCCBQ4TYHAWDKME,YGWDS6F6KLTFCC7T3AMAJHXI2A,K4HMVDEHRKOGOFQZXBJ2PSVIMM,NLLRMPWOTS6SP4D7YLCQA42IQQ,BBDLEDOZYAX5CWM6GNAALRVUXY,7NMT4ZISY5F4U6B6CQML2C526E,NVDRYMFHIERJEVGW5TE7QEAS2A
QR4HMFZU3STBJEXOZIXPDRQTGM    86900   IN    TXT   tree-branch:5ELKMY4HVAV5CBY6KDMXWOFSN4,7PHYT72EXSZJ6MT2IQ7VGUFQHI,AM6BJFCERRNKBG4A5X3MORBDZU,2WOYKPVTNYAY3KVDTDY4CEVOJM,PW5BHSJMPEHVJKRF5QTRXQB4LU,IS4YMOJGD4XPODBAMHZOUTIVMI,NSEE5WE57FWG2EERXI5TBBD32E,GOLZDJTTQ7V2MO2BG45O3Q22XI,4VL7USGBWKW576WM4TX7XIXS4A,GZQSPHDZYS7FXURGOQU3RIDUK4,T7L645CJJKCQVQMUADDO44EGOM,ATPMZZZB4RGYKC6K7QDFC22WIE,57KNNYA4WOKVZAODRCFYK64MBA
WHCXLEQB3467BFATRY5SMIV62M    86900   IN    TXT   tree-branch:BJF5S37KVATG2SYHO6M7APDCNU,OUB3BDKUZQWXXFX5OSF5JCB6BA,6JZEHDWM6WWQYIEYVZN5QVMUXA,LXNNOBVTTZBPD3N5VTOCPVG7JE,LMWLKDCBT2U3CGSHKR2PYJNV5I,K2SSCP4ZIF7TQI4MRVLELFAQQE,MKR7II3GYETKN7MSCUQOF6MBQ4,FBJ5VFCV37SGUOEYA2SPGO3TLA,6SHSDL7PJCJAER3OS53NYPNDFI,KYU2OQJBU6AU3KJFCUSLOJWKVE,3N6XKDWY3WTBOSBS22YPUAHCFQ,IPEWOISXUGOL7ORZIOXBD24SPI,PCGDGGVEQQQFL4U2FYRXVHVMUM
JXR4V3C7T6PNCVGY5JHPTNX7DI    86900   IN    TXT   tree-branch:WHCXLEQB3467BFATRY5SMIV62M,LAHEXJDXOPZSS2TDVXTJACCB6Q,QR4HMFZU3STBJEXOZIXPDRQTGM,JZUKVXBOLBPXCELWIE5G6E6UUU
JZUKVXBOLBPXCELWIE5G6E6UUU    86900   IN    TXT   nodes:ChEKDDE5Mi4xNjguMC40MBCQTg
K4HMVDEHRKOGOFQZXBJ2PSVIMM    86900   IN    TXT   nodes:ChEKDDE5Mi4xNjguMC4yMhCQTg
BBDLEDOZYAX5CWM6GNAALRVUXY    86900   IN    TXT   nodes:ChEKDDE5Mi4xNjguMC4yNBCQTg
PCGDGGVEQQQFL4U2FYRXVHVMUM    86900   IN    TXT   nodes:ChEKDDE5Mi4xNjguMC4xMxCQTg
...
PCGDGGVEQQQFL4U2FYRXVHVMUM    86900   IN    TXT   nodes:ChEKDDE5Mi4xNjguMC4xMxCQTg

For conciseness, we use G763M53MOPYWUVJSW6CGE27GE4 to refer G763M53MOPYWUVJSW6CGE27GE4.nodes.example.org in DNS TXT record.

DNS Look-Up

Some DNS servers are not always available in some countries because of network restrictions, so it's necessary to provide a list of tested and widely available servers that are compatible with either ipv4 or ipv6.

We can get TXT records by using the command dig:

dig nodes.example.org TXT

Build a tree with two subtrees

Every ‘n’ leaf constructs a branch and every ‘n’ branch constructs a parent branch. So we recursively build an ‘n-ary' tree with leaf nodes and branch nodes.

Suppose we have already collected 40 nodes( EndPoint as above) from DHT, with address 192.168.0.1 ~ 192.168.0.40, port 10000, all nodes' addressIpv6 and nodeId are null. we can build a node tree as the following, each leaf contains a node.

                                  b  r  a  n  c  h  4
                   /                 /           \          \
                /                  /               \           \
             /                    /                   \            \
          branch1               branch2              branch3          \
        /      \              /       \             /       \           \
      node-01 ~ node-13    node-14 ~ node-26     node-27 ~ node-39    node-40

The tree's depth is 3. As the DNS TXT record's length has a 512-byte limit, each branch can contain 13 children at most, namely n = 13. Building a link tree has the same step as a node tree.

Merge nodes

If we have hundreds of nodes and each leaf contains only one node, we may have to sync too many times to get all nodes. It is observed that the length of the node is very short and far from 512, so it is possible to merge several nodes into one leaf to reduce network interactions. If we want to update some nodes, namely delete old nodes and add new nodes, but how to make changes as small as possible? It should be done like this:

  1. Divide these nodes according to the A network segment of the IP address into 256 groups.
  2. Sort nodes in one group by address.
  3. Merge several nodes into a leaf, define maxMergeSize, and default 5 nodes.

According to the test, we may reduce 70% of DNS requests using maxMergeSize = 5 compared to that maxMergeSize=1 in go-ethereum.

Client Protocol

To find nodes at a given DNS name say “mynodes.org”:

  1. Resolve the TXT record of the name and check whether it contains a valid “tree-root-v1:” entry. Let’s say the eRoot hash contained in the entry is “JXR4V3C7T6PNCVGY5JHPTNX7DI”.
  2. Verify the signature on the root against the known public key and check whether the sequence number is larger than or equal to any previous number seen for that name.
  3. Resolve the TXT record of the hash subdomain, e.g. “CFZUWDU7JNQR4VTCZVOJZ5ROV4.mynodes.org” and verify whether the content matches the hash.
  4. The next step depends on the entry type found:
    • for tree-branch: parse the list of hashes and continue resolving them (step 3).
    • for nodes: decode, verify the node record and import it to local node storage.

During traversal, the client must track hashes and domains which are already resolved to avoid going into an infinite loop. It’s in the client’s best interest to traverse the tree in random order.

Client implementations should avoid downloading the entire tree at once during normal operation. It’s much better to request entries via DNS when needed, i.e. at the time when the client is looking for peers.

Publish DNS periodically

DNS nodes from DHT changes dynamically, publish every change is very expensive and low efficient. One task will check the changes periodically, it only publish the changes when change percent is bigger than a threshold. Clients that syncs the whole tree only need to check the root periodically at the same time.

Rationale

Why DNS?

Choosing DNS as the distribution medium because it is always available, even under restrictive network conditions. The protocol provides low latency and answers to DNS queries can be cached by intermediate resolvers. No custom server software is needed. Node lists can be deployed to any DNS provider such as CloudFlare DNS, Aliyun DNS, and Amazon Route 53 using their respective client libraries. The user only has to implement this interface:

public interface Publish<T> {
  void deploy(String domainName, Tree t) throws Exception;

  boolean deleteDomain(String domainName) throws Exception;

  Map<String, T> collectRecords(String domainName) throws Exception;
}

In libp2p, it is implemented with Amazon Route 53 and Aliyun.

Why is this a merkle tree?

Being a merkle tree, any node list can be authenticated by a single signature on the root. Hash subdomains protect the integrity of the list. At worst intermediate resolvers can block access to the list or disallow updates to it, but cannot corrupt its content. The sequence number prevents replacing the root with an older version.

Synchronizing updates on the client side can be done incrementally, which matters for large lists. Individual entries of the tree are small enough to fit into a single UDP packet, ensuring compatibility with environments where only basic UDP DNS is available. The tree format also works well with caching resolvers: only the root of the tree needs a short TTL. Intermediate entries and leaves can be cached for days.

Why does the link subtree exist?

Links between lists enable federation and web-of-trust functionality. The operator of a large list can delegate maintenance to other list providers. If two node lists link to each other, users can use either list and get nodes from both.

The link subtree is separate from the tree containing nodes. This is done to enable client implementations to sync these trees independently. A client wanting to get as many nodes as possible will sync the link tree first and add all linked names to the sync horizon.

Security Considerations

Discovery via DNS is less secure than via DHT because it relies on a trusted party to publish the records regularly. The actor could easily eclipse bootstrapping nodes by only publishing node records that it controls.

Reference

EIP-1459

@317787106 317787106 changed the title DNS-Based Node Discovery Tip-548 DNS-Based Node Discovery May 22, 2023
@317787106 317787106 changed the title Tip-548 DNS-Based Node Discovery TIP-548 DNS-Based Node Discovery May 22, 2023
@317787106 317787106 changed the title TIP-548 DNS-Based Node Discovery TIP-548: DNS-Based Node Discovery May 22, 2023
@jernganl
Copy link

Good idea.
This DNS-Based Node Discovery makes network more convenient.

@sunflower-sun
Copy link

When Kademlia is not avaliable, node can still finds peers through DNS. Very useful feature.

@ExOCeo
Copy link

ExOCeo commented May 25, 2023

Are the widely available DNS servers provided in the node config file or are acquired dynamically as a process?

@souppopnix
Copy link

I like the merkle tree part, would be great if have more details.

@317787106
Copy link
Contributor Author

Are the widely available DNS servers provided in the node config file or are acquired dynamically as a process?

you can provide the dns tree in config file.

@317787106
Copy link
Contributor Author

I like the merkle tree part, would be great if have more details.

You can find more detail on https://github.com/tronprotocol/libp2p/tree/release_v0.2.0/src/main/java/org/tron/p2p/dns

@317787106
Copy link
Contributor Author

Are the widely available DNS servers provided in the node config file or are acquired dynamically as a process?

After this feature enables, we will give a dns tree for sharing. Of course everyone can publish their own tree.

@Ocea91
Copy link

Ocea91 commented May 26, 2023

The client would sync the regularly published DNS, is it synced automatically or manually? How often should the check need to be done if manually?

@317787106
Copy link
Contributor Author

317787106 commented May 26, 2023

The client would sync the regularly published DNS, is it synced automatically or manually? How often should the check need to be done if manually?

The client will sync the dns tree only if it finds that the tree's root has changed, the process is automatically.

@317787106
Copy link
Contributor Author

Close this issue as it is implemented by GreatVoyage-v4.7.2.
Check TIP detail at TIP-548
Check implementation PR at tronprotocol/libp2p#6, tronprotocol/libp2p#22

@317787106
Copy link
Contributor Author

317787106 commented Aug 1, 2023

official dns tree of tron mainnet is support: "tree://AKMQMNAJJBL73LXWPXDI4I5ZWWIZ4AWO34DWQ636QOBBXNFXH3LQS@main.trondisco.net".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants