Skip to content

Commit

Permalink
feat: Node discovery via DNS (hyperledger#7129)
Browse files Browse the repository at this point in the history
- Node discovery via DNS module as a vertex verticle (code adapted from Tuweni)
- Mock DNS server in unit tests.

---------

Signed-off-by: Usman Saleem <usman@usmans.info>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
  • Loading branch information
usmansaleem and macfarla authored Jun 5, 2024
1 parent 9d8c191 commit e4902af
Show file tree
Hide file tree
Showing 18 changed files with 1,376 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
- Default bonsai to use full-flat db and code-storage-by-code-hash [#6984](https://github.com/hyperledger/besu/pull/6894)
- New RPC methods miner_setExtraData and miner_getExtraData [#7078](https://github.com/hyperledger/besu/pull/7078)
- Disconnect peers that have multiple discovery ports since they give us bad neighbours [#7089](https://github.com/hyperledger/besu/pull/7089)
- Port Tuweni dns-discovery into Besu. [#7129](https://github.com/hyperledger/besu/pull/7129)

### Known Issues
- [Frequency: occasional < 10%] Chain download halt. Only affects new syncs (new nodes syncing from scratch). Symptom: Block import halts, despite having a full set of peers and world state downloading finishing. Generally restarting besu will resolve the issue. We are tracking this in [#6884](https://github.com/hyperledger/besu/pull/6884)
Expand Down
19 changes: 19 additions & 0 deletions NOTICE
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Hyperledger Besu
Copyright contributors to Hyperledger Besu.

This product includes software adapted from Tuweni. (https://tuweni.tmio.io/)
Copyright 2023-2024 The Machine Consultancy LLC
Licensed under Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)

The following NOTICE file content is provided by Tuweni:
------------------------------------------------------------
This product includes code developed under the Apache Tuweni incubation project.

Copyright 2019-2023 The Apache Software Foundation

This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

In addition, this product includes software developed by
Copyright 2018-2019 ConsenSys, Inc.
------------------------------------------------------------
Empty file removed NOTICE.md
Empty file.
4 changes: 1 addition & 3 deletions ethereum/p2p/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,6 @@ dependencies {
implementation('io.tmio:tuweni-devp2p') {
exclude group:'ch.qos.logback', module:'logback-classic'
}
implementation('io.tmio:tuweni-dns-discovery'){
exclude group:'ch.qos.logback', module:'logback-classic'
}
implementation 'io.tmio:tuweni-io'
implementation 'io.tmio:tuweni-rlp'
implementation 'io.tmio:tuweni-units'
Expand Down Expand Up @@ -84,6 +81,7 @@ dependencies {
}
testImplementation 'io.vertx:vertx-codegen'
testImplementation 'io.vertx:vertx-unit'
testImplementation 'io.vertx:vertx-junit5'
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.awaitility:awaitility'
testImplementation 'org.junit.jupiter:junit-jupiter'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.p2p.discovery.dns;

import java.util.List;
import java.util.Optional;

import io.vertx.core.AbstractVerticle;
import org.apache.tuweni.devp2p.EthereumNodeRecord;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

// Adapted from https://github.com/tmio/tuweni and licensed under Apache 2.0
/**
* Resolves DNS records over time, refreshing records. This is written as a Vertx Verticle which
* allows to run outside the Vertx event loop
*/
public class DNSDaemon extends AbstractVerticle {
private static final Logger LOG = LoggerFactory.getLogger(DNSDaemon.class);
private final String enrLink;
private final long seq;
private final long initialDelay;
private final long delay;
private final Optional<DNSDaemonListener> listener;
private final Optional<String> dnsServer;
private Optional<Long> periodicTaskId = Optional.empty();
private DNSResolver dnsResolver;

/**
* Creates a new DNSDaemon.
*
* @param enrLink the ENR link to start with, of the form enrtree://PUBKEY@domain
* @param listener Listener notified when records are read and whenever they are updated.
* @param seq the sequence number of the root record. If the root record seq is higher, proceed
* with visit.
* @param initialDelay the delay in milliseconds before the first poll of DNS records.
* @param delay the delay in milliseconds at which to poll DNS records. If negative or zero, it
* runs only once.
* @param dnsServer the DNS server to use for DNS query. If null, the default DNS server will be
* used.
*/
public DNSDaemon(
final String enrLink,
final DNSDaemonListener listener,
final long seq,
final long initialDelay,
final long delay,
final String dnsServer) {
this.enrLink = enrLink;
this.listener = Optional.ofNullable(listener);
this.seq = seq;
this.initialDelay = initialDelay;
this.delay = delay;
this.dnsServer = Optional.ofNullable(dnsServer);
}

/** Starts the DNSDaemon. */
@Override
public void start() {
if (vertx == null) {
throw new IllegalStateException("DNSDaemon must be deployed as a vertx verticle.");
}

LOG.info("Starting DNSDaemon for {}, using {} DNS host.", enrLink, dnsServer.orElse("default"));
dnsResolver = new DNSResolver(vertx, enrLink, seq, dnsServer);
if (delay > 0) {
periodicTaskId = Optional.of(vertx.setPeriodic(initialDelay, delay, this::refreshENRRecords));
} else {
// do one-shot resolution
refreshENRRecords(0L);
}
}

/** Stops the DNSDaemon. */
@Override
public void stop() {
LOG.info("Stopping DNSDaemon for {}", enrLink);
periodicTaskId.ifPresent(vertx::cancelTimer);
dnsResolver.close();
}

/**
* Refresh enr records by calling dnsResolver and updating the listeners.
*
* @param taskId the task id of the periodic task
*/
void refreshENRRecords(final Long taskId) {
LOG.debug("Refreshing DNS records");
final long startTime = System.nanoTime();
final List<EthereumNodeRecord> ethereumNodeRecords = dnsResolver.collectAll();
final long endTime = System.nanoTime();
LOG.debug("Time taken to DNSResolver.collectAll: {} ms", (endTime - startTime) / 1_000_000);
listener.ifPresent(it -> it.newRecords(dnsResolver.sequence(), ethereumNodeRecords));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.p2p.discovery.dns;

import java.util.List;

import org.apache.tuweni.devp2p.EthereumNodeRecord;

// Adapted from https://github.com/tmio/tuweni and licensed under Apache 2.0
/** Callback listening to updates of the DNS records. */
@FunctionalInterface
public interface DNSDaemonListener {
/**
* Callback called when the seq is updated on the DNS server
*
* @param seq the update identifier of the records
* @param records the records stored on the server
*/
void newRecords(long seq, List<EthereumNodeRecord> records);
}
Loading

0 comments on commit e4902af

Please sign in to comment.