Commit ac83284e authored by luxq's avatar luxq

Initial commit

parents
Pipeline #869 failed with stages

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

This diff is collapsed.
[codespell]
skip = .git,package-lock.json,LOG.old.*,venv
count =
quiet-level = 3
ignore-words = ./.codespell/wordlist.txt
\ No newline at end of file
afterall
errorprone
vertx
dout
interruptors
bu
socio-economic
* text eol=lf
*.jar -text
*.bat -text
*.pcap binary
*.blocks binary
*.dll binary
*.dylib binary
*.so binary
*.gz binary
*.ssz binary
*.ssz_snappy binary
storage/src/integration-test/resources/tech/pegasys/teku/storage/client/compatibility/** binary
\ No newline at end of file
<!--
By filing an Issue, you are expected to comply with the Code of Conduct,
including treating everyone with respect:
https://github.com/Consensys/teku/blob/master/CODE-OF-CONDUCT.md
Not all sections will apply to all issue types.
-->
### Description
[Detailed description of the problem and the impact it has]
### Steps to Reproduce (Bug)
[Please be as specific as possible]
**Expected behavior:** [What you expect to happen]
**Actual behavior:** [What actually happens]
**Frequency:** [How regularly does it occur?]
### Versions (Add all that apply)
* Software version: [`teku --version` or look for log lines starting with `Teku version:`]
* Java version: [`java -version`]
* OS Name & Version: [`cat /etc/*release`]
* Docker Version: [`docker version`]
* Cloud VM, type, size: [Amazon Web Services I3-large]
<!-- Thanks for sending a pull request! Please check out our contribution guidelines: -->
<!-- https://github.com/Consensys/teku/blob/master/CONTRIBUTING.md -->
## PR Description
## Fixed Issue(s)
<!-- Please link to fixed issue(s) here using format: fixes #<issue number> -->
<!-- Example: "fixes #2" -->
## Documentation
- [ ] I thought about documentation and added the `doc-change-required` label to this PR if updates are required.
## Changelog
- [ ] I thought about adding a changelog entry, and added one if I deemed necessary.
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master, "*-integration", "release-*" ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '39 22 * * 5'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'java' ]
# CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ]
# Use only 'java-kotlin' to analyze code written in Java, Kotlin or both
# Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: adopt
java-version: 21
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v3
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
queries: security-extended,security-and-quality
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v3
# ℹ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3
with:
category: "/language:${{matrix.language}}"
# A Github action that uses codespell to check spell.
# .codespell/.codespellrc is a config file.
# .codespell/wordlist.txt is a list of words that will ignore word checks.
# More details please check the following link:
# https://github.com/codespell-project/codespell
name: Codespell
on: pull_request
jobs:
codespell:
runs-on: ubuntu-latest
steps:
- name: Checkout the repository
uses: actions/checkout@v4
- name: Set up a Python venv and install prerequisites
run: |
python3 -m venv venv
source venv/bin/activate
pip install codespell
- name: Spell check
run: |
source venv/bin/activate
codespell --config=./.codespell/.codespellrc
\ No newline at end of file
name: "Validate Gradle Wrapper"
on: [push, pull_request]
jobs:
validation:
name: "Validation"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: gradle/wrapper-validation-action@v2
\ No newline at end of file
name: "Test Path Check"
on: [push, pull_request]
jobs:
check:
name: "Check"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check test paths
run: ./scripts/testcheck.sh
\ No newline at end of file
*.bak
*.swp
*.tmp
*~.nib
*.iml
*.launch
*.swp
*.log
.lh/*
db/
db.version
.classpath
.DS_Store
.gradletasknamecache
.externalToolBuilders/
.gradle/
.idea/
.loadpath
.metadata
.prefs
.project
.recommenders/
.run
.settings
.springBeans
.vertx
bin/
local.properties
target/
tmp/
build/
out/
pow/src/main/resources/keys.json
!validator_test_data.json
/interopDepositsAndKeys.json
*.vscode/
pow/truffle/.vscode/
pow/truffle/node_modules/
pow/truffle/npm-debug.*
scripts/demo
config/runConfig.*
gradle/flow/
proto/build/
proto/gen/*
*.rlib
*.d
*.a
*.class
scripts/.java-version
config/peer_ids.dat
config/*.key
eth-reference-tests/src/test/
eth-reference-tests/src/referenceTest/generated/
eth-reference-tests/src/referenceTest/generated_tests/
eth-reference-tests/src/referenceTest/resources/consensus-spec-tests/
**/src/jmh/generated_tests/
**/src/jmh/generated/*
.jqwik-database
/teku.db
node_modules
\ No newline at end of file
version: 2.1
jobs:
build:
docker:
- image: alpine:3.7
steps:
- run:
name: noop
command: "true"
env:
node: true
commonjs: true
es2020: true
extends: 'eslint:recommended'
parserOptions:
ecmaVersion: 11
rules: {}
dist/
node_modules/
\ No newline at end of file
# Teku OpenAPI Spec Publish
This directory contains NodeJS project which publishes Teku OpenAPI specifications to
[`gh-pages`](https://github.com/Consensys/teku/tree/gh-pages) branch via CI job after tests are green.
See `publishOpenApiSpec` job in `.circleci/config.yml`.
The actual up-to-date generated doc is available at https://consensys.github.io/teku/
## Procedure
The script performs following tasks:
* Extract spec from Teku latest docker image by reaching the spec endpoint
* Create `dist` directory (this folder will be pushed to `gh-pages` branch)
* Copy the specs to `dist` as `teku-latest.json` (and latest.json for retro-compatibility on the name).
* Update the spec version file depending on tag existence for the build
For release version (when tagged with CalVer version), it performs following additional steps:
* Copy the spec to `dist` as `teku-<version>.json`
* Fetch `https://github.com/Consensys/teku/raw/gh-pages/versions.json`
* Update versions' json with release versions by updating `stable.spec` and `stable.source` to the release version and adding a new entry
for it. For example after adding spec version `20.11.0`, the `versions.json` would look like:
~~~
{
"latest": {
"spec": "latest",
"source": "master"
},
"stable": {
"spec": "20.11.1",
"source": "20.11.1"
},
"20.11.0": {
"spec": "20.11.0",
"source": "20.11.0"
}
"20.11.1": {
"spec": "20.11.1",
"source": "20.11.1"
}
}
~~~
* Save updated `versions.json` to `dist` folder.
* Push the `dist` folder to `gh-pages` branch. The script is using [gh-pages](https://www.npmjs.com/package/gh-pages)
npm module to automate this step.
## Environment variables
Following environment variables can be used to override defaults
* `OA_GIT_URL` (default: `git@github.com:Consensys/teku.git`)
* `OA_GH_PAGES_BRANCH` (default: `gh-pages`)
* `OA_GIT_USERNAME` (default: `CircleCI Build`)
* `OA_GIT_EMAIL` (default: `ci-build@consensys.net`)
Following should only be overridden if changing the project
* `OA_VERSIONS_FILE_NAME` (default: `versions.json`)
* `OA_DIST_DIR` (default: `./dist`)
* `OA_SPEC_URL` (default: `http://localhost:5051/swagger-docs`)
* `OA_SPEC_DIR` (default: `./openapidoc/spec`)
import fs from "fs";
import path from "path";
import yaml from "js-yaml";
import GitUrlParse from "git-url-parse";
const distDir = process.env.OA_DIST_DIR || "./dist";
const specDir =
process.env.OA_SPEC_DIR || "./spec";
const gitUrl =
process.env.OA_GIT_URL || "git@github.com:Consensys/teku.git";
const gitUserName = process.env.OA_GIT_USERNAME || "CircleCI Build";
const gitEmail = process.env.OA_GIT_EMAIL || "ci-build@consensys.net";
const branch = process.env.OA_GH_PAGES_BRANCH || "gh-pages";
const versionsFileName = process.env.OA_VERSIONS_FILE_NAME || "versions.json";
export default {
getConfig,
};
function getConfig() {
const repo = GitUrlParse(gitUrl);
const specs = calculateSpecs();
if (specs.length === 0) {
throw new Error("Unable to parse specs in dist" + distDir);
}
return {
specs: specs,
distDir: distDir,
versions: calculateVersionDetails(repo, branch),
ghPagesConfig: {
add: true, // allows gh-pages module to keep remote files
branch: branch,
repo: repo.href,
user: {
name: gitUserName,
email: gitEmail,
},
message: `[skip ci] OpenAPI Publish [${specs[0].version}]`,
},
};
}
function calculateSpecs() {
const extension = ".json";
const specFiles = fs.readdirSync(specDir);
var specArr = [];
specFiles.forEach((file) => {
if (path.extname(file).toLowerCase() === extension) {
specArr.push(calculateSpecDetails(path.join(specDir, file)));
}
});
return specArr;
}
function calculateSpecDetails(specFile) {
const specVersion = calculateSpecVersion(specFile);
const release = isReleaseVersion(specVersion);
const latestDist = destinationPath(true, specFile, "latest");
const latestDistCompat = destinationPath(false, specFile, "latest");
const releaseDist = destinationPath(true, specFile, specVersion);
return {
path: specFile,
version: specVersion,
isReleaseVersion: release,
latestDist: latestDist,
latestDistCompat: latestDistCompat,
releaseDist: releaseDist,
};
}
function calculateSpecVersion(specFile) {
return yaml.load(fs.readFileSync(specFile, "utf8")).info.version;
}
function isReleaseVersion(specVersion) {
// our main project's gradle's build calculateVersion adds "+<new commits since stable>-<hash>"
// after the version for dev builds
return !specVersion.includes("+");
}
function destinationPath(usePrefix, specFile, suffix) {
const prefix = usePrefix ? `${path.parse(specFile).name}-` : '';
const extension = path.extname(specFile);
return path.join(distDir, `${prefix}${suffix}${extension}`);
}
function calculateVersionDetails(repo, branch) {
const versionsFileUrl = `https://${repo.source}/${repo.owner}/${repo.name}/raw/${branch}/${versionsFileName}`;
const versionsFileDist = path.join(distDir, versionsFileName);
return {
url: versionsFileUrl,
dist: versionsFileDist,
};
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "openapidoc",
"version": "1.0.0",
"description": "Prepare Teku OpenAPI spec to be pushed to gh-pages branch",
"type": "module",
"repository": {
"type": "git",
"url": "git://github.com/Consensys/teku.git"
},
"bugs": {
"url": "https://github.com/Consensys/teku/issues"
},
"main": "./publish.js",
"dependencies": {
"gh-pages": "^5.0.0",
"git-url-parse": "^13.1.0",
"js-yaml": "^4.1.0",
"node-fetch": "^3.3.1"
},
"devDependencies": {
"eslint": "^7.9.0"
},
"scripts": {
"lint": "eslint publish.js config.js",
"start": "node publish.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Consensys Software",
"license": "Apache-2.0"
}
import fs from "fs";
import fetch from "node-fetch";
import ghpages from "gh-pages";
import config from "./config.js";
const log = (...args) => console.log(...args); // eslint-disable-line no-console
/**
* Main function to prepare and publish openapi spec to gh-pages branch
*/
async function main() {
try {
const cfg = config.getConfig();
const {distDir, specs, versions, ghPagesConfig} = cfg;
prepareDistDir(distDir);
specs.forEach(function (spec) {
copySpecFileToDist(spec);
});
if (specs[0].isReleaseVersion) {
const versionsJson = await fetchVersions(versions.url);
const updatedVersionsJson = updateVersions(
versionsJson,
specs[0].version
);
saveVersionsJson(updatedVersionsJson, versions.dist);
}
log("Publishing following files: ");
fs.readdirSync(distDir).forEach((file) => {
log(file);
});
cleanGhPagesCache();
await publishToGHPages(distDir, ghPagesConfig);
log(
`OpenAPI specs [${specs[0].version}] published to [${ghPagesConfig.branch}] using user [${ghPagesConfig.user.name}]`
);
} catch (err) {
log(`ERROR: OpenAPI spec failed to publish: ${err.message}`);
log(config);
process.exit(1);
}
}
/**
* Re-create dist dir
* @param {string} dirPath
*/
function prepareDistDir(dirPath) {
if (fs.existsSync(dirPath)) {
fs.rmdirSync(dirPath, {recursive: true});
}
fs.mkdirSync(dirPath, {recursive: true});
}
function copySpecFileToDist(spec) {
fs.copyFileSync(spec.path, spec.latestDist);
fs.copyFileSync(spec.path, spec.latestDistCompat);
if (spec.isReleaseVersion) {
fs.copyFileSync(spec.path, spec.releaseDist);
}
}
/**
* Fetch versions.json
*/
async function fetchVersions(versionsUrl) {
const response = await fetch(versionsUrl);
if (response.ok) {
const versionsJson = await response.json();
return versionsJson;
}
throw new Error(
`${versionsUrl} fetch failed with status: ${response.statusText}`
);
}
/**
* update versions
* @param versionsJson
* @param {string} specVersion
*/
function updateVersions(versionsJson, specVersion) {
versionsJson[specVersion] = {
spec: specVersion,
source: specVersion,
};
versionsJson["stable"] = {
spec: specVersion,
source: specVersion,
};
return versionsJson;
}
function saveVersionsJson(versionsJson, versionsDist) {
fs.writeFileSync(versionsDist, JSON.stringify(versionsJson, null, 1));
}
function cleanGhPagesCache() {
ghpages.clean();
}
/**
* Publish dist folder to gh-pages branch
*/
async function publishToGHPages(distDir, ghPagesConfig) {
return new Promise((resolve, reject) => {
ghpages.publish(distDir, ghPagesConfig, (err) => {
if (err) {
reject(err);
}
resolve();
});
});
}
// start execution of main method
main();
# Teku Bug Triage Process
In Teku, we have a well-defined policy regarding bug categorization.
#### Deciding Priority
When deciding how to respond to a bug, consider the two variables of probability and severity, and apply them to a risk matrix to determine the priority and corresponding action.
#### Probability
How often actual customers are likely to encounter the issue during their use of the product?
A feature rarely used, with a high likelihood of failure, may have a lower overall probability than a popular feature with a low likelihood of failure (due to a high number of customers affected).
<table>
<tbody>
<tr>
<th class="confluenceTh">
<p><strong>Frequent</strong></p>
</th>
<td class="confluenceTd">
<p title=""><span>Will occur several times in the lifetime of the product.</span></p>
</td>
</tr>
<tr>
<th class="confluenceTh">
<p><strong>Probable</strong></p>
</th>
<td class="confluenceTd">
<p><span>Likely to occur often in the lifetime of the product.</span></p>
</td>
</tr>
<tr>
<th class="confluenceTh">
<p><strong>Occasional</strong></p>
</th>
<td class="confluenceTd">
<p><span>Likely to occur some time in the lifetime of the product.</span></p>
</td>
</tr>
<tr>
<th class="confluenceTh">
<p><strong>Remote</strong></p>
</th>
<td class="confluenceTd">
<p><span>Unlikely but possible to occur in the lifetime of the product.</span></p>
</td>
</tr>
<tr>
<th class="confluenceTh">
<p><strong>Improbable</strong></p>
</th>
<td class="confluenceTd">
<p><span>So unlikely, it can be assumed occurrence may not be experienced.</span></p>
</td>
</tr>
</tbody>
</table>
#### Severity
How bad is the problem when it is encountered?
An issue that causes data loss/corruption is automatically classed as Catastrophic.
<table>
<tbody>
<tr>
<th class="confluenceTh">
<p><strong>Catastrophic</strong></p>
</th>
<td class="confluenceTd">
<p><span>Detrimental risk for the product as a whole.</span></p>
</td>
</tr>
<tr>
<th class="confluenceTh">
<p><strong>Critical</strong></p>
</th>
<td class="confluenceTd">
<p><span>Jeopardises feature(s) of the product, not ruining the product entirely.</span></p>
</td>
</tr>
<tr>
<th class="confluenceTh">
<p><strong>Moderate</strong></p>
</th>
<td class="confluenceTd">
<p><span>Jeopardises aspects of a feature, not ruining the feature entirely.</span></p>
</td>
</tr>
<tr>
<th class="confluenceTh">
<p><strong>Marginal</strong></p>
</th>
<td class="confluenceTd">
<p><span>Causes an inconvenience when using a feature of the product.</span></p>
</td>
</tr>
<tr>
<th class="confluenceTh">
<p><strong>Insignificant</strong></p>
</th>
<td class="confluenceTd">
<p><span>No significant threat posed and can be left unmediated.&nbsp;</span></p>
</td>
</tr>
</tbody>
</table>
#### Risk Matrix
<table class="tg">
<thead>
<tr>
<th class="tg-c3ow"></th>
<th class="tg-7btt">Catastrophic</th>
<th class="tg-7btt">Critical</th>
<th class="tg-7btt">Moderate</th>
<th class="tg-7btt">Marginal</th>
<th class="tg-7btt">Insignificant</th>
</tr>
</thead>
<tbody>
<tr>
<td class="tg-7btt"><b>Frequent</b></td>
<td class="tg-c3ow">Very High</td>
<td class="tg-c3ow">High</td>
<td class="tg-c3ow">High</td>
<td class="tg-c3ow">Medium</td>
<td class="tg-c3ow">Very Low</td>
</tr>
<tr>
<td class="tg-7btt"><b>Probable</b></td>
<td class="tg-c3ow">High</td>
<td class="tg-c3ow">High</td>
<td class="tg-c3ow">Medium</td>
<td class="tg-c3ow">Medium</td>
<td class="tg-c3ow">Very Low</td>
</tr>
<tr>
<td class="tg-7btt"><b>Occasional</b></td>
<td class="tg-c3ow">High</td>
<td class="tg-c3ow">Medium</td>
<td class="tg-c3ow">Medium</td>
<td class="tg-c3ow">Low</td>
<td class="tg-c3ow">Very Low</td>
</tr>
<tr>
<td class="tg-7btt"><b>Remote</b></td>
<td class="tg-c3ow">Medium</td>
<td class="tg-c3ow">Medium</td>
<td class="tg-c3ow">Low</td>
<td class="tg-c3ow">Low</td>
<td class="tg-c3ow">Very Low</td>
</tr>
<tr>
<td class="tg-7btt"><b>Improbable</b></td>
<td class="tg-c3ow">Medium</td>
<td class="tg-c3ow">Low</td>
<td class="tg-c3ow">Low</td>
<td class="tg-c3ow">Very Low</td>
<td class="tg-c3ow">Very Low</td>
</tr>
</tbody>
</table>
#### Examples
In a nutshell, our bugs are categorized into 5 priorities:
| Priority | Examples |
|-|-|
| Very High (P1) | <ul><li>Affects consensus</li> <li>Breaks sync</li><li>Security issue</li></ul> |
| High (P2) | <ul><li> Degrades validator rewards</li> <li>Incurs penalties</li> <li>Significant performance regression</li> <li>Unexpected behaviour of core features</li></ul>|
| Medium (P3) | <ul><li>Feature not working with a specific client library</li></ul>|
| Low (P4) | <ul><li>Node doesn't start up when the configuration file has unexpected "end-of-line" character</li></ul> |
| Very Low (P5) | <ul><li>Typo on a CLI command description</li> <li>An inconsequential issue gets logged on ERROR level</li></ul>|
#### Why is it important?
All software has bugs. And sometimes we don't find them. This is something that we need to live with. However, when we do find bugs we need to do our best to identify the potential impact of that bug in our system.
A proper triaged bug helps up to prioritize and act on bugs in a timely manner. Also, by analysing the severity and impact of our bugs, we can create/update processes to ensure we are better prepared for them in the future.
#### How to triage a bug?
All of our bugs are reported on GitHub issues. They are identified by the BUG label. We can make use of GitHub labels to categorize our bugs. We have 5 labels, P1, P2, ..., P5 to categorize a bug according to our [policy](#Bug-Priorities) shown above.
##### Step-by-step process (for maintainers)
1) Identify that a bug doesn't have a priority (no Px label).
2) Use the bug categorization policy to determine the final priority of the bug.
3) Based on your assessment, add the corresponding label to the bug.
4) (optional) Add a comment to the issue explaining the rationale (justifying the severity and probability analysis).
#### FAQ
##### What if I don't agree with the priority of a bug?
We try to be deterministic on our assessments, and most of the time it will involve some discussion. However, if you feel like the assessment on a bug priority is wrong, you can always add a comment on the issue and start a discussion.
##### Can I change the priority of a bug?
You can. However, if another person was responsible for the initial assessment it is expected that you will contact that person to share your thoughts before changing it.
It is a combination of being polite and reasonable. :)
##### What if I don't have enough knowledge of a specific area to prioritize a bug?
That's ok! Deeper understanding of the impact of a bug will come from the people that work closer to the affected area of the code. Leave it to them!
_Mostly taken from Besu Docs_:
* [Besu Bug Triage Process](https://wiki.hyperledger.org/display/BESU/Bug+Triage+Process)
* [Besu Defect Prioritisation Policy](https://wiki.hyperledger.org/display/BESU/Defect+Prioritisation+Policy)
# Changelog
## Upcoming Breaking Changes
## Current Releases
## Unreleased Changes
### Breaking Changes
### Additions and Improvements
### Bug Fixes
- Updated the gas change check for block building so that warnings only get raised if the change is off spec.
- Fixed an issue with the `/eth/v1/config/spec` API not returning all previously included configuration parameters.
- Increase the maximum size of a compressed message for libp2p to ensure uncompressed blocks can grow to max size.
Sign the CLA
=============
This page is the step-by-step guide to signing the Consensys Software Inc.
Individual Contributor License Agreement.
1. First and foremost, read [the current version of the CLA].
It is written to be as close to plain English as possible.
2. Make an account on [GitHub] if you don't already have one.
3. After creating your first pull request, you will see a merge
pre-requisite requiring you to read and sign the CLA.
If you have any questions, you can reach us on [Discord].
[Discord]: https://discord.gg/7hPv2T6
[GitHub]: https://github.com/
[the current version of the CLA]: https://gist.github.com/rojotek/978b48a5e8b68836856a8961d6887992
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sexual characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct that could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at [private-quorum@consensys.net]. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[Contributor Covenant]: https://www.contributor-covenant.org
[private-quorum@consensys.net]: mailto:private-quorum@consensys.net
# Contributing to Teku
:+1::tada: First off, thanks for taking the time to contribute! :tada::+1:
Welcome to the Teku repository! The following is a set of guidelines for contributing to this repo and its packages. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
#### Table Of Contents
[Code of Conduct](#code-of-conduct)
[I just have a quick question](#i-just-have-a-quick-question)
[How to contribute](#how-to-contribute)
* [Your first code contribution](#your-first-code-contribution)
* [Reporting bugs](#reporting-bugs)
[Style Guides](#style-guides)
* [Java Style Guide](#java-code-style-guide)
* [Coding Conventions](#coding-conventions)
* [Git Commit Messages & Pull Request Messages](#git-commit-messages--pull-request-messages)
## Code of Conduct
This project and everyone participating in it is governed by the [Teku Code of Conduct](CODE-OF-CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [private-quorum@consensys.net].
## I just have a quick question
You'll find us on [Discord] and that's the fastest way to get an answer.
## How To Contribute
### Your first code contribution
Start by looking through the 'good first issue' and 'help wanted' issues:
* [Good First Issue][search-label-good-first-issue] - issues which should only require a few lines of code, and a test or two.
* [Help wanted issues][search-label-help-wanted] - issues that are a bit more involved than `good first issue` issues.
Please keep in mind that we do not accept non-code contributions like fixing comments, typos or some other trivial fixes. Although we appreciate the extra help, managing lots of these small contributions is unfeasible, and puts extra pressure in our continuous delivery systems (running all tests, etc). Feel free to open an issue pointing any of those errors and we will batch them into a single change.
### Local Development
The codebase is maintained using the "*contributor workflow*" where everyone without exception contributes patch proposals using "*pull-requests*". This facilitates social contribution, easy testing and peer review.
To contribute a patch, the workflow is as follows:
* Fork repository
* Create topic branch
* Commit patch
* Create pull-request, adhering to the coding conventions herein set forth
In general a commit serves a single purpose and diffs should be easily comprehensible. For this reason do not mix any formatting fixes or code moves with actual code changes.
### Architectural Best Practices
Questions on architectural best practices will be guided by the principles set forth in Effective Java by Joshua Bloch
### Automated Test coverage
All code submissions must be accompanied by appropriate automated tests. The goal is to provide confidence in the code’s robustness, while avoiding redundant tests.
### Pull Requests
The process described here has several goals:
- Maintain Product quality
- Fix problems that are important to users
- Engage the community in working toward the best possible product
- Enable a sustainable system for maintainers to review contributions
- Further explanation on PR & commit messages can be found in this post: [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/).
Please follow these steps to have your contribution considered by the approvers:
1. Complete the CLA, as described in [CLA.md]
2. Follow all instructions in [PULL-REQUEST-TEMPLATE.md](.github/pull_request_template.md)
3. Include appropriate test coverage. Testing is 100% automated. There is no such thing as a manual test.
4. Follow the [Style Guides](#style-guides)
5. After you submit your pull request, verify that all [status checks](https://help.github.com/articles/about-status-checks/) are passing <details><summary>What if the status checks are failing?</summary>If a status check is failing, and you believe that the failure is unrelated to your change, please leave a comment on the pull request explaining why you believe the failure is unrelated. A maintainer will re-run the status check for you. If we conclude that the failure was a false positive, then we will open an issue to track that problem with our status check suite.</details>
While the prerequisites above must be satisfied prior to having your pull request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted. Please refer to [Code Reviews].
## Reporting Bugs
Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports.
Explain the problem and include additional details to help maintainers reproduce the problem:
* **Use a clear and descriptive title** for the issue to identify the problem.
* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you started Teku, e.g. which command exactly you used in the terminal, or how you started it otherwise.
* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
* **Explain which behavior you expected to see instead and why.**
* **Include screenshots** which show you following the described steps and clearly demonstrate the problem.
Provide more context by answering these questions:
* **Did the problem start happening recently** (e.g. after updating to a new version of the software) or was this always a problem?
* If the problem started happening recently, **can you reproduce the problem in an older version of the software?** What's the most recent version in which the problem doesn't happen?
* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens.
Include details about your configuration and environment:
* **Which version of the software are you using?** You can get the exact version by running `teku -v` in your terminal.
* **What OS & Version are you running?**
* **For Linux - What kernel are you running?** You can get the exact version by running `uname -a` in your terminal.
* **Are you running in a virtual machine?** If so, which VM software are you using and which operating systems and versions are used for the host and the guest?
* **Are you running in a docker container?** If so, what version of docker?
* **Are you running in a Cloud?** If so, which one, and what type/size of VM is it?
* **What version of Java are you running?** You can get the exact version by looking at the Teku logfile during startup.
# Style Guides
## Java Code Style Guide
We use Google's Java coding conventions for the project. To reformat code, run:
```
./gradlew spotlessApply
```
Code style will be checked automatically during a build.
## Coding Conventions
We have a set of [coding conventions](https://wiki.hyperledger.org/display/BESU/Coding+Conventions) to which we try to adhere. These are not strictly enforced during the build, but should be adhered to and called out in code reviews.
## Git Commit Messages & Pull Request Messages
* Use the present tense ("Add feature" not "Added feature")
* Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
* Provide a summary on the first line with more details on additional lines as needed
* Reference issues and pull requests liberally
[private-quorum@consensys.net]: mailto:private-quorum@consensys.net
[Discord]: https://discord.gg/7hPv2T6
[CLA.md]: CLA.md
# Overview
This project is led by a benevolent dictator (Consensys) and managed by the community. That is, the community actively contributes to the day-to-day maintenance of the project, but the general strategic line is drawn by the benevolent dictator. In case of disagreement, they have the last word. It is the benevolent dictator’s job to resolve disputes within the community and to ensure that the project is able to progress in a coordinated way. In turn, it is the community’s job to guide the decisions of the benevolent dictator through active engagement and contribution.
# Principles
The community adheres to the following principles:
* Open: Teku is open source. See repository guidelines and CLA, below.
* Welcoming and respectful: See Code of Conduct, below.
* Transparent and accessible: Work and collaboration should be done in public.
* Merit: Ideas and contributions are accepted according to their technical merit and alignment with project objectives and design principles.
# Code of Conduct
See [code of conduct]
# Community membership
See [community membership]
# Decision Making
Decision making will be handled by the Approvers (see [community membership]). If consensus cannot be reached, the Benevolent Dictator will provide the final word on the decision.
# CLA
All contributors must sign the CLA, as described in [CLA.md].
## Attribution
This document was influenced by the following:
- Kubernetes community-membership.md, available at [kub community membership].
- Kubernetes governance.md, available at [kub governance]
- OSSWatch Benevolent Dictator Governance Model, available at [oss watch benevolent dictator].
[CLA.md]: CLA.md
[community membership]: community-membership.md
[code of conduct]: CODE-OF-CONDUCT.md
[oss watch benevolent dictator]: http://oss-watch.ac.uk/resources/benevolentdictatorgovernancemodel
[kub community membership]: https://raw.githubusercontent.com/kubernetes/community/master/community-membership.md
[kub governance]:https://github.com/kubernetes/community/blob/master/governance.md
This diff is collapsed.
# teku
[![Build Status](https://circleci.com/gh/Consensys/teku.svg?style=svg)](https://circleci.com/gh/Consensys/workflows/teku)
[![GitHub License](https://img.shields.io/github/license/Consensys/teku.svg?logo=apache)](https://github.com/Consensys/teku/blob/master/LICENSE)
[![Documentation](https://img.shields.io/badge/docs-readme-blue?logo=readme&logoColor=white)](https://docs.teku.consensys.io/)
[![Discord](https://img.shields.io/badge/Chat-on%20Discord-%235865F2?logo=discord&logoColor=white)](https://discord.gg/7hPv2T6)
[![Twitter Follow](https://img.shields.io/twitter/follow/Teku_Consensys)](https://twitter.com/Teku_Consensys)
[![GitPOAP Badge](https://public-api.gitpoap.io/v1/repo/ConsenSys/teku/badge)](https://www.gitpoap.io/gh/ConsenSys/teku)
Teku is an open-source Ethereum consensus client written in Java and containing a full beacon node and validator client implementation.
See the [Changelog](https://github.com/Consensys/teku/releases) for details of the latest releases and upcoming breaking changes.
## Useful links
* [Ethereum Beacon Chain specification](https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md)
* [Teku user documentation](https://docs.teku.consensys.net/)
* [Teku REST API reference documentation](https://consensys.github.io/teku/)
* [Teku issues](https://github.com/Consensys/teku/issues)
* [Contribution guidelines](CONTRIBUTING.md)
* [Teku Changelog](https://github.com/Consensys/teku/releases)
## Teku users
See our [user documentation](https://docs.teku.consensys.net/).
Raise a [documentation issue](https://github.com/Consensys/doc.teku/issues) or get in touch in
the #teku channel on [Discord](https://discord.gg/7hPv2T6) if you've got questions or feedback.
## Teku developers
* [Contribution Guidelines](CONTRIBUTING.md)
* [Coding Conventions](https://wiki.hyperledger.org/display/BESU/Coding+Conventions)
## Binary Releases
Binary releases are available from the [releases page](https://github.com/Consensys/teku/releases).
Binary builds that track the latest changes on the master branch are available on
[Dockerhub](https://hub.docker.com/r/consensys/teku) using the `develop` version or as binary
downloads ([tar.gz format](https://artifacts.consensys.net/public/teku/raw/names/teku.tar.gz/versions/develop/teku-develop.tar.gz)
or [zip format](https://artifacts.consensys.net/public/teku/raw/names/teku.zip/versions/develop/teku-develop.zip)).
We recommend only using release versions for Mainnet, but `develop` builds are useful for testing
the latest changes on testnets.
Release notifications are available via:
* Sign up to our [release announcements](https://pages.consensys.net/teku-sign-up) email list (release and important announcements only, no marketing)
* Follow us on [Twitter](https://twitter.com/Teku_Consensys)
* `teku` in [Consensys Discord](https://discord.gg/7hPv2T6),
* Subscribe to release notifications on github for [teku](https://github.com/Consensys/teku)
## Build Instructions
### Install Prerequisites
* Java 21+
Note: Official builds of Teku are performed with Java 21.
Building on a more recent version of Java is supported, but the resulting build will not work on earlier versions of Java.
### Build and Dist
To create a ready to run distribution:
```shell script
git clone https://github.com/Consensys/teku.git
cd teku && ./gradlew distTar installDist
```
This produces:
- Fully packaged distribution in `build/distributions`
- Expanded distribution, ready to run in `build/install/teku`
### Build and Test
To build, clone this repo and run with `gradle`:
```shell script
git clone https://github.com/Consensys/teku.git
cd teku && ./gradlew
```
After a successful build, distribution packages are available in `build/distributions`.
### Other Useful Gradle Targets
| Target | Builds |
|-------------|---------------------------------------------------------|
| distTar | Full distribution in build/distributions (as `.tar.gz`) |
| distZip | Full distribution in build/distributions (as `.zip`) |
| installDist | Expanded distribution in `build/install/teku` |
| distDocker | The `consensys/teku` docker image |
## Code Style
We use Google's Java coding conventions for the project. To reformat code, run:
```shell script
./gradlew spotlessApply
```
Code style is checked automatically during a build.
## Testing
All the unit tests are run as part of the build, but can be explicitly triggered with:
```shell script
./gradlew test
```
## Special thanks
YourKit for providing us with a free profiler open source license.
YourKit supports open source projects with innovative and intelligent tools
for monitoring and profiling Java and .NET applications.
YourKit is the creator of <a href="https://www.yourkit.com/java/profiler/">YourKit Java Profiler</a>,
<a href="https://www.yourkit.com/.net/profiler/">YourKit .NET Profiler</a>,
and <a href="https://www.yourkit.com/youmonitor/">YourKit YouMonitor</a>.
![YourKit Logo](https://www.yourkit.com/images/yklogo.png)
# Security Policy
## Reporting a Vulnerability
If you think you have discovered a security issue in Teku, we'd love to hear from you. We will take
all security bugs seriously and if confirmed upon investigation we will patch it within a reasonable
amount of time and release a public security bulletin discussing the impact and credit the discoverer.
To report a security bug, email a description of the flaw and any related information
(e.g. reproduction steps, version) to [security-quorum@consensys.net](mailto:security-quorum@consensys.net).
\ No newline at end of file
dependencies {
acceptanceTestImplementation project(":infrastructure:bls")
acceptanceTestImplementation project(':infrastructure:time')
acceptanceTestImplementation project(':data:serializer')
acceptanceTestImplementation project(':ethereum:execution-types')
acceptanceTestImplementation project(':ethereum:spec')
testFixturesImplementation project(':beacon:pow')
testFixturesImplementation project(':data:provider')
testFixturesImplementation project(':data:serializer')
testFixturesImplementation project(':ethereum:spec')
testFixturesImplementation testFixtures(project(':ethereum:spec'))
testFixturesImplementation project(':ethereum:execution-types')
testFixturesImplementation project(':ethereum:networks')
testFixturesImplementation project(':data:publisher')
testFixturesImplementation testFixtures(project(':data:publisher'))
testFixturesImplementation project(':infrastructure:async')
testFixturesImplementation project(':infrastructure:bls')
testFixturesImplementation project(':infrastructure:metrics')
testFixturesImplementation testFixtures(project(':infrastructure:async'))
testFixturesImplementation project(':teku')
testFixturesImplementation project(':ethereum:json-types')
testFixturesImplementation project(':infrastructure:crypto')
testFixturesImplementation project(':infrastructure:time')
testFixturesImplementation project(':infrastructure:bls-keystore')
testFixturesImplementation 'com.squareup.okhttp3:okhttp'
testFixturesImplementation 'io.libp2p:jvm-libp2p'
testFixturesImplementation 'org.apache.commons:commons-lang3'
testFixturesImplementation 'commons-io:commons-io'
testFixturesImplementation 'io.tmio:tuweni-bytes'
testFixturesImplementation 'org.junit.jupiter:junit-jupiter-api'
testFixturesImplementation 'org.testcontainers:testcontainers'
testFixturesImplementation 'org.testcontainers:junit-jupiter'
testFixturesImplementation 'com.fasterxml.jackson.core:jackson-databind'
testFixturesImplementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-toml'
testFixturesImplementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml'
testFixturesImplementation 'org.web3j:core'
testFixturesImplementation 'org.apache.logging.log4j:log4j-core'
testFixturesImplementation 'com.launchdarkly:okhttp-eventsource'
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.GenesisGenerator.InitialStateData;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.tools.deposits.ValidatorKeystores;
public class AddValidatorsAcceptanceTest extends AcceptanceTestBase {
@Test
void shouldLoadAdditionalValidatorsWithoutRestart() throws Exception {
final String networkName = "swift";
final ValidatorKeystores initialKeystores =
createTekuDepositSender(networkName).generateValidatorKeys(2);
final ValidatorKeystores additionalKeystores =
createTekuDepositSender(networkName).generateValidatorKeys(2);
final InitialStateData genesis =
createGenesisGenerator()
.network(networkName)
.validatorKeys(initialKeystores, additionalKeystores)
.generate();
final TekuBeaconNode node =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode()
.withNetwork(networkName)
.withInitialState(genesis)
.withReadOnlyKeystorePath(initialKeystores)
.build());
node.start();
node.waitForOwnedValidatorCount(2);
node.waitForGenesis();
node.addValidators(additionalKeystores);
node.waitForOwnedValidatorCount(4);
// If the added validators perform their duties properly, the network will finalize.
node.waitForNewFinalization();
// Check loading new validators a second time still works and that they don't have to be active
final ValidatorKeystores evenMoreKeystores =
createTekuDepositSender(networkName).generateValidatorKeys(1);
node.addValidators(evenMoreKeystores);
node.waitForOwnedValidatorCount(5);
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
public class AttestationGossipAcceptanceTest extends AcceptanceTestBase {
@Test
public void shouldFinalizeWithTwoNodes() throws Exception {
final TekuBeaconNode node1 =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode()
.withRealNetwork()
.withInteropValidators(0, 32)
.build());
node1.start();
final UInt64 genesisTime = node1.getGenesisTime();
final TekuBeaconNode node2 =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode()
.withGenesisTime(genesisTime.intValue())
.withRealNetwork()
.withPeers(node1)
.withInteropValidators(32, 32)
.build());
node2.start();
node2.waitForAttestationBeingGossiped(32, 64);
// Check aggregate gossip is also being received by both nodes.
node1.waitForAggregateGossipReceived();
node2.waitForAggregateGossipReceived();
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import com.google.common.io.Resources;
import java.io.IOException;
import java.net.URL;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.infrastructure.time.SystemTimeProvider;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.BesuNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.tools.deposits.ValidatorKeystores;
public class BellatrixMergeTransitionAcceptanceTest extends AcceptanceTestBase {
private static final String NETWORK_NAME = "swift";
private static final URL JWT_FILE = Resources.getResource("auth/ee-jwt-secret.hex");
private final SystemTimeProvider timeProvider = new SystemTimeProvider();
private BesuNode eth1Node;
private TekuBeaconNode tekuNode;
@BeforeEach
void setup() throws Exception {
final int genesisTime = timeProvider.getTimeInSeconds().plus(10).intValue();
eth1Node =
createBesuNode(
config ->
config
.withMiningEnabled(true)
.withMergeSupport()
.withGenesisFile("besu/preMergeGenesis.json")
.withJwtTokenAuthorization(JWT_FILE));
eth1Node.start();
final int totalValidators = 4;
final ValidatorKeystores validatorKeystores =
createTekuDepositSender(NETWORK_NAME).sendValidatorDeposits(eth1Node, totalValidators);
tekuNode =
createTekuBeaconNode(
configureTekuNode(genesisTime)
.withDepositsFrom(eth1Node)
.withExecutionEngine(eth1Node)
.withStartupTargetPeerCount(0)
.withReadOnlyKeystorePath(validatorKeystores)
.withValidatorProposerDefaultFeeRecipient(
"0xFE3B557E8Fb62b89F4916B721be55cEb828dBd73")
.withJwtSecretFile(JWT_FILE)
.build());
tekuNode.start();
}
@Test
void shouldHaveNonDefaultExecutionPayloadAndFinalizeAfterMergeTransition() {
tekuNode.waitForGenesis();
tekuNode.waitForNonDefaultExecutionPayload();
tekuNode.waitForNewFinalization();
}
private TekuNodeConfigBuilder configureTekuNode(final int genesisTime) throws IOException {
return TekuNodeConfigBuilder.createBeaconNode()
.withBellatrixEpoch(UInt64.ONE)
.withTotalTerminalDifficulty(10001)
.withGenesisTime(genesisTime);
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import static org.assertj.core.api.Assertions.assertThat;
import com.google.common.io.Resources;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Locale;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import tech.pegasys.teku.infrastructure.bytes.Bytes20;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.GenesisGenerator.InitialStateData;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.TekuValidatorNode;
import tech.pegasys.teku.test.acceptance.dsl.tools.deposits.ValidatorKeystores;
public class BlockProposalAcceptanceTest extends AcceptanceTestBase {
private static final URL JWT_FILE = Resources.getResource("auth/ee-jwt-secret.hex");
@ParameterizedTest(name = "ssz_encode={0}")
@ValueSource(booleans = {true, false})
void shouldHaveCorrectFeeRecipientAndGraffiti(final boolean useSszBlocks) throws Exception {
final String networkName = "swift";
final ValidatorKeystores validatorKeystores =
createTekuDepositSender(networkName).generateValidatorKeys(8);
final InitialStateData genesis =
createGenesisGenerator()
.network(networkName)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.validatorKeys(validatorKeystores, validatorKeystores)
.generate();
final String defaultFeeRecipient = "0xFE3B557E8Fb62b89F4916B721be55cEb828dBd73";
final String userGraffiti = "My block \uD83D\uDE80"; // 13 bytes
final TekuBeaconNode beaconNode =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode()
.withStubExecutionEngine()
.withJwtSecretFile(JWT_FILE)
.withNetwork(networkName)
.withInitialState(genesis)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withValidatorProposerDefaultFeeRecipient(defaultFeeRecipient)
.build());
final TekuValidatorNode validatorClient =
createValidatorNode(
TekuNodeConfigBuilder.createValidatorClient()
.withReadOnlyKeystorePath(validatorKeystores)
.withValidatorProposerDefaultFeeRecipient(defaultFeeRecipient)
.withInteropModeDisabled()
.withBeaconNodes(beaconNode)
.withBeaconNodeSszBlocksEnabled(useSszBlocks)
.withGraffiti(userGraffiti)
.withNetwork("auto")
.build());
beaconNode.start();
validatorClient.start();
beaconNode.waitForBlockSatisfying(
block -> {
final Bytes20 feeRecipient =
block
.getMessage()
.getBody()
.getOptionalExecutionPayload()
.orElseThrow()
.getFeeRecipient();
assertThat(feeRecipient.toHexString().toLowerCase(Locale.ROOT))
.isEqualTo(defaultFeeRecipient.toLowerCase(Locale.ROOT));
final Bytes32 graffiti = block.getMessage().getBody().getGraffiti();
final String graffitiMessage =
new String(
Arrays.copyOfRange(
graffiti.toArray(), 0, 32 - graffiti.numberOfTrailingZeroBytes()),
StandardCharsets.UTF_8);
// 13 bytes + 1 byte
assertThat(graffitiMessage).startsWith(userGraffiti + " ");
// 18 bytes left, so 12 bytes client footprint: TKxxxxELxxxx. 20 bytes with full commits
// doesn't fit
assertThat(graffitiMessage).contains("TK");
// stub execution endpoint.
assertThat(graffitiMessage).endsWith("SB0000");
});
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import java.io.IOException;
import java.util.List;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.api.response.v1.EventType;
import tech.pegasys.teku.bls.BLSKeyPair;
import tech.pegasys.teku.ethereum.execution.types.Eth1Address;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.datastructures.interop.MockStartValidatorKeyPairFactory;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
public class BlsToExecutionChangeAcceptanceTest extends AcceptanceTestBase {
@Test
void shouldUpdateWithdrawalCredentials() throws Exception {
final int validatorIndex = 0;
// Generating the same keypair that is used for interop validator index = 0
final List<BLSKeyPair> blsKeyPairs =
new MockStartValidatorKeyPairFactory().generateKeyPairs(0, 1);
final BLSKeyPair validatorKeyPair = blsKeyPairs.get(validatorIndex);
final Eth1Address executionAddress =
Eth1Address.fromHexString("0xFE3B557E8Fb62b89F4916B721be55cEb828dBd73");
final UInt64 capellaActivationEpoch = UInt64.ONE;
final TekuBeaconNode primaryNode = createPrimaryNode(executionAddress, capellaActivationEpoch);
primaryNode.start();
primaryNode.waitForNextEpoch();
primaryNode.startEventListener(EventType.bls_to_execution_change);
final TekuBeaconNode lateJoiningNode =
createLateJoiningNode(capellaActivationEpoch, primaryNode, primaryNode.getGenesisTime());
lateJoiningNode.start();
lateJoiningNode.waitUntilInSyncWith(primaryNode);
lateJoiningNode.startEventListener(EventType.bls_to_execution_change);
lateJoiningNode.submitBlsToExecutionChange(validatorIndex, validatorKeyPair, executionAddress);
lateJoiningNode.waitForValidatorWithCredentials(validatorIndex, executionAddress);
primaryNode.waitForBlsToExecutionChangeEventForValidator(0);
lateJoiningNode.waitForBlsToExecutionChangeEventForValidator(0);
}
private TekuBeaconNode createPrimaryNode(
final Eth1Address executionAddress, final UInt64 capellaActivationEpoch) throws IOException {
return createTekuBeaconNode(
beaconNodeWithMilestones(capellaActivationEpoch)
.withStartupTargetPeerCount(0)
.withValidatorProposerDefaultFeeRecipient(executionAddress.toHexString())
.build());
}
private TekuBeaconNode createLateJoiningNode(
final UInt64 capellaActivationEpoch,
final TekuBeaconNode primaryNode,
final UInt64 genesisTime)
throws IOException {
return createTekuBeaconNode(
beaconNodeWithMilestones(capellaActivationEpoch)
.withPeers(primaryNode)
.withGenesisTime(genesisTime.intValue())
.withInteropValidators(0, 0)
.build());
}
private static TekuNodeConfigBuilder beaconNodeWithMilestones(final UInt64 capellaActivationEpoch)
throws IOException {
return TekuNodeConfigBuilder.createBeaconNode()
.withRealNetwork()
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withCapellaEpoch(capellaActivationEpoch)
.withTotalTerminalDifficulty(0)
.withStubExecutionEngine(
"0x14e88057b0b7538a8205cb07726a0de03dd69d9a70e88bcffae15ca3fc6b5215");
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import com.google.common.io.Resources;
import java.net.URL;
import java.util.Map;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.ethereum.execution.types.Eth1Address;
import tech.pegasys.teku.infrastructure.time.SystemTimeProvider;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.BesuDockerVersion;
import tech.pegasys.teku.test.acceptance.dsl.BesuNode;
import tech.pegasys.teku.test.acceptance.dsl.GenesisGenerator.InitialStateData;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfig;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.Web3SignerNode;
import tech.pegasys.teku.test.acceptance.dsl.tools.ValidatorKeysApi;
import tech.pegasys.teku.test.acceptance.dsl.tools.deposits.ValidatorKeystores;
public class CapellaRemoteSignerAcceptanceTest extends AcceptanceTestBase {
private static final String NETWORK_NAME = "swift";
public static final Eth1Address WITHDRAWAL_ADDRESS =
Eth1Address.fromHexString("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
private static final URL JWT_FILE = Resources.getResource("auth/ee-jwt-secret.hex");
@Test
void capellaWithRemoteSigner() throws Exception {
final UInt64 currentTime = new SystemTimeProvider().getTimeInSeconds();
final int genesisTime =
currentTime.intValue() + 30; // genesis needs added time for nodes to startup
final Web3SignerNode web3SignerNode =
createWeb3SignerNode(
config ->
config
.withNetwork(NETWORK_NAME)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withCapellaEpoch(UInt64.ZERO));
web3SignerNode.start();
final ValidatorKeysApi signerApi = web3SignerNode.getValidatorKeysApi();
final BesuNode besuNode = createBesuNode(genesisTime);
besuNode.start();
final ValidatorKeystores validatorKeys =
createTekuDepositSender(NETWORK_NAME).generateValidatorKeys(4, WITHDRAWAL_ADDRESS);
signerApi.addLocalValidatorsAndExpect(validatorKeys, "imported");
signerApi.assertLocalValidatorListing(validatorKeys.getPublicKeys());
final InitialStateData initialStateData =
createGenesisGenerator()
.network(NETWORK_NAME)
.withGenesisTime(genesisTime)
.genesisDelaySeconds(0)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withCapellaEpoch(UInt64.ZERO)
.withTotalTerminalDifficulty(0)
.genesisExecutionPayloadHeaderSource(besuNode::createGenesisExecutionPayload)
.validatorKeys(validatorKeys)
.generate();
final TekuBeaconNode tekuNode =
createTekuBeaconNode(
beaconNode(
genesisTime, besuNode, initialStateData, web3SignerNode.getValidatorRestApiUrl()));
tekuNode.start();
tekuNode.waitForNextEpoch();
tekuNode.waitForNewBlock();
tekuNode.waitForFullSyncCommitteeAggregate();
}
private BesuNode createBesuNode(final int genesisTime) {
final int shanghai =
genesisTime + 2; // 4 slots, 2 seconds each (swift) - activate Prague on first slot
final Map<String, String> genesisOverrides = Map.of("shanghaiTime", String.valueOf(shanghai));
return createBesuNode(
BesuDockerVersion.STABLE,
config ->
config
.withMergeSupport()
.withGenesisFile("besu/mergedGenesis.json")
.withP2pEnabled(true)
.withJwtTokenAuthorization(JWT_FILE),
genesisOverrides);
}
private static TekuNodeConfig beaconNode(
final int genesisTime,
final BesuNode besuNode,
final InitialStateData initialStateData,
final String signerUrl)
throws Exception {
return TekuNodeConfigBuilder.createBeaconNode()
.withInitialState(initialStateData)
.withInteropModeDisabled()
.withNetwork(NETWORK_NAME)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withCapellaEpoch(UInt64.ZERO)
.withTotalTerminalDifficulty(0)
.withGenesisTime(genesisTime)
.withExecutionEngine(besuNode)
.withJwtSecretFile(JWT_FILE)
.withExternalSignerUrl(signerUrl)
.withExternalSignerPublicKeys("external-signer")
.withValidatorProposerDefaultFeeRecipient("0xFE3B557E8Fb62b89F4916B721be55cEb828dBd73")
.withStartupTargetPeerCount(0)
.withRealNetwork()
.build();
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import com.google.common.io.Resources;
import java.net.URL;
import java.util.Map;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.infrastructure.time.SystemTimeProvider;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.SpecMilestone;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.BesuDockerVersion;
import tech.pegasys.teku.test.acceptance.dsl.BesuNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
public class CapellaUpgradeAcceptanceTest extends AcceptanceTestBase {
private final SystemTimeProvider timeProvider = new SystemTimeProvider();
private static final URL JWT_FILE = Resources.getResource("auth/ee-jwt-secret.hex");
@Test
void shouldUpgradeToCapella() throws Exception {
final UInt64 currentTime = timeProvider.getTimeInSeconds();
final int genesisTime = currentTime.plus(60).intValue(); // magic node startup time
final int shanghaiTime = genesisTime + 4 * 2; // 4 slots, 2 seconds each
final Map<String, String> genesisOverrides =
Map.of("shanghaiTime", String.valueOf(shanghaiTime));
BesuNode primaryEL =
createBesuNode(
BesuDockerVersion.STABLE,
config ->
config
.withMergeSupport()
.withGenesisFile("besu/mergedGenesis.json")
.withP2pEnabled(true)
.withJwtTokenAuthorization(JWT_FILE),
genesisOverrides);
primaryEL.start();
TekuBeaconNode primaryNode =
createTekuBeaconNode(
beaconNodeConfigWithForks(genesisTime, primaryEL)
.withStartupTargetPeerCount(0)
.build());
primaryNode.start();
primaryNode.waitForMilestone(SpecMilestone.CAPELLA);
BesuNode secondaryEL =
createBesuNode(
BesuDockerVersion.STABLE,
config ->
config
.withMergeSupport()
.withGenesisFile("besu/mergedGenesis.json")
.withP2pEnabled(true)
.withJwtTokenAuthorization(JWT_FILE),
genesisOverrides);
secondaryEL.start();
secondaryEL.addPeer(primaryEL);
final int primaryNodeGenesisTime = primaryNode.getGenesisTime().intValue();
TekuBeaconNode lateJoiningNode =
createTekuBeaconNode(
beaconNodeConfigWithForks(primaryNodeGenesisTime, secondaryEL)
.withPeers(primaryNode)
.withInteropValidators(0, 0)
.build());
lateJoiningNode.start();
lateJoiningNode.waitUntilInSyncWith(primaryNode);
primaryNode.waitForNewBlock();
}
private static TekuNodeConfigBuilder beaconNodeConfigWithForks(
final int genesisTime, final BesuNode besuNode) throws Exception {
return TekuNodeConfigBuilder.createBeaconNode()
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withCapellaEpoch(UInt64.ONE)
.withTotalTerminalDifficulty(0)
.withGenesisTime(genesisTime)
.withExecutionEngine(besuNode)
.withJwtSecretFile(JWT_FILE)
.withRealNetwork();
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import com.google.common.io.Resources;
import java.net.URL;
import java.util.Map;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.ethereum.execution.types.Eth1Address;
import tech.pegasys.teku.infrastructure.time.SystemTimeProvider;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.BesuDockerVersion;
import tech.pegasys.teku.test.acceptance.dsl.BesuNode;
import tech.pegasys.teku.test.acceptance.dsl.GenesisGenerator.InitialStateData;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfig;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.Web3SignerNode;
import tech.pegasys.teku.test.acceptance.dsl.tools.ValidatorKeysApi;
import tech.pegasys.teku.test.acceptance.dsl.tools.deposits.ValidatorKeystores;
public class DenebRemoteSignerAcceptanceTest extends AcceptanceTestBase {
private static final String NETWORK_NAME = "swift";
public static final Eth1Address WITHDRAWAL_ADDRESS =
Eth1Address.fromHexString("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
private static final URL JWT_FILE = Resources.getResource("auth/ee-jwt-secret.hex");
@Test
void denebWithRemoteSigner() throws Exception {
final UInt64 currentTime = new SystemTimeProvider().getTimeInSeconds();
final int genesisTime =
currentTime.intValue() + 30; // genesis in 30 seconds to give node time to start
final Web3SignerNode web3SignerNode =
createWeb3SignerNode(
config ->
config
.withNetwork(NETWORK_NAME)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withCapellaEpoch(UInt64.ZERO)
.withDenebEpoch(UInt64.ZERO)
.withTrustedSetupFromClasspath("mainnet-trusted-setup.txt"));
web3SignerNode.start();
final ValidatorKeysApi signerApi = web3SignerNode.getValidatorKeysApi();
final BesuNode besuNode = createBesuNode(genesisTime);
besuNode.start();
final ValidatorKeystores validatorKeys =
createTekuDepositSender(NETWORK_NAME).generateValidatorKeys(4, WITHDRAWAL_ADDRESS);
signerApi.addLocalValidatorsAndExpect(validatorKeys, "imported");
signerApi.assertLocalValidatorListing(validatorKeys.getPublicKeys());
final InitialStateData initialStateData =
createGenesisGenerator()
.network(NETWORK_NAME)
.withGenesisTime(genesisTime)
.genesisDelaySeconds(0)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withCapellaEpoch(UInt64.ZERO)
.withDenebEpoch(UInt64.ZERO)
.withTotalTerminalDifficulty(0)
.genesisExecutionPayloadHeaderSource(besuNode::createGenesisExecutionPayload)
.validatorKeys(validatorKeys)
.generate();
final TekuBeaconNode tekuNode =
createTekuBeaconNode(
beaconNode(
genesisTime, besuNode, initialStateData, web3SignerNode.getValidatorRestApiUrl()));
tekuNode.start();
tekuNode.waitForNextEpoch();
tekuNode.waitForNewBlock();
tekuNode.waitForFullSyncCommitteeAggregate();
}
private BesuNode createBesuNode(final int genesisTime) {
final Map<String, String> genesisOverrides = Map.of("cancunTime", String.valueOf(genesisTime));
return createBesuNode(
BesuDockerVersion.STABLE,
config ->
config
.withMergeSupport()
.withGenesisFile("besu/mergedGenesis.json")
.withP2pEnabled(true)
.withJwtTokenAuthorization(JWT_FILE),
genesisOverrides);
}
private static TekuNodeConfig beaconNode(
final int genesisTime,
final BesuNode besuNode,
final InitialStateData initialStateData,
final String signerUrl)
throws Exception {
return TekuNodeConfigBuilder.createBeaconNode()
.withInitialState(initialStateData)
.withInteropModeDisabled()
.withNetwork(NETWORK_NAME)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withCapellaEpoch(UInt64.ZERO)
.withDenebEpoch(UInt64.ZERO)
.withTotalTerminalDifficulty(0)
.withGenesisTime(genesisTime)
.withExecutionEngine(besuNode)
.withJwtSecretFile(JWT_FILE)
.withExternalSignerUrl(signerUrl)
.withExternalSignerPublicKeys("external-signer")
.withValidatorProposerDefaultFeeRecipient("0xFE3B557E8Fb62b89F4916B721be55cEb828dBd73")
.withStartupTargetPeerCount(0)
.withRealNetwork()
.build();
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import com.google.common.io.Resources;
import java.net.URL;
import java.util.Map;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.infrastructure.time.SystemTimeProvider;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.SpecMilestone;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.BesuDockerVersion;
import tech.pegasys.teku.test.acceptance.dsl.BesuNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
public class DenebUpgradeAcceptanceTest extends AcceptanceTestBase {
private final SystemTimeProvider timeProvider = new SystemTimeProvider();
private static final URL JWT_FILE = Resources.getResource("auth/ee-jwt-secret.hex");
@Test
void shouldUpgradeToDeneb() throws Exception {
final UInt64 currentTime = timeProvider.getTimeInSeconds();
final int genesisTime = currentTime.plus(60).intValue(); // magic node startup time
final int epochDuration = 4 * 2; // 4 slots, 2 seconds each for swift
final int shanghaiTime = genesisTime + epochDuration;
final Map<String, String> genesisOverrides =
Map.of(
"shanghaiTime",
String.valueOf(shanghaiTime),
"cancunTime",
String.valueOf(shanghaiTime + epochDuration));
BesuNode primaryEL =
createBesuNode(
BesuDockerVersion.STABLE,
config ->
config
.withMergeSupport()
.withGenesisFile("besu/mergedGenesis.json")
.withP2pEnabled(true)
.withJwtTokenAuthorization(JWT_FILE),
genesisOverrides);
primaryEL.start();
TekuBeaconNode primaryNode =
createTekuBeaconNode(
beaconNodeWithTrustedSetup(genesisTime, primaryEL)
.withStartupTargetPeerCount(0)
.build());
primaryNode.start();
primaryNode.waitForMilestone(SpecMilestone.DENEB);
BesuNode secondaryEL =
createBesuNode(
BesuDockerVersion.STABLE,
config ->
config
.withMergeSupport()
.withGenesisFile("besu/mergedGenesis.json")
.withP2pEnabled(true)
.withJwtTokenAuthorization(JWT_FILE),
genesisOverrides);
secondaryEL.start();
secondaryEL.addPeer(primaryEL);
final int primaryNodeGenesisTime = primaryNode.getGenesisTime().intValue();
TekuBeaconNode lateJoiningNode =
createTekuBeaconNode(
beaconNodeWithTrustedSetup(primaryNodeGenesisTime, secondaryEL)
.withPeers(primaryNode)
.withInteropValidators(0, 0)
.build());
lateJoiningNode.start();
lateJoiningNode.waitUntilInSyncWith(primaryNode);
primaryNode.waitForNewBlock();
}
private static TekuNodeConfigBuilder beaconNodeWithTrustedSetup(
final int genesisTime, final BesuNode besuNode) throws Exception {
return TekuNodeConfigBuilder.createBeaconNode()
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withCapellaEpoch(UInt64.ONE)
.withGenesisTime(genesisTime)
.withExecutionEngine(besuNode)
.withRealNetwork()
.withJwtSecretFile(JWT_FILE)
.withDenebEpoch(UInt64.valueOf(2))
.withTotalTerminalDifficulty(0);
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import com.google.common.io.Resources;
import java.io.IOException;
import java.net.URL;
import java.util.Collections;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.BesuNode;
import tech.pegasys.teku.test.acceptance.dsl.GenesisGenerator;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.TekuValidatorNode;
import tech.pegasys.teku.test.acceptance.dsl.tools.ValidatorKeysApi;
import tech.pegasys.teku.test.acceptance.dsl.tools.deposits.ValidatorKeystores;
public class DoppelgangerDetectorAcceptanceTest extends AcceptanceTestBase {
private static final URL JWT_FILE = Resources.getResource("auth/ee-jwt-secret.hex");
private final String networkName = "less-swift";
private BesuNode eth1Node;
@BeforeEach
void setUp() {
eth1Node =
createBesuNode(
config ->
config
.withMiningEnabled(true)
.withMergeSupport()
.withGenesisFile("besu/preMergeGenesis.json")
.withJwtTokenAuthorization(JWT_FILE));
}
@Test
void shouldDetectDoppelgangersViaKeyManagerAPI() throws Exception {
eth1Node.start();
final ValidatorKeystores validatorKeystores =
createTekuDepositSender(networkName).sendValidatorDeposits(eth1Node, 2);
final GenesisGenerator.InitialStateData genesis =
createGenesisGenerator().network(networkName).validatorKeys(validatorKeystores).generate();
final String defaultFeeRecipient = "0xFE3B557E8Fb62b89F4916B721be55cEb828dBd73";
final TekuBeaconNode beaconNode =
createTekuBeaconNode(
configureBeaconNode(eth1Node)
.withValidatorLivenessTracking()
.withJwtSecretFile(JWT_FILE)
.withBellatrixEpoch(UInt64.ONE)
.withTotalTerminalDifficulty(10001)
.withValidatorProposerDefaultFeeRecipient(defaultFeeRecipient)
.withInitialState(genesis)
.build());
final TekuValidatorNode firstValidatorClient =
createValidatorNode(
TekuNodeConfigBuilder.createValidatorClient()
.withValidatorApiEnabled()
.withValidatorProposerDefaultFeeRecipient(defaultFeeRecipient)
.withInteropModeDisabled()
.withBeaconNodes(beaconNode)
.withNetwork("auto")
.withDoppelgangerDetectionEnabled()
.build());
final ValidatorKeysApi api = firstValidatorClient.getValidatorKeysApi();
beaconNode.start();
firstValidatorClient.start();
api.assertLocalValidatorListing(Collections.emptyList());
api.addLocalValidatorsAndExpect(validatorKeystores, "imported");
firstValidatorClient.waitForLogMessageContaining("No validators doppelganger detected");
final TekuValidatorNode secondValidatorClient =
createValidatorNode(
TekuNodeConfigBuilder.createValidatorClient()
.withValidatorApiEnabled()
.withValidatorProposerDefaultFeeRecipient(defaultFeeRecipient)
.withInteropModeDisabled()
.withBeaconNodes(beaconNode)
.withNetwork("auto")
.withDoppelgangerDetectionEnabled()
.build());
final ValidatorKeysApi secondApi = secondValidatorClient.getValidatorKeysApi();
secondValidatorClient.start();
secondApi.assertLocalValidatorListing(Collections.emptyList());
secondApi.addLocalValidatorsAndExpect(validatorKeystores, "imported");
secondValidatorClient.waitForLogMessageContaining("Validator doppelganger detected...");
secondValidatorClient.waitForLogMessageContaining("Doppelganger detection check finished");
secondValidatorClient.waitForLogMessageContaining("Detected 2 validators doppelganger");
secondValidatorClient.waitForLogMessageContaining(
"Detected 2 active validators doppelganger. The following keys have been ignored");
firstValidatorClient.stop();
secondValidatorClient.stop();
beaconNode.stop();
eth1Node.stop();
}
@Test
void shouldDetectDoppelgangersAtStartUp() throws Exception {
eth1Node.start();
final ValidatorKeystores keyStore =
createTekuDepositSender(networkName).generateValidatorKeys(2);
final GenesisGenerator.InitialStateData genesis =
createGenesisGenerator().network(networkName).validatorKeys(keyStore).generate();
final TekuBeaconNode firstNode =
createTekuBeaconNode(
configureBeaconNode(eth1Node)
.withInitialState(genesis)
.withReadOnlyKeystorePath(keyStore)
.build());
firstNode.start();
firstNode.waitForOwnedValidatorCount(2);
firstNode.waitForGenesis();
firstNode.waitForEpochAtOrAbove(2);
final TekuBeaconNode secondNode =
createTekuBeaconNode(
configureBeaconNode(eth1Node)
.withPeers(firstNode)
.withInitialState(genesis)
.withReadOnlyKeystorePath(keyStore)
.withDoppelgangerDetectionEnabled()
.build());
secondNode.start();
secondNode.waitForLogMessageContaining("Validator doppelganger detected...");
secondNode.waitForLogMessageContaining("Detected 2 validators doppelganger");
secondNode.waitForLogMessageContaining(
"Doppelganger detection check finished. Stopping doppelganger detection");
secondNode.waitForLogMessageContaining("Detected 2 validators doppelganger");
secondNode.waitForLogMessageContaining("Shutting down...");
secondNode.stop();
eth1Node.stop();
}
private TekuNodeConfigBuilder configureBeaconNode(final BesuNode eth1Node) throws IOException {
return TekuNodeConfigBuilder.createBeaconNode()
.withDepositsFrom(eth1Node)
.withRealNetwork()
.withExecutionEngine(eth1Node)
.withNetwork(networkName);
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import com.google.common.io.Resources;
import java.net.URL;
import java.util.Map;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.ethereum.execution.types.Eth1Address;
import tech.pegasys.teku.infrastructure.time.SystemTimeProvider;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.SpecMilestone;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.BesuDockerVersion;
import tech.pegasys.teku.test.acceptance.dsl.BesuNode;
import tech.pegasys.teku.test.acceptance.dsl.GenesisGenerator.InitialStateData;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfig;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.tools.deposits.ValidatorKeystores;
@Disabled("Won't work until we update Engine API for Electra + EL changes")
public class ElectraUpgradeAcceptanceTest extends AcceptanceTestBase {
private static final String NETWORK_NAME = "swift";
public static final Eth1Address WITHDRAWAL_ADDRESS =
Eth1Address.fromHexString("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
private static final URL JWT_FILE = Resources.getResource("auth/ee-jwt-secret.hex");
@Test
void upgradeFromDeneb() throws Exception {
final UInt64 currentTime = new SystemTimeProvider().getTimeInSeconds();
final int genesisTime =
currentTime.intValue() + 30; // genesis in 30 seconds to give node time to start
final BesuNode besuNode = createBesuNode(genesisTime);
besuNode.start();
final ValidatorKeystores validatorKeys =
createTekuDepositSender(NETWORK_NAME).generateValidatorKeys(4, WITHDRAWAL_ADDRESS);
final InitialStateData initialStateData =
createGenesisGenerator()
.network(NETWORK_NAME)
.withGenesisTime(genesisTime)
.genesisDelaySeconds(0)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withCapellaEpoch(UInt64.ZERO)
.withDenebEpoch(UInt64.ZERO)
.withElectraEpoch(UInt64.ONE)
.withTotalTerminalDifficulty(0)
.genesisExecutionPayloadHeaderSource(besuNode::createGenesisExecutionPayload)
.validatorKeys(validatorKeys)
.generate();
final TekuBeaconNode tekuNode =
createTekuBeaconNode(beaconNode(genesisTime, besuNode, initialStateData, validatorKeys));
tekuNode.start();
tekuNode.waitForMilestone(SpecMilestone.ELECTRA);
tekuNode.waitForNewBlock();
}
private BesuNode createBesuNode(final int genesisTime) {
final int pragueTime =
genesisTime + 4 * 2; // 4 slots, 2 seconds each (swift) - activate Prague on first slot
final Map<String, String> genesisOverrides = Map.of("pragueTime", String.valueOf(pragueTime));
return createBesuNode(
BesuDockerVersion.STABLE,
config ->
config
.withMergeSupport()
.withGenesisFile("besu/pragueGenesis.json")
.withP2pEnabled(true)
.withJwtTokenAuthorization(JWT_FILE),
genesisOverrides);
}
private static TekuNodeConfig beaconNode(
final int genesisTime,
final BesuNode besuNode,
final InitialStateData initialStateData,
final ValidatorKeystores validatorKeys)
throws Exception {
return TekuNodeConfigBuilder.createBeaconNode()
.withInitialState(initialStateData)
.withNetwork(NETWORK_NAME)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withCapellaEpoch(UInt64.ZERO)
.withDenebEpoch(UInt64.ZERO)
.withElectraEpoch(UInt64.ONE)
.withTotalTerminalDifficulty(0)
.withGenesisTime(genesisTime)
.withExecutionEngine(besuNode)
.withJwtSecretFile(JWT_FILE)
.withReadOnlyKeystorePath(validatorKeys)
.withValidatorProposerDefaultFeeRecipient("0xFE3B557E8Fb62b89F4916B721be55cEb828dBd73")
.withStartupTargetPeerCount(0)
.withRealNetwork()
.build();
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import com.google.common.io.Resources;
import java.net.URL;
import java.util.Map;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.bls.BLSPublicKey;
import tech.pegasys.teku.ethereum.execution.types.Eth1Address;
import tech.pegasys.teku.infrastructure.time.SystemTimeProvider;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.BesuDockerVersion;
import tech.pegasys.teku.test.acceptance.dsl.BesuNode;
import tech.pegasys.teku.test.acceptance.dsl.GenesisGenerator.InitialStateData;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfig;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.tools.deposits.ValidatorKeys;
import tech.pegasys.teku.test.acceptance.dsl.tools.deposits.ValidatorKeystores;
public class ExecutionLayerTriggeredExitAcceptanceTest extends AcceptanceTestBase {
private static final String NETWORK_NAME = "swift";
private static final URL JWT_FILE = Resources.getResource("auth/ee-jwt-secret.hex");
@Test
@Disabled("Won't work until we update Engine API for Electra")
void triggerValidatorExitWithFullWithdrawal() throws Exception {
final UInt64 currentTime = new SystemTimeProvider().getTimeInSeconds();
final int genesisTime =
currentTime.intValue() + 30; // genesis in 30 seconds to give node time to start
final BesuNode besuNode = createBesuNode(genesisTime);
besuNode.start();
final String eth1Address =
besuNode.getRichBenefactorAddress(); // used as withdrawal_credentials
final String eth1PrivateKey =
besuNode.getRichBenefactorKey(); // key for withdrawal_credentials account
final ValidatorKeystores validatorKeys =
createTekuDepositSender(NETWORK_NAME)
.generateValidatorKeys(4, Eth1Address.fromHexString(eth1Address));
final InitialStateData initialStateData =
createGenesisGenerator()
.network(NETWORK_NAME)
.withGenesisTime(genesisTime)
.genesisDelaySeconds(0)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withCapellaEpoch(UInt64.ZERO)
.withDenebEpoch(UInt64.ZERO)
.withElectraEpoch(UInt64.ZERO)
.withTotalTerminalDifficulty(0)
.genesisExecutionPayloadHeaderSource(besuNode::createGenesisExecutionPayload)
.validatorKeys(validatorKeys)
.generate();
final TekuBeaconNode tekuNode =
createTekuBeaconNode(beaconNode(genesisTime, besuNode, initialStateData, validatorKeys));
tekuNode.start();
// Ensures validator is active long enough to exit
tekuNode.waitForNewFinalization();
final ValidatorKeys validator = validatorKeys.getValidatorKeys().get(0);
final BLSPublicKey validatorPubkey = validator.getValidatorKey().getPublicKey();
besuNode.createWithdrawalRequest(eth1PrivateKey, validatorPubkey, UInt64.ZERO);
// Wait for validator exit confirmation
tekuNode.waitForLogMessageContaining(
"has changed status from active_ongoing to active_exiting");
}
private BesuNode createBesuNode(final int genesisTime) {
final Map<String, String> genesisOverrides = Map.of("pragueTime", String.valueOf(genesisTime));
return createBesuNode(
BesuDockerVersion.STABLE,
config ->
config
.withMergeSupport()
.withGenesisFile("besu/pragueGenesis.json")
.withP2pEnabled(true)
.withJwtTokenAuthorization(JWT_FILE),
genesisOverrides);
}
private static TekuNodeConfig beaconNode(
final int genesisTime,
final BesuNode besuNode,
final InitialStateData initialStateData,
final ValidatorKeystores validatorKeys)
throws Exception {
return TekuNodeConfigBuilder.createBeaconNode()
.withInitialState(initialStateData)
.withNetwork(NETWORK_NAME)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withCapellaEpoch(UInt64.ZERO)
.withDenebEpoch(UInt64.ZERO)
.withElectraEpoch(UInt64.ZERO)
.withTotalTerminalDifficulty(0)
.withGenesisTime(genesisTime)
.withExecutionEngine(besuNode)
.withJwtSecretFile(JWT_FILE)
.withReadOnlyKeystorePath(validatorKeys)
.withValidatorProposerDefaultFeeRecipient("0xFE3B557E8Fb62b89F4916B721be55cEb828dBd73")
.withStartupTargetPeerCount(0)
.withRealNetwork()
.build();
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.ExternalMetricNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
public class ExternalMetricPublisherAcceptanceTest extends AcceptanceTestBase {
private static final int ACTIVE_VALIDATOR_COUNT = 8;
private static final int TOTAL_VALIDATOR_COUNT = 64;
@Test
void shouldPublishDataFromPrometheus() throws Throwable {
ExternalMetricNode externalMetricNode = createExternalMetricNode();
externalMetricNode.start();
final TekuBeaconNode tekuNode =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode()
.withExternalMetricsClient(externalMetricNode, 1)
.withInteropNumberOfValidators(TOTAL_VALIDATOR_COUNT)
.withInteropValidators(0, ACTIVE_VALIDATOR_COUNT)
.build());
tekuNode.start();
externalMetricNode.waitForBeaconNodeMetricPublication();
externalMetricNode.waitForValidatorMetricPublication(ACTIVE_VALIDATOR_COUNT);
externalMetricNode.waitForSystemMetricPublication();
tekuNode.stop();
externalMetricNode.stop();
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import static tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder.DEFAULT_NETWORK_NAME;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.BesuNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuDepositSender;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.tools.deposits.ValidatorKeystores;
public class GenesisStateAcceptanceTest extends AcceptanceTestBase {
@Test
public void shouldCreateTheSameGenesisState() throws Exception {
final BesuNode eth1Node = createBesuNode(config -> config.withMiningEnabled(true));
eth1Node.start();
createTekuDepositSender(DEFAULT_NETWORK_NAME).sendValidatorDeposits(eth1Node, 4);
final TekuBeaconNode firstTeku =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode().withDepositsFrom(eth1Node).build());
firstTeku.start();
firstTeku.waitForGenesis();
final TekuBeaconNode lateJoinTeku =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode().withDepositsFrom(eth1Node).build());
lateJoinTeku.start();
lateJoinTeku.waitForGenesis();
// Even though the nodes aren't connected to each other they should generate the same genesis
// state because they processed the same deposits from the same ETH1 chain.
lateJoinTeku.waitUntilInSyncWith(firstTeku);
}
@Test
public void shouldCreateGenesisFromPartialDeposits() throws Exception {
final BesuNode eth1Node = createBesuNode(config -> config.withMiningEnabled(true));
eth1Node.start();
int numberOfValidators = 4;
final TekuDepositSender depositSender = createTekuDepositSender(DEFAULT_NETWORK_NAME);
final ValidatorKeystores validatorKeys =
depositSender.generateValidatorKeys(numberOfValidators);
depositSender.sendValidatorDeposits(
eth1Node, validatorKeys, depositSender.getMinDepositAmount());
depositSender.sendValidatorDeposits(
eth1Node,
validatorKeys,
depositSender.getMaxEffectiveBalance().minus(depositSender.getMinDepositAmount()));
final TekuBeaconNode teku =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode().withDepositsFrom(eth1Node).build());
teku.start();
teku.waitForGenesis();
teku.waitForValidators(numberOfValidators);
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import com.google.common.io.Resources;
import java.net.URL;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.bls.BLSPublicKey;
import tech.pegasys.teku.ethereum.execution.types.Eth1Address;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.GenesisGenerator.InitialStateData;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.TekuValidatorNode;
import tech.pegasys.teku.test.acceptance.dsl.tools.ValidatorKeysApi;
import tech.pegasys.teku.test.acceptance.dsl.tools.deposits.ValidatorKeystores;
public class LocalValidatorKeysAcceptanceTest extends AcceptanceTestBase {
private static final URL JWT_FILE = Resources.getResource("auth/ee-jwt-secret.hex");
@Test
void shouldMaintainValidatorsInMutableClient() throws Exception {
final String networkName = "swift";
final ValidatorKeystores validatorKeystores =
createTekuDepositSender(networkName).generateValidatorKeys(8);
final ValidatorKeystores extraKeys =
createTekuDepositSender(networkName).generateValidatorKeys(1);
final InitialStateData genesis =
createGenesisGenerator()
.network(networkName)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.validatorKeys(validatorKeystores, extraKeys)
.generate();
final String defaultFeeRecipient = "0xFE3B557E8Fb62b89F4916B721be55cEb828dBd73";
final TekuBeaconNode beaconNode =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode()
.withStubExecutionEngine()
.withJwtSecretFile(JWT_FILE)
.withNetwork(networkName)
.withInitialState(genesis)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withValidatorProposerDefaultFeeRecipient(defaultFeeRecipient)
.build());
final TekuValidatorNode validatorClient =
createValidatorNode(
TekuNodeConfigBuilder.createValidatorClient()
.withValidatorApiEnabled()
.withValidatorProposerDefaultFeeRecipient(defaultFeeRecipient)
.withInteropModeDisabled()
.withBeaconNodes(beaconNode)
.withNetwork("auto")
.build());
final ValidatorKeysApi api = validatorClient.getValidatorKeysApi();
beaconNode.start();
validatorClient.start();
api.assertLocalValidatorListing(Collections.emptyList());
api.addLocalValidatorsAndExpect(validatorKeystores, "imported");
validatorClient.waitForLogMessageContaining("Added validator");
validatorClient.waitForLogMessageContaining("Published block");
api.assertLocalValidatorListing(validatorKeystores.getPublicKeys());
api.assertValidatorGasLimit(
validatorKeystores.getPublicKeys().get(1), UInt64.valueOf(30000000));
// generate voluntary exit
api.generateVoluntaryExitAndCheckValidatorIndex(validatorKeystores.getPublicKeys().get(1), 1);
api.assertValidatorFeeRecipient(validatorKeystores.getPublicKeys().get(1), defaultFeeRecipient);
final String expectedFeeRecipient = "0xAbcF8e0d4e9587369b2301D0790347320302cc09";
api.addFeeRecipient(
validatorKeystores.getPublicKeys().get(0), Eth1Address.fromHexString(expectedFeeRecipient));
api.assertValidatorFeeRecipient(
validatorKeystores.getPublicKeys().get(0), expectedFeeRecipient);
final UInt64 expectedGasLimit = UInt64.valueOf(1234567);
api.addGasLimit(validatorKeystores.getPublicKeys().get(0), expectedGasLimit);
api.assertValidatorGasLimit(validatorKeystores.getPublicKeys().get(0), expectedGasLimit);
// second add attempt would be duplicates
api.addLocalValidatorsAndExpect(validatorKeystores, "duplicate");
// a random key won't be found, remove should give not_found
api.removeLocalValidatorAndCheckStatus(extraKeys.getPublicKeys().get(0), "not_found");
// Wait for a full epoch to pass so that all validators have attested
// This ensures they have all generated slashing protection data
beaconNode.waitForNextEpoch();
beaconNode.waitForNextEpoch();
// remove a validator
final BLSPublicKey removedPubkey = validatorKeystores.getPublicKeys().get(0);
api.removeLocalValidatorAndCheckStatus(removedPubkey, "deleted");
// should only be 7 validators left
validatorClient.waitForLogMessageContaining("Removed validator");
validatorClient.waitForLogMessageContaining("Published block");
api.assertLocalValidatorListing(validatorKeystores.getPublicKeys().subList(1, 7));
// remove the same validator again
api.removeLocalValidatorAndCheckStatus(removedPubkey, "not_active");
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import com.google.common.io.Resources;
import java.net.URL;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.ethereum.execution.types.Eth1Address;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.BesuNode;
import tech.pegasys.teku.test.acceptance.dsl.GenesisGenerator.InitialStateData;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.tools.deposits.ValidatorKeystores;
public class MergedGenesisAcceptanceTest extends AcceptanceTestBase {
private static final String NETWORK_NAME = "swift";
private static final URL JWT_FILE = Resources.getResource("auth/ee-jwt-secret.hex");
public static final Eth1Address WITHDRAWAL_ADDRESS =
Eth1Address.fromHexString("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee");
private BesuNode eth1Node;
private TekuBeaconNode tekuNode;
@BeforeEach
void setup() throws Exception {
eth1Node =
createBesuNode(
config ->
config
.withMergeSupport()
.withGenesisFile("besu/mergedGenesis.json")
.withJwtTokenAuthorization(JWT_FILE));
eth1Node.start();
final ValidatorKeystores validatorKeys =
createTekuDepositSender(NETWORK_NAME).generateValidatorKeys(4, WITHDRAWAL_ADDRESS);
final InitialStateData initialStateData =
createGenesisGenerator()
.network(NETWORK_NAME)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withTotalTerminalDifficulty(0)
.genesisExecutionPayloadHeaderSource(eth1Node::createGenesisExecutionPayload)
.validatorKeys(validatorKeys)
.generate();
tekuNode =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode()
.withNetwork(NETWORK_NAME)
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withTotalTerminalDifficulty(0)
.withInitialState(initialStateData)
.withStartupTargetPeerCount(0)
.withReadOnlyKeystorePath(validatorKeys)
.withValidatorProposerDefaultFeeRecipient(
"0xFE3B557E8Fb62b89F4916B721be55cEb828dBd73")
.withExecutionEngine(eth1Node)
.withJwtSecretFile(JWT_FILE)
.build());
tekuNode.start();
}
@Test
void shouldHaveNonDefaultExecutionPayloadAndFinalizeAfterMergeTransition() {
tekuNode.waitForGenesisWithNonDefaultExecutionPayload();
tekuNode.waitForNewFinalization();
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import static org.assertj.core.api.Fail.fail;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.EnumSource;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.spec.SpecMilestone;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfig;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
public class MergedGenesisInteropModeAcceptanceTest extends AcceptanceTestBase {
@ParameterizedTest
@EnumSource(SpecMilestone.class)
public void startFromMergedStatePerMilestoneUsingTerminalBlockHash(
final SpecMilestone specMilestone) throws Exception {
if (specMilestone.isGreaterThanOrEqualTo(SpecMilestone.CAPELLA)) {
final TekuNodeConfig config =
createTekuNodeBuilderForMilestone(specMilestone)
.withTerminalBlockHash(
"0x00000000000000000000000000000000000000000000000000000000000000aa")
.withStubExecutionEngine()
.build();
final TekuBeaconNode node = createTekuBeaconNode(config);
node.start();
node.waitForNonDefaultExecutionPayload();
node.waitForNewBlock();
}
}
@ParameterizedTest
@EnumSource(SpecMilestone.class)
public void startFromMergedStatePerMilestoneUsingTotalDifficultySimulation(
final SpecMilestone specMilestone) throws Exception {
if (specMilestone.isGreaterThanOrEqualTo(SpecMilestone.CAPELLA)) {
final TekuNodeConfig config =
createTekuNodeBuilderForMilestone(specMilestone).withStubExecutionEngine().build();
final TekuBeaconNode node = createTekuBeaconNode(config);
node.start();
node.waitForNonDefaultExecutionPayload();
node.waitForNewBlock();
}
}
private static TekuNodeConfigBuilder createTekuNodeBuilderForMilestone(
final SpecMilestone specMilestone) throws Exception {
final TekuNodeConfigBuilder tekuNodeConfigBuilder =
TekuNodeConfigBuilder.createBeaconNode()
.withRealNetwork()
.withNetwork("minimal")
.withAltairEpoch(UInt64.ZERO)
.withBellatrixEpoch(UInt64.ZERO)
.withTotalTerminalDifficulty(0)
.withStartupTargetPeerCount(0)
.withInteropNumberOfValidators(64)
.withValidatorProposerDefaultFeeRecipient("0xFE3B557E8Fb62b89F4916B721be55cEb828dBd73");
switch (specMilestone) {
// We do not need to consider PHASE0, ALTAIR or BELLATRIX as they are all pre-Merge
// milestones
case CAPELLA:
tekuNodeConfigBuilder.withCapellaEpoch(UInt64.ZERO);
break;
case DENEB:
tekuNodeConfigBuilder.withCapellaEpoch(UInt64.ZERO);
tekuNodeConfigBuilder.withDenebEpoch(UInt64.ZERO);
break;
case ELECTRA:
tekuNodeConfigBuilder.withCapellaEpoch(UInt64.ZERO);
tekuNodeConfigBuilder.withDenebEpoch(UInt64.ZERO);
tekuNodeConfigBuilder.withElectraEpoch(UInt64.ZERO);
break;
default:
// Test will reach this whenever a new milestone is added and isn't mapped on the switch.
// This is a way to force us to always remember to validate that a new milestone can start
// from a merged
// state.
fail("Milestone %s not used on merged genesis interop test", specMilestone);
}
return tekuNodeConfigBuilder;
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import com.google.common.io.Resources;
import java.net.URL;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.infrastructure.time.SystemTimeProvider;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.BesuNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
public class OptimisticSyncPostMergeAcceptanceTest extends AcceptanceTestBase {
private static final URL JWT_FILE = Resources.getResource("auth/ee-jwt-secret.hex");
private static final int VALIDATORS = 64;
private final SystemTimeProvider timeProvider = new SystemTimeProvider();
private BesuNode executionNode1;
private BesuNode executionNode2;
private TekuBeaconNode tekuNode2;
@BeforeEach
void setup() throws Exception {
final int genesisTime = timeProvider.getTimeInSeconds().plus(10).intValue();
executionNode1 =
createBesuNode(
config ->
config
.withMiningEnabled(true)
.withMergeSupport()
.withP2pEnabled(true)
.withGenesisFile("besu/preMergeGenesis.json")
.withJwtTokenAuthorization(JWT_FILE));
executionNode1.start();
executionNode2 =
createBesuNode(
config ->
config
.withMergeSupport()
.withP2pEnabled(true)
.withGenesisFile("besu/preMergeGenesis.json")
.withJwtTokenAuthorization(JWT_FILE));
executionNode2.start();
executionNode2.addPeer(executionNode1);
TekuBeaconNode tekuNode1 =
createTekuBeaconNode(
createBeaconNode(executionNode1, genesisTime)
.withInteropValidators(0, VALIDATORS)
.build());
tekuNode1.start();
tekuNode2 =
createTekuBeaconNode(
createBeaconNode(executionNode2, genesisTime)
.withInteropValidators(0, 0)
.withPeers(tekuNode1)
.build());
tekuNode2.start();
}
@Test
void shouldSwitchToOptimisticSyncAfterMergeWhenExecutionEngineIsSyncing() throws Exception {
// Reset execution client's DB after the merge and leave it without any chance to sync
tekuNode2.waitForNonDefaultExecutionPayload();
executionNode2.restartWithEmptyDatabase();
tekuNode2.waitForOptimisticBlock();
// Now make execution node sync and clarify switch from optimistic sync back to the normal
executionNode2.addPeer(executionNode1);
tekuNode2.waitForNonOptimisticBlock();
}
private TekuNodeConfigBuilder createBeaconNode(
final BesuNode executionEngine, final int genesisTime) throws Exception {
return TekuNodeConfigBuilder.createBeaconNode()
.withBellatrixEpoch(UInt64.ZERO)
.withTotalTerminalDifficulty(10001)
.withGenesisTime(genesisTime)
.withRealNetwork()
.withStartupTargetPeerCount(0)
.withExecutionEngine(executionEngine)
.withJwtSecretFile(JWT_FILE);
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import com.google.common.io.Resources;
import java.net.URL;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.infrastructure.time.SystemTimeProvider;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.BesuNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
public class OptimisticSyncSafeSlotsAcceptanceTest extends AcceptanceTestBase {
private static final String NETWORK_NAME = "swift";
private static final Integer SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY = 8;
private static final URL JWT_FILE = Resources.getResource("auth/ee-jwt-secret.hex");
private static final int VALIDATORS = 64;
private final SystemTimeProvider timeProvider = new SystemTimeProvider();
private BesuNode executionNode1;
private BesuNode executionNode2;
private TekuBeaconNode tekuNode1;
private TekuBeaconNode tekuNode2;
@BeforeEach
void setup() throws Exception {
final int genesisTime = timeProvider.getTimeInSeconds().plus(10).intValue();
executionNode1 =
createBesuNode(
config ->
config
.withMiningEnabled(true)
.withMergeSupport()
.withP2pEnabled(true)
.withGenesisFile("besu/preMergeGenesis.json")
.withJwtTokenAuthorization(JWT_FILE));
executionNode1.start();
executionNode2 =
createBesuNode(
config ->
config
.withMergeSupport()
.withP2pEnabled(true)
.withGenesisFile("besu/preMergeGenesis.json")
.withJwtTokenAuthorization(JWT_FILE));
executionNode2.start();
tekuNode1 =
createTekuBeaconNode(
configureTekuNode(executionNode1, genesisTime)
.withInteropValidators(0, VALIDATORS)
.build());
tekuNode1.start();
tekuNode2 =
createTekuBeaconNode(
configureTekuNode(executionNode2, genesisTime)
.withInteropValidators(0, 0)
.withPeers(tekuNode1)
.withSafeSlotsToImportOptimistically(SAFE_SLOTS_TO_IMPORT_OPTIMISTICALLY)
.build());
tekuNode2.start();
}
@Test
void shouldPassMergeOptimisticallyAndBeginFinalizationAfterSafeSlotsToImport() throws Exception {
tekuNode2.waitForNonDefaultExecutionPayload();
tekuNode2.waitForOptimisticBlock();
// Now make execution node sync and clarify switch from optimistic sync back to the normal
executionNode2.addPeer(executionNode1);
tekuNode2.waitForNonOptimisticBlock();
}
private TekuNodeConfigBuilder configureTekuNode(
final BesuNode executionEngine, final int genesisTime) throws Exception {
return TekuNodeConfigBuilder.createBeaconNode()
.withNetwork(NETWORK_NAME)
.withBellatrixEpoch(UInt64.ZERO)
.withTotalTerminalDifficulty(10001)
.withGenesisTime(genesisTime)
.withRealNetwork()
.withStartupTargetPeerCount(0)
.withExecutionEngine(executionEngine)
.withJwtSecretFile(JWT_FILE);
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import static tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder.DEFAULT_NETWORK_NAME;
import java.io.IOException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.TekuValidatorNode;
public class RemoteValidatorAcceptanceTest extends AcceptanceTestBase {
private static final int VALIDATOR_COUNT = 8;
private TekuBeaconNode beaconNode;
private TekuValidatorNode validatorClient;
@BeforeEach
public void setup() throws IOException {
beaconNode =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode()
.withInteropNumberOfValidators(VALIDATOR_COUNT)
.withInteropValidators(0, 0)
.build());
validatorClient =
createValidatorNode(
TekuNodeConfigBuilder.createValidatorClient()
.withInteropValidators(0, VALIDATOR_COUNT)
.withBeaconNodes(beaconNode)
.build());
}
@Test
void shouldCreateAttestationsWithRemoteValidator() throws Exception {
beaconNode.start();
validatorClient.start();
waitForValidatorDutiesToComplete();
}
@Test
void shouldCreateAttestationsWithRemoteValidatorStartingFirst() throws Exception {
// if the validator starts first with network auto, it'll spin until it gets spec
// so here we'll explicitly define the network in use.
validatorClient =
createValidatorNode(
TekuNodeConfigBuilder.createValidatorClient()
.withNetwork(DEFAULT_NETWORK_NAME)
.withInteropValidators(0, VALIDATOR_COUNT)
.withBeaconNodes(beaconNode)
.build());
validatorClient.start();
validatorClient.waitForLogMessageContaining(
"Error while connecting to beacon node event stream");
beaconNode.start();
waitForSuccessfulEventStreamConnection();
waitForValidatorDutiesToComplete();
}
@Test
void shouldFailoverWhenPrimaryBeaconNodeGoesDown() throws Exception {
final TekuBeaconNode failoverBeaconNode =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode()
.withInteropNumberOfValidators(VALIDATOR_COUNT)
.withInteropValidators(0, 0)
.withPeers(beaconNode)
.build());
beaconNode.start();
failoverBeaconNode.start();
validatorClient =
createValidatorNode(
TekuNodeConfigBuilder.createValidatorClient()
.withInteropValidators(0, VALIDATOR_COUNT)
.withBeaconNodes(beaconNode, failoverBeaconNode)
.build());
validatorClient.start();
waitForSuccessfulEventStreamConnection();
waitForValidatorDutiesToComplete();
beaconNode.stop(false);
validatorClient.waitForLogMessageContaining(
"Switching to failover beacon node for event streaming");
waitForSuccessfulEventStreamConnection();
waitForValidatorDutiesToComplete();
// primary beacon node recovers
beaconNode.start();
validatorClient.waitForLogMessageContaining(
"Switching back to the primary beacon node for event streaming");
waitForSuccessfulEventStreamConnection();
waitForValidatorDutiesToComplete();
}
@Test
void shouldPerformDutiesIfPrimaryBeaconNodeIsDownOnStartup() throws Exception {
// creating a primary beacon node which we would never start
final TekuBeaconNode primaryBeaconNode =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode()
.withInteropNumberOfValidators(VALIDATOR_COUNT)
.withInteropValidators(0, 0)
.withPeers(beaconNode)
.build());
beaconNode.start();
validatorClient =
createValidatorNode(
TekuNodeConfigBuilder.createValidatorClient()
.withInteropValidators(0, VALIDATOR_COUNT)
.withBeaconNodes(primaryBeaconNode, beaconNode)
.build());
validatorClient.start();
validatorClient.waitForLogMessageContaining(
"The primary beacon node is NOT ready to accept requests");
validatorClient.waitForLogMessageContaining(
"Switching to failover beacon node for event streaming");
waitForSuccessfulEventStreamConnection();
waitForValidatorDutiesToComplete();
}
private void waitForSuccessfulEventStreamConnection() {
validatorClient.waitForLogMessageContaining(
"Successfully connected to beacon node event stream");
}
private void waitForValidatorDutiesToComplete() {
validatorClient.waitForLogMessageContaining("Published block");
validatorClient.waitForLogMessageContaining("Published attestation");
validatorClient.waitForLogMessageContaining("Published aggregate");
validatorClient.waitForLogMessageContaining("Published sync_signature");
validatorClient.waitForLogMessageContaining("Published sync_contribution");
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuDockerVersion;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.TekuValidatorNode;
public class RemoteValidatorCompatibilityAcceptanceTest extends AcceptanceTestBase {
static final int VALIDATOR_COUNT = 8;
private TekuBeaconNode beaconNode;
private TekuValidatorNode validatorClient;
@Test
void shouldRunUpdatedValidatorAgainstOldBeaconNode() throws Exception {
verifyCompatibility(TekuDockerVersion.V24_2_0, TekuDockerVersion.LOCAL_BUILD);
}
@Test
void shouldRunUpdatedValidatorAgainstLastReleaseBeaconNode() throws Exception {
verifyCompatibility(TekuDockerVersion.LAST_RELEASE, TekuDockerVersion.LOCAL_BUILD);
}
@Test
void shouldRunLastReleaseValidatorAgainstUpdatedBeaconNode() throws Exception {
verifyCompatibility(TekuDockerVersion.LOCAL_BUILD, TekuDockerVersion.LAST_RELEASE);
}
private void verifyCompatibility(
final TekuDockerVersion beaconNodeVersion, final TekuDockerVersion validatorNodeVersion)
throws Exception {
createBeaconNode(beaconNodeVersion);
createValidatorClient(validatorNodeVersion);
beaconNode.start();
validatorClient.start();
validatorClient.waitForLogMessageContaining("Published block");
validatorClient.waitForLogMessageContaining("Published attestation");
validatorClient.waitForLogMessageContaining("Published aggregate");
}
private void createValidatorClient(final TekuDockerVersion version) {
validatorClient =
createValidatorNode(
version,
TekuNodeConfigBuilder.createValidatorClient()
.withNetwork("auto")
.withInteropValidators(0, VALIDATOR_COUNT)
.withBeaconNodes(beaconNode)
.build());
}
private void createBeaconNode(final TekuDockerVersion version) throws IOException {
beaconNode =
createTekuBeaconNode(
version,
TekuNodeConfigBuilder.createBeaconNode()
.withInteropNumberOfValidators(VALIDATOR_COUNT)
.withInteropValidators(0, 0)
.build());
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import java.util.Collections;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.bls.BLSPublicKey;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.GenesisGenerator.InitialStateData;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.TekuValidatorNode;
import tech.pegasys.teku.test.acceptance.dsl.Web3SignerNode;
import tech.pegasys.teku.test.acceptance.dsl.tools.ValidatorKeysApi;
import tech.pegasys.teku.test.acceptance.dsl.tools.deposits.ValidatorKeystores;
public class RemoteValidatorKeysAcceptanceTest extends AcceptanceTestBase {
@Test
void shouldMaintainValidatorsInMutableClient() throws Exception {
final String networkName = "swift";
final ValidatorKeystores validatorKeystores =
createTekuDepositSender(networkName).generateValidatorKeys(8);
final InitialStateData genesis =
createGenesisGenerator().network(networkName).validatorKeys(validatorKeystores).generate();
final TekuBeaconNode beaconNode =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode().withInitialState(genesis).build());
final Web3SignerNode web3SignerNode =
createWeb3SignerNode(config -> config.withNetwork(networkName));
web3SignerNode.start();
final ValidatorKeysApi signerApi = web3SignerNode.getValidatorKeysApi();
final TekuValidatorNode validatorClient =
createValidatorNode(
TekuNodeConfigBuilder.createValidatorClient()
.withValidatorApiEnabled()
.withExternalSignerUrl(web3SignerNode.getValidatorRestApiUrl())
.withInteropModeDisabled()
.withBeaconNodes(beaconNode)
.build());
beaconNode.start();
validatorClient.start();
signerApi.addLocalValidatorsAndExpect(validatorKeystores, "imported");
signerApi.assertLocalValidatorListing(validatorKeystores.getPublicKeys());
final ValidatorKeysApi validatorNodeApi = validatorClient.getValidatorKeysApi();
validatorNodeApi.assertLocalValidatorListing(Collections.emptyList());
validatorNodeApi.assertRemoteValidatorListing(Collections.emptyList());
validatorNodeApi.addRemoteValidatorsAndExpect(
validatorKeystores.getPublicKeys(), web3SignerNode.getValidatorRestApiUrl(), "imported");
validatorClient.waitForLogMessageContaining("Added validator");
validatorNodeApi.assertLocalValidatorListing(Collections.emptyList());
validatorNodeApi.assertRemoteValidatorListing(validatorKeystores.getPublicKeys());
// add Local should see duplicates, as they're already loaded
validatorNodeApi.addLocalValidatorsAndExpect(validatorKeystores, "duplicate");
// second remote add should also see as duplicates
validatorNodeApi.addRemoteValidatorsAndExpect(
validatorKeystores.getPublicKeys(), web3SignerNode.getValidatorRestApiUrl(), "duplicate");
validatorClient.waitForLogMessageContaining("Published block");
// generate voluntary exit
validatorNodeApi.generateVoluntaryExitAndCheckValidatorIndex(
validatorKeystores.getPublicKeys().get(1), 1);
// remove a validator
final BLSPublicKey removedPubKey = validatorKeystores.getPublicKeys().get(0);
validatorNodeApi.removeRemoteValidatorAndCheckStatus(removedPubKey, "deleted");
// should only be 7 validators left
validatorClient.waitForLogMessageContaining("Removed remote validator");
validatorClient.waitForLogMessageContaining("Published block");
validatorNodeApi.assertRemoteValidatorListing(validatorKeystores.getPublicKeys().subList(1, 7));
// remove validator that doesn't exist
validatorNodeApi.removeRemoteValidatorAndCheckStatus(removedPubKey, "not_found");
validatorClient.stop();
beaconNode.stop();
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.SentryNodesConfig;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.TekuValidatorNode;
public class SentryNodesAcceptanceTest extends AcceptanceTestBase {
@Test
void sentryBeaconNodesSetup() throws Exception {
final TekuBeaconNode dutiesProviderNode = createAndStartBootstrapBeaconNode();
final TekuBeaconNode attestationPublisherNode =
createAndStartPeerBeaconNode(dutiesProviderNode, dutiesProviderNode.getGenesisTime());
final TekuBeaconNode blockHandlerNode =
createAndStartPeerBeaconNode(dutiesProviderNode, dutiesProviderNode.getGenesisTime());
final SentryNodesConfig sentryNodesConfig =
new SentryNodesConfig.Builder()
.withDutiesProviders(dutiesProviderNode)
.withAttestationPublisher(attestationPublisherNode)
.withBlockHandlers(blockHandlerNode)
.build();
final TekuValidatorNode remoteValidator =
createValidatorNode(
TekuNodeConfigBuilder.createValidatorClient()
.withInteropValidators(0, 32)
.withSentryNodes(sentryNodesConfig)
.build());
remoteValidator.start();
remoteValidator.waitForDutiesRequestedFrom(dutiesProviderNode);
remoteValidator.waitForAttestationPublishedTo(attestationPublisherNode);
remoteValidator.waitForBlockPublishedTo(blockHandlerNode);
}
private TekuBeaconNode createAndStartPeerBeaconNode(
final TekuBeaconNode dutiesProviderNode, final UInt64 genesisTime) throws Exception {
final TekuBeaconNode blockHandlerNode =
createLateJoiningNode(dutiesProviderNode, genesisTime.intValue());
blockHandlerNode.start();
return blockHandlerNode;
}
private TekuBeaconNode createAndStartBootstrapBeaconNode() throws Exception {
final TekuBeaconNode dutiesProviderNode =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode()
.withRealNetwork()
.withInteropNumberOfValidators(64)
.withInteropValidators(32, 32)
.build());
dutiesProviderNode.start();
return dutiesProviderNode;
}
private TekuBeaconNode createLateJoiningNode(
final TekuBeaconNode primaryNode, final int genesisTime) throws IOException {
return createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode()
.withGenesisTime(genesisTime)
.withRealNetwork()
.withPeers(primaryNode)
.withInteropValidators(0, 0)
.build());
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import static tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder.DEFAULT_NETWORK_NAME;
import java.io.File;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.BesuNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
public class StartupAcceptanceTest extends AcceptanceTestBase {
@Test
public void shouldProgressChainAfterStartingFromMockGenesis() throws Exception {
final TekuBeaconNode node = createTekuBeaconNode();
node.start();
node.waitForGenesis();
node.waitForNewBlock();
}
@Test
public void shouldProgressChainAfterStartingFromDisk() throws Exception {
final TekuBeaconNode node1 = createTekuBeaconNode();
node1.start();
final UInt64 genesisTime = node1.getGenesisTime();
File dataDirectory = node1.getDataDirectoryFromContainer();
node1.stop();
final TekuBeaconNode node2 = createTekuBeaconNode();
node2.copyContentsToWorkingDirectory(dataDirectory);
node2.start();
node2.waitForGenesisTime(genesisTime);
node2.waitForNewBlock();
}
@Test
public void shouldContainSyncCommitteeAggregatesOnAltair() throws Exception {
final TekuBeaconNode node1 =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode().withAltairEpoch(UInt64.ZERO).build());
node1.start();
node1.waitForFullSyncCommitteeAggregate();
}
@Test
public void shouldFinalize() throws Exception {
final TekuBeaconNode node1 = createTekuBeaconNode();
node1.start();
node1.waitForNewFinalization();
node1.stop();
}
@Test
public void shouldStartChainFromDepositContract() throws Exception {
final BesuNode eth1Node = createBesuNode(config -> config.withMiningEnabled(true));
eth1Node.start();
final TekuBeaconNode tekuNode =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode().withDepositsFrom(eth1Node).build());
tekuNode.start();
createTekuDepositSender(DEFAULT_NETWORK_NAME).sendValidatorDeposits(eth1Node, 4);
tekuNode.waitForGenesis();
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
public class SyncAcceptanceTest extends AcceptanceTestBase {
@Test
public void shouldSyncToNodeWithGreaterFinalizedEpoch() throws Exception {
final TekuBeaconNode primaryNode =
createTekuBeaconNode(TekuNodeConfigBuilder.createBeaconNode().withRealNetwork().build());
primaryNode.start();
UInt64 genesisTime = primaryNode.getGenesisTime();
final TekuBeaconNode lateJoiningNode =
createLateJoiningNode(primaryNode, genesisTime.intValue());
primaryNode.waitForNewFinalization();
lateJoiningNode.start();
lateJoiningNode.waitForGenesis();
lateJoiningNode.waitUntilInSyncWith(primaryNode);
}
private TekuBeaconNode createLateJoiningNode(
final TekuBeaconNode primaryNode, final int genesisTime) throws IOException {
return createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode()
.withGenesisTime(genesisTime)
.withRealNetwork()
.withPeers(primaryNode)
.withInteropValidators(0, 0)
.build());
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import java.io.IOException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.api.response.v1.EventType;
import tech.pegasys.teku.infrastructure.time.SystemTimeProvider;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.TekuValidatorNode;
public class SyncCommitteeGossipAcceptanceTest extends AcceptanceTestBase {
private static final int NODE_VALIDATORS = 8;
private static final int TOTAL_VALIDATORS = NODE_VALIDATORS * 2;
private final String network = "swift";
private final SystemTimeProvider timeProvider = new SystemTimeProvider();
private TekuBeaconNode primaryNode;
private TekuBeaconNode secondaryNode;
private TekuValidatorNode validatorClient;
private TekuBeaconNode watcherNode;
@BeforeEach
public void setup() throws IOException {
final int genesisTime = timeProvider.getTimeInSeconds().plus(15).intValue();
primaryNode =
createTekuBeaconNode(
configureNode(genesisTime).withInteropValidators(0, NODE_VALIDATORS).build());
secondaryNode =
createTekuBeaconNode(
configureNode(genesisTime).withInteropValidators(0, 0).withPeers(primaryNode).build());
validatorClient =
createValidatorNode(
TekuNodeConfigBuilder.createValidatorClient()
.withNetwork(network)
.withInteropValidators(NODE_VALIDATORS, NODE_VALIDATORS)
.withBeaconNodes(secondaryNode)
.build());
// Use a third node to watch for published aggregates.
watcherNode =
createTekuBeaconNode(
configureNode(genesisTime)
.withPeers(primaryNode, secondaryNode)
.withInteropValidators(0, 0)
.build());
}
@Test
public void shouldContainSyncCommitteeAggregates() throws Exception {
primaryNode.start();
secondaryNode.start();
validatorClient.start();
watcherNode.start();
watcherNode.startEventListener(EventType.contribution_and_proof);
primaryNode.waitForEpochAtOrAbove(1);
// Wait until we get a contribution over gossip. The watcher node doesn't run any validators.
watcherNode.waitForContributionAndProofEvent();
// And make sure that the contributions get combined properly into a full aggregate in the block
secondaryNode.waitForFullSyncCommitteeAggregate();
validatorClient.stop();
secondaryNode.stop();
primaryNode.stop();
}
private TekuNodeConfigBuilder configureNode(final int genesisTime) throws IOException {
return TekuNodeConfigBuilder.createBeaconNode()
.withNetwork(network)
.withAltairEpoch(UInt64.ZERO)
.withGenesisTime(genesisTime)
.withInteropNumberOfValidators(TOTAL_VALIDATORS)
.withRealNetwork();
}
}
/*
* Copyright Consensys Software Inc., 2023
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import com.google.common.io.Resources;
import java.net.URL;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.BesuNode;
import tech.pegasys.teku.test.acceptance.dsl.GenesisGenerator;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
import tech.pegasys.teku.test.acceptance.dsl.tools.deposits.ValidatorKeystores;
public class SyncingStatusAcceptanceTest extends AcceptanceTestBase {
private static final URL JWT_FILE = Resources.getResource("auth/ee-jwt-secret.hex");
@Test
void shouldRespondSyncingWhenExecutionLayerIsDown() throws Exception {
final String networkName = "less-swift";
final BesuNode eth1Node =
createBesuNode(
config ->
config
.withMiningEnabled(true)
.withMergeSupport()
.withGenesisFile("besu/preMergeGenesis.json")
.withJwtTokenAuthorization(JWT_FILE));
eth1Node.start();
final ValidatorKeystores validatorKeystores =
createTekuDepositSender(networkName).sendValidatorDeposits(eth1Node, 8);
final GenesisGenerator.InitialStateData genesis =
createGenesisGenerator().network(networkName).validatorKeys(validatorKeystores).generate();
final String defaultFeeRecipient = "0xFE3B557E8Fb62b89F4916B721be55cEb828dBd73";
final TekuBeaconNode beaconNode =
createTekuBeaconNode(
TekuNodeConfigBuilder.createBeaconNode()
.withValidatorLivenessTracking()
.withNetwork(networkName)
.withDepositsFrom(eth1Node)
.withBellatrixEpoch(UInt64.ONE)
.withTotalTerminalDifficulty(10001)
.withValidatorProposerDefaultFeeRecipient(defaultFeeRecipient)
.withExecutionEngine(eth1Node)
.withInitialState(genesis)
.withRealNetwork()
.withJwtSecretFile(JWT_FILE)
.build());
beaconNode.start();
beaconNode.waitForEpochAtOrAbove(1);
beaconNode.expectElOnline();
beaconNode.expectNodeNotSyncing();
eth1Node.stop();
beaconNode.waitForLogMessageContaining("Make sure the Execution Client is online");
beaconNode.expectElOffline();
beaconNode.stop();
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance;
import static tech.pegasys.teku.test.acceptance.dsl.ValidatorLivenessExpectation.expectLive;
import static tech.pegasys.teku.test.acceptance.dsl.ValidatorLivenessExpectation.expectNotLive;
import java.io.IOException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import tech.pegasys.teku.infrastructure.time.SystemTimeProvider;
import tech.pegasys.teku.infrastructure.unsigned.UInt64;
import tech.pegasys.teku.test.acceptance.dsl.AcceptanceTestBase;
import tech.pegasys.teku.test.acceptance.dsl.TekuBeaconNode;
import tech.pegasys.teku.test.acceptance.dsl.TekuNodeConfigBuilder;
public class ValidatorLivenessAcceptanceTest extends AcceptanceTestBase {
private static final int NODE_VALIDATORS = 2;
private static final int TOTAL_VALIDATORS = NODE_VALIDATORS * 2;
private final SystemTimeProvider timeProvider = new SystemTimeProvider();
private TekuBeaconNode primaryNode;
private TekuBeaconNode secondaryNode;
@BeforeEach
public void setup() throws IOException {
final UInt64 altairEpoch = UInt64.valueOf(100);
final int genesisTime = timeProvider.getTimeInSeconds().plus(10).intValue();
primaryNode =
createTekuBeaconNode(
configureNode(genesisTime)
.withAltairEpoch(altairEpoch)
.withInteropValidators(0, NODE_VALIDATORS)
.build());
secondaryNode =
createTekuBeaconNode(
configureNode(genesisTime)
.withAltairEpoch(altairEpoch)
.withInteropValidators(NODE_VALIDATORS, NODE_VALIDATORS)
.withPeers(primaryNode)
.build());
}
/*
* Primary and Secondary node, each with half of the validators
* - Primary is online at genesis, it's validators should be always performing duties.
* - no validator keys from the secondary will be seen as active in epoch 0 or 1.
* - Secondary is online at epoch 2, so by epoch 3 should all be performing duties.
* - by epoch 5, all validators should be seen as performing duties in epoch 3
*/
@Test
@Disabled("this test has been flaking (88% over last 100 CI) #4821")
void shouldTrackValidatorLivenessOverEpochs() throws Exception {
primaryNode.start();
final int startEpoch = primaryNode.waitForEpochAtOrAbove(2);
secondaryNode.start();
primaryNode.checkValidatorLiveness(
1,
TOTAL_VALIDATORS,
expectLive(0, NODE_VALIDATORS),
expectNotLive(NODE_VALIDATORS, NODE_VALIDATORS));
primaryNode.waitForEpochAtOrAbove(startEpoch + 3);
primaryNode.checkValidatorLiveness(
startEpoch + 1, TOTAL_VALIDATORS, expectLive(0, TOTAL_VALIDATORS));
secondaryNode.checkValidatorLiveness(
startEpoch + 1, TOTAL_VALIDATORS, expectLive(0, TOTAL_VALIDATORS));
secondaryNode.stop();
primaryNode.stop();
}
private TekuNodeConfigBuilder configureNode(final int genesisTime) throws IOException {
return TekuNodeConfigBuilder.createBeaconNode()
.withValidatorLivenessTracking()
.withGenesisTime(genesisTime)
.withInteropNumberOfValidators(TOTAL_VALIDATORS)
.withRealNetwork();
}
}
/*
* Copyright Consensys Software Inc., 2022
*
* 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.
*/
package tech.pegasys.teku.test.acceptance.dsl;
public enum BesuDockerVersion {
STABLE("24.9.1"),
DEVELOP("develop");
private final String version;
BesuDockerVersion(final String version) {
this.version = version;
}
public String getVersion() {
return version;
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment