Skip to content

Commit ce8ff2f

Browse files
committed
Merge branch 'master' into support-binary
2 parents 4a14a22 + c995869 commit ce8ff2f

File tree

145 files changed

+2542
-431
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

145 files changed

+2542
-431
lines changed

Diff for: .codespell/.codespellrc

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[codespell]
2+
skip = .git,package-lock.json,LOG.old.*
3+
count =
4+
quiet-level = 3
5+
ignore-words = ./.codespell/wordlist.txt

Diff for: .codespell/wordlist.txt

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
afterall
2+
errorprone
3+
vertx
4+
dout
5+
interruptors
6+
bu

Diff for: .github/workflows/codespell.yml

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# A Github action that using codespell to check spell.
2+
# .codespell/.codespellrc is a config file.
3+
# .codespell/wordlist.txt is a list of words that will ignore word checks.
4+
# More details please check the following link:
5+
# https://github.com/codespell-project/codespell
6+
7+
name: Codespell
8+
9+
on: pull_request
10+
11+
jobs:
12+
codespell:
13+
runs-on: ubuntu-latest
14+
15+
steps:
16+
- name: Checkout the repository
17+
uses: actions/checkout@v4
18+
19+
- name: Install prerequisites
20+
run: pip install codespell
21+
22+
- name: Spell check
23+
run: codespell --config=./.codespell/.codespellrc

Diff for: CHANGELOG.md

+2-5
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,9 @@ the [releases page](https://github.com/Consensys/teku/releases).
1010
## Unreleased Changes
1111

1212
### Breaking Changes
13-
- When a lock file is unable to be cleaned up, the (BN or VC) will now exit code 2 in preference to being 'up' but not able to perform duties. This will not self recover and will need intervention from the node operator.
1413

1514
### Additions and Improvements
16-
- Improve block rewards calculation performance for `/eth/v3/validator/blocks/{slot}` block production beacon node API.
17-
- Updated Javalin to v.6 (used by rest-api and keymanager-api).
18-
- Docker image tags now default to jdk21 images unless a jdk-specific tag is used.
15+
- Introduced [Validator Slashing Prevention feature](https://docs.teku.consensys.io/how-to/prevent-slashing/detect-slashing).
16+
- If the EL supports the `engine_getClientVersionV1` Engine API method, the default graffiti (when no graffiti has been configured by the validator) will include EL as well as CL version information. For more details, please see https://github.com/ethereum/execution-apis/pull/517.
1917

2018
### Bug Fixes
21-
- Fixed an issue where stale lock files weren't able to be cleaned up and would effectively park the service (BN or VC) with no user errors or any indication that the service was in a bad state.

Diff for: README.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
[![Twitter Follow](https://img.shields.io/twitter/follow/Teku_Consensys)](https://twitter.com/Teku_Consensys)
88
[![GitPOAP Badge](https://public-api.gitpoap.io/v1/repo/ConsenSys/teku/badge)](https://www.gitpoap.io/gh/ConsenSys/teku)
99

10-
Teku is an open-source Ethereum consensus client written in Java and containing a full beacon node and validator client implementation.
10+
Teku is an open-source Ethereum consensus client written in Java and containing a full beacon node and validator client implementation.
11+
1112
See the [Changelog](https://github.com/Consensys/teku/releases) for details of the latest releases and upcoming breaking changes.
1213

1314
## Useful links

Diff for: acceptance-tests/src/acceptance-test/java/tech/pegasys/teku/test/acceptance/ValidatorClientServiceAcceptanceTest.java

+63-8
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,20 @@
1313

1414
package tech.pegasys.teku.test.acceptance;
1515

16+
import static org.assertj.core.api.Assertions.assertThat;
17+
import static org.junit.jupiter.api.Assertions.assertTrue;
18+
import static org.junit.jupiter.api.Assertions.fail;
19+
1620
import java.nio.file.Path;
1721
import java.util.Collections;
22+
import java.util.Map;
23+
import org.apache.logging.log4j.LogManager;
24+
import org.apache.logging.log4j.Logger;
1825
import org.junit.jupiter.api.Test;
1926
import org.junit.jupiter.api.io.TempDir;
2027
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
2128
import tech.pegasys.teku.test.acceptance.dsl.GenesisGenerator;
29+
import tech.pegasys.teku.test.acceptance.dsl.SimpleHttpClient;
2230
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
2331
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfig;
2432
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
@@ -27,6 +35,7 @@
2735
import tech.pegasys.teku.test.acceptance.dsl.tools.deposits.ValidatorKeystores;
2836

2937
public class ValidatorClientServiceAcceptanceTest extends AcceptanceTestBase {
38+
private static final Logger LOG = LogManager.getLogger();
3039

3140
@Test
3241
void shouldFailWithNoValidatorKeysWhenExitOptionEnabledOnBeaconNode() throws Exception {
@@ -56,8 +65,7 @@ void bn_shouldFailIfValidatorKeyLocked(@TempDir final Path tempDir) throws Excep
5665
.withInitialState(genesis)
5766
.build());
5867

59-
beaconNode.startWithFailure(
60-
"Unable to initialize validator keys, please manually correct errors and try again.");
68+
beaconNode.startWithFailure("FATAL - Failed to load keystore", 20);
6169
}
6270

6371
@Test
@@ -82,8 +90,7 @@ void vc_shouldFailIfValidatorKeyLocked(@TempDir final Path tempDir) throws Excep
8290
.withWritableKeystorePath(initialKeystores, tempDir)
8391
.build());
8492

85-
validatorNode.startWithFailure(
86-
"Unable to initialize validator keys, please manually correct errors and try again.");
93+
validatorNode.startWithFailure("FATAL - Failed to load keystore", 20);
8794
}
8895

8996
@Test
@@ -104,8 +111,7 @@ void bn_shouldFailIfCannotLockKeys(@TempDir final Path tempDir) throws Exception
104111
.withReadOnlyKeystorePath(initialKeystores, tempDir)
105112
.build());
106113

107-
beaconNode.startWithFailure(
108-
"Unable to initialize validator keys, please manually correct errors and try again.");
114+
beaconNode.startWithFailure("FATAL - Please check the logs for details.");
109115
}
110116

111117
@Test
@@ -127,8 +133,7 @@ void bn_shouldFailIfCannotLockKeysUnhandledException() throws Exception {
127133
.withValidatorKeystoreLockingEnabled(true)
128134
.build());
129135

130-
beaconNode.startWithFailure(
131-
"Unable to initialize validator keys, please manually correct errors and try again.");
136+
beaconNode.startWithFailure("FATAL - Failed to load keystore, error Access Denied", 20);
132137
}
133138

134139
@Test
@@ -183,4 +188,54 @@ void shouldNotFailWithNoValidatorKeysWhenExitOptionDisabledOnValidatorClient() t
183188
validatorClient.stop();
184189
beaconNode.stop();
185190
}
191+
192+
@Test
193+
void shouldStartValidatorApiWithoutSslAndAccessData() throws Exception {
194+
final TekuBeaconNode beaconNode =
195+
createTekuBeaconNode(
196+
TekuNodeConfigBuilder.createBeaconNode()
197+
.withExitWhenNoValidatorKeysEnabled(false)
198+
.withValidatorApiNoSsl(true)
199+
.withSpecifiedBearerToken("admin")
200+
.withInteropValidators(0, 8)
201+
.build());
202+
beaconNode.start();
203+
beaconNode.waitForLogMessageContaining("UNSAFE");
204+
try {
205+
final SimpleHttpClient client = new SimpleHttpClient();
206+
final String result =
207+
client.get(
208+
beaconNode.getValidatorApiUrl(),
209+
"/eth/v1/keystores",
210+
Map.of("Authorization", "Bearer admin"));
211+
assertThat(result).contains("validating_pubkey");
212+
} catch (AssertionError ex) {
213+
fail("Failed to read response from keystores");
214+
}
215+
}
216+
217+
@Test
218+
void shouldStartValidatorApiWithoutSslAndRequireCorrectBearer() throws Exception {
219+
boolean caught = false;
220+
final TekuBeaconNode beaconNode =
221+
createTekuBeaconNode(
222+
TekuNodeConfigBuilder.createBeaconNode()
223+
.withExitWhenNoValidatorKeysEnabled(false)
224+
.withValidatorApiNoSsl(true)
225+
.withSpecifiedBearerToken("admin")
226+
.withInteropValidators(0, 8)
227+
.build());
228+
beaconNode.start();
229+
beaconNode.waitForLogMessageContaining("UNSAFE");
230+
try {
231+
final SimpleHttpClient client = new SimpleHttpClient();
232+
// without a bearer token this will fail
233+
client.get(beaconNode.getValidatorApiUrl(), "/eth/v1/keystores");
234+
} catch (AssertionError ex) {
235+
caught = true;
236+
LOG.debug(ex.getMessage());
237+
assertThat(ex.getMessage()).contains("401");
238+
}
239+
assertTrue(caught);
240+
}
186241
}

Diff for: acceptance-tests/src/acceptance-test/java/tech/pegasys/teku/test/acceptance/validatorslashing/MultiPeersStandAloneVcAcceptanceTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
* - Node 2: Stand-alone VC with a separate BN <br>
2929
* The slashing event is sent to the first node via the POST attester/proposer slashing REST API. It
3030
* is then sent <br>
31-
* to the second BN which sends it to it's VC vie the attester/proposer slashing SSE channel
31+
* to the second BN which sends it to it's VC via the attester/proposer slashing SSE channel
3232
*/
3333
public class MultiPeersStandAloneVcAcceptanceTest extends ValidatorSlashingDetectionAcceptanceTest {
3434

Diff for: acceptance-tests/src/acceptance-test/java/tech/pegasys/teku/test/acceptance/validatorslashing/MultiPeersStandAloneVcBlocksAcceptanceTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
* - Node 2: Stand-alone VC with a separate BN <br>
2929
* The slashing event is sent to the first node via the POST attester/proposer slashing REST API. It
3030
* is then sent <br>
31-
* to the second BN withing a block which sends it to it's VC via the attester/proposer slashing SSE
31+
* to the second BN within a block which sends it to it's VC via the attester/proposer slashing SSE
3232
* channel
3333
*/
3434
public class MultiPeersStandAloneVcBlocksAcceptanceTest

Diff for: acceptance-tests/src/acceptance-test/java/tech/pegasys/teku/test/acceptance/validatorslashing/ValidatorSlashingDetectionAcceptanceTest.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ public class ValidatorSlashingDetectionAcceptanceTest extends AcceptanceTestBase
4545
final SystemTimeProvider timeProvider = new SystemTimeProvider();
4646
final String network = "swift";
4747
final String slashingActionLog =
48-
"Validator(s) with public key(s) %s got slashed. Shutting down...";
48+
"Validator slashing detection is enabled and validator(s) with public key(s) %s detected as slashed. "
49+
+ "Shutting down...";
4950
final int shutdownWaitingSeconds = 60;
5051

5152
enum SlashingEventType {

Diff for: acceptance-tests/src/testFixtures/java/tech/pegasys/teku/test/acceptance/dsl/TekuBeaconNode.java

+5-1
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,13 @@ private TekuBeaconNode(
113113
super(network, TEKU_DOCKER_IMAGE_NAME, version, LOG);
114114
this.config = tekuNodeConfig;
115115
this.spec = SpecFactory.create(config.getNetworkName(), config.getSpecConfigModifier());
116+
if (config.getConfigMap().containsKey("validator-api-enabled")) {
117+
container.addExposedPort(VALIDATOR_API_PORT);
118+
}
119+
120+
container.addExposedPorts(METRICS_PORT, REST_API_PORT);
116121
container
117122
.withWorkingDirectory(WORKING_DIRECTORY)
118-
.withExposedPorts(REST_API_PORT, METRICS_PORT)
119123
.waitingFor(
120124
new HttpWaitStrategy()
121125
.forPort(REST_API_PORT)

Diff for: acceptance-tests/src/testFixtures/java/tech/pegasys/teku/test/acceptance/dsl/TekuNode.java

+20-2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import tech.pegasys.teku.api.response.v1.EventType;
3232

3333
public abstract class TekuNode extends Node {
34+
public static final int VALIDATOR_API_PORT = 9052;
3435
private static final Logger LOG = LogManager.getLogger();
3536
protected boolean started = false;
3637

@@ -68,12 +69,18 @@ public void start() throws Exception {
6869
container.start();
6970
}
7071

72+
// be aware that these regex's slow down quickly the bigger they are
73+
// by default 10 second timeout, needs to be a short, simple match.
7174
public void startWithFailure(final String expectedError) throws Exception {
75+
startWithFailure(expectedError, 10);
76+
}
77+
78+
public void startWithFailure(final String expectedError, final int timeout) throws Exception {
7279
setUpStart();
7380
container.waitingFor(
7481
new LogMessageWaitStrategy()
75-
.withRegEx(".*" + expectedError + ".*")
76-
.withStartupTimeout(Duration.ofSeconds(10)));
82+
.withRegEx(".*?" + expectedError + ".*")
83+
.withStartupTimeout(Duration.ofSeconds(timeout)));
7784
container.start();
7885
}
7986

@@ -140,4 +147,15 @@ private String getEventUrl(List<EventType> events) {
140147
protected URI getRestApiUrl() {
141148
return URI.create("http://127.0.0.1:" + container.getMappedPort(REST_API_PORT));
142149
}
150+
151+
public URI getValidatorApiUrl() {
152+
final boolean isUseSsl =
153+
(boolean) getConfig().getConfigMap().getOrDefault("Xvalidator-api-ssl-enabled", true);
154+
final String prefix = isUseSsl ? "https" : "http";
155+
156+
final URI uri =
157+
URI.create(prefix + "://127.0.0.1:" + container.getMappedPort(VALIDATOR_API_PORT));
158+
LOG.debug("Validator URL: {}", uri);
159+
return uri;
160+
}
143161
}

Diff for: acceptance-tests/src/testFixtures/java/tech/pegasys/teku/test/acceptance/dsl/TekuNodeConfigBuilder.java

+24-3
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@
2525
import static tech.pegasys.teku.test.acceptance.dsl.Node.SENTRY_NODE_CONFIG_FILE_PATH;
2626
import static tech.pegasys.teku.test.acceptance.dsl.Node.WORKING_DIRECTORY;
2727
import static tech.pegasys.teku.test.acceptance.dsl.Node.copyToTmpFile;
28+
import static tech.pegasys.teku.test.acceptance.dsl.TekuNode.VALIDATOR_API_PORT;
2829
import static tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfig.DEFAULT_VALIDATOR_COUNT;
2930
import static tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfig.INITIAL_STATE_FILE;
30-
import static tech.pegasys.teku.test.acceptance.dsl.TekuValidatorNode.VALIDATOR_API_PORT;
3131

3232
import com.google.common.io.Resources;
3333
import io.libp2p.core.PeerId;
@@ -300,6 +300,27 @@ public TekuNodeConfigBuilder withValidatorApiEnabled() {
300300
return this;
301301
}
302302

303+
public TekuNodeConfigBuilder withValidatorApiNoSsl(final boolean ignoreSslInterface) {
304+
LOG.debug("Validator api enabled, no SSL, ignoreInterface={}", ignoreSslInterface);
305+
configMap.put("validator-api-enabled", true);
306+
configMap.put("Xvalidator-api-ssl-enabled", false);
307+
configMap.put("validator-api-port", VALIDATOR_API_PORT);
308+
configMap.put("validator-api-host-allowlist", "*");
309+
configMap.put("Xvalidator-api-unsafe-hosts-enabled", ignoreSslInterface);
310+
return this;
311+
}
312+
313+
public TekuNodeConfigBuilder withSpecifiedBearerToken(final String password) throws IOException {
314+
final String bearerPath = "/bearer.txt";
315+
LOG.debug("Setting bearer password, and mapping to file {}}", bearerPath);
316+
configMap.put("validator-api-bearer-file", bearerPath);
317+
final File bearerFile = File.createTempFile("bearer", ".txt");
318+
Files.writeString(bearerFile.toPath(), password, UTF_8);
319+
bearerFile.deleteOnExit();
320+
configFileMap.put(bearerFile, bearerPath);
321+
return this;
322+
}
323+
303324
public TekuNodeConfigBuilder withWritableKeystorePath(
304325
ValidatorKeystores keystores, Path tempDir) {
305326
LOG.debug("Xinterop-enabled=false");
@@ -390,8 +411,8 @@ public TekuNodeConfigBuilder withExternalMetricsClient(
390411
}
391412

392413
public TekuNodeConfigBuilder withStopVcWhenValidatorSlashedEnabled() {
393-
LOG.debug("Xshut-down-when-validator-slashed-enabled={}", true);
394-
configMap.put("Xshut-down-when-validator-slashed-enabled", true);
414+
LOG.debug("shut-down-when-validator-slashed-enabled={}", true);
415+
configMap.put("shut-down-when-validator-slashed-enabled", true);
395416
return this;
396417
}
397418

Diff for: acceptance-tests/src/testFixtures/java/tech/pegasys/teku/test/acceptance/dsl/TekuValidatorNode.java

-6
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import static tech.pegasys.teku.test.acceptance.dsl.metrics.MetricConditions.withNameEqualsTo;
1818
import static tech.pegasys.teku.test.acceptance.dsl.metrics.MetricConditions.withValueGreaterThan;
1919

20-
import java.net.URI;
2120
import java.nio.charset.StandardCharsets;
2221
import java.util.Map;
2322
import org.apache.logging.log4j.LogManager;
@@ -29,7 +28,6 @@
2928
public class TekuValidatorNode extends TekuNode {
3029

3130
private static final Logger LOG = LogManager.getLogger();
32-
public static final int VALIDATOR_API_PORT = 9052;
3331
protected static final String VALIDATOR_PATH = DATA_PATH + "validator/";
3432

3533
private final TekuNodeConfig config;
@@ -101,10 +99,6 @@ public void waitForBlockPublishedTo(final TekuBeaconNode node) {
10199
withValueGreaterThan(0));
102100
}
103101

104-
private URI getValidatorApiUrl() {
105-
return URI.create("https://127.0.0.1:" + container.getMappedPort(VALIDATOR_API_PORT));
106-
}
107-
108102
private String getApiPassword() {
109103
return container.copyFileFromContainer(
110104
VALIDATOR_PATH + "key-manager/validator-api-bearer",

Diff for: beacon/pow/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
dependencies {
22
implementation project(':ethereum:pow:api')
3+
implementation project(':ethereum:pow:merkletree')
34
implementation project(':ethereum:spec')
45
implementation project(':infrastructure:async')
56
implementation project(':infrastructure:bls')

Diff for: beacon/pow/src/main/java/tech/pegasys/teku/beacon/pow/DepositSnapshotFileLoader.java

+10-1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.apache.tuweni.bytes.Bytes;
3232
import tech.pegasys.teku.ethereum.pow.api.DepositTreeSnapshot;
3333
import tech.pegasys.teku.ethereum.pow.api.schema.LoadDepositSnapshotResult;
34+
import tech.pegasys.teku.ethereum.pow.merkletree.DepositTree;
3435
import tech.pegasys.teku.infrastructure.exceptions.InvalidConfigurationException;
3536
import tech.pegasys.teku.infrastructure.http.UrlSanitizer;
3637
import tech.pegasys.teku.infrastructure.io.resource.ResourceLoader;
@@ -68,11 +69,19 @@ public LoadDepositSnapshotResult loadDepositSnapshot() {
6869
try {
6970
STATUS_LOG.loadingDepositSnapshotResource(sanitizedUrl);
7071
final DepositTreeSnapshot depositTreeSnapshot = loadFromUrl(depositSnapshotResourceUrl);
72+
// Validate
73+
DepositTree.fromSnapshot(depositTreeSnapshot);
7174
STATUS_LOG.onDepositSnapshot(
7275
depositTreeSnapshot.getDepositCount(), depositTreeSnapshot.getExecutionBlockHash());
7376
return LoadDepositSnapshotResult.create(Optional.of(depositTreeSnapshot));
7477
} catch (final Exception e) {
75-
LOG.warn("Failed to load deposit tree snapshot from " + sanitizedUrl, e);
78+
if (e instanceof IllegalArgumentException) {
79+
LOG.warn(
80+
"Deposit tree snapshot loaded from " + sanitizedUrl + " is not a correct snapshot",
81+
e);
82+
} else {
83+
LOG.warn("Failed to load deposit tree snapshot from " + sanitizedUrl, e);
84+
}
7685

7786
if (isRequired) {
7887
throw new InvalidConfigurationException(

0 commit comments

Comments
 (0)