Skip to content

Ethereum. Refactoring, parametrised network ID & Reth support #167

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 0 additions & 29 deletions lib/ethereum/.env-sample

This file was deleted.

34 changes: 25 additions & 9 deletions lib/ethereum/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,55 +3,71 @@ import 'dotenv/config'
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import * as nag from "cdk-nag";
import * as config from "./lib/config/ethConfig";
import { EthNodeRole } from "./lib/config/ethConfig.interface";
import * as config from "./lib/config/node-config";
import { EthNodeRole } from "./lib/config/node-config.interface";

import { EthSingleNodeStack } from "./lib/single-node-stack";
import { EthCommonStack } from "./lib/common-stack";
import { EthRpcNodesStack } from "./lib/rpc-nodes-stack";

const app = new cdk.App();
cdk.Tags.of(app).add("Project", "Ethereum");
cdk.Tags.of(app).add("Project", "AWSEthereum");

new EthCommonStack(app, "eth-common", {
env: { account: config.baseConfig.accountId, region: config.baseConfig.region },
stackName: `eth-nodes-common`,
env: { account: config.baseConfig.accountId, region: config.baseConfig.region },
snapshotType: config.baseConfig.snapshotType,
});

new EthSingleNodeStack(app, "eth-sync-node", {
stackName: `eth-sync-node-${config.baseConfig.clientCombination}`,

env: { account: config.baseConfig.accountId, region: config.baseConfig.region },
ethClientCombination: config.baseConfig.clientCombination,
network: config.baseConfig.network,
snapshotType: config.baseConfig.snapshotType,
consensusSnapshotURL: config.baseConfig.consensusSnapshotURL,
executionSnapshotURL: config.baseConfig.executionSnapshotURL,
consensusCheckpointSyncURL: config.baseConfig.consensusCheckpointSyncURL,
nodeRole: <EthNodeRole> "sync-node",
instanceType: config.syncNodeConfig.instanceType,
instanceCpuType: config.syncNodeConfig.instanceCpuType,
dataVolumes: config.syncNodeConfig.dataVolumes,
dataVolume: config.syncNodeConfig.dataVolumes[0],
});

new EthSingleNodeStack(app, "eth-single-node", {
stackName: `eth-single-node-${config.baseConfig.clientCombination}`,

env: { account: config.baseConfig.accountId, region: config.baseConfig.region },
ethClientCombination: config.baseConfig.clientCombination,
network: config.baseConfig.network,
snapshotType: config.baseConfig.snapshotType,
consensusSnapshotURL: config.baseConfig.consensusSnapshotURL,
executionSnapshotURL: config.baseConfig.executionSnapshotURL,
consensusCheckpointSyncURL: config.baseConfig.consensusCheckpointSyncURL,
nodeRole: <EthNodeRole> "single-node",
instanceType: config.syncNodeConfig.instanceType,
instanceCpuType: config.syncNodeConfig.instanceCpuType,
dataVolumes: config.syncNodeConfig.dataVolumes,
instanceType: config.rpcNodeConfig.instanceType,
instanceCpuType: config.rpcNodeConfig.instanceCpuType,
dataVolume: config.rpcNodeConfig.dataVolumes[0],
});

new EthRpcNodesStack(app, "eth-rpc-nodes", {
stackName: `eth-rpc-nodes-${config.baseConfig.clientCombination}`,

env: { account: config.baseConfig.accountId, region: config.baseConfig.region },
ethClientCombination: config.baseConfig.clientCombination,
network: config.baseConfig.network,
snapshotType: config.baseConfig.snapshotType,
consensusSnapshotURL: config.baseConfig.consensusSnapshotURL,
executionSnapshotURL: config.baseConfig.executionSnapshotURL,
consensusCheckpointSyncURL: config.baseConfig.consensusCheckpointSyncURL,
nodeRole: <EthNodeRole> "rpc-node",
instanceType: config.rpcNodeConfig.instanceType,
instanceCpuType: config.rpcNodeConfig.instanceCpuType,
numberOfNodes: config.rpcNodeConfig.numberOfNodes,
albHealthCheckGracePeriodMin: config.rpcNodeConfig.albHealthCheckGracePeriodMin,
heartBeatDelayMin: config.rpcNodeConfig.heartBeatDelayMin,
dataVolumes: config.syncNodeConfig.dataVolumes,
dataVolume: config.syncNodeConfig.dataVolumes[0],
});


Expand Down
11 changes: 0 additions & 11 deletions lib/ethereum/lib/assets/copy-data-to-s3.sh

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[cfn-auto-reloader-hook]
triggers=post.update
path=Resources.WebServerHost.Metadata.AWS::CloudFormation::Init
action=/opt/aws/bin/cfn-init -v --stack __AWS_STACK_NAME__ --resource WebServerHost --region __AWS_REGION__
5 changes: 5 additions & 0 deletions lib/ethereum/lib/assets/instance/cfn-hup/cfn-hup.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[main]
stack=__AWS_STACK_ID__
region=__AWS_REGION__
# The interval used to check for changes to the resource metadata in minutes. Default is 15
interval=2
8 changes: 8 additions & 0 deletions lib/ethereum/lib/assets/instance/cfn-hup/cfn-hup.service
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[Unit]
Description=cfn-hup daemon
[Service]
Type=simple
ExecStart=/usr/local/bin/cfn-hup
Restart=always
[Install]
WantedBy=multi-user.target
40 changes: 40 additions & 0 deletions lib/ethereum/lib/assets/instance/cfn-hup/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/bin/bash

if [ -n "$1" ]; then
export STACK_ID=$1
else
echo "Error: No Stack ID is provided"
echo "Usage: instance/cfn-hup/setup.sh <stack_id> <aws_region>"
exit 1
fi

if [ -n "$2" ]; then
export AWS_REGION=$2
else
echo "Error: No AWS Region is provided"
echo "Usage: instance/cfn-hup/setup.sh <stack_id> <aws_region>"
exit 1
fi

echo "Install CloudFormation helper scripts"
mkdir -p /opt/aws/
pip3 install --break-system-packages https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz
ln -s /usr/local/init/ubuntu/cfn-hup /etc/init.d/cfn-hup

echo "Configuring CloudFormation helper scripts"
mkdir -p /etc/cfn/
mv /opt/instance/cfn-hup/cfn-hup.conf /etc/cfn/cfn-hup.conf
sed -i "s;__AWS_STACK_ID__;\"$STACK_ID\";g" /etc/cfn/cfn-hup.conf
sed -i "s;__AWS_REGION__;\"$AWS_REGION\";g" /etc/cfn/cfn-hup.conf

mkdir -p /etc/cfn/hooks.d/system
mv /opt/instance/cfn-hup/cfn-auto-reloader.conf /etc/cfn/hooks.d/cfn-auto-reloader.conf
sed -i "s;__AWS_STACK_NAME__;\"$STACK_NAME\";g" /etc/cfn/hooks.d/cfn-auto-reloader.conf
sed -i "s;__AWS_REGION__;\"$AWS_REGION\";g" /etc/cfn/hooks.d/cfn-auto-reloader.conf

echo "Starting CloudFormation helper scripts as a service"
mv /opt/instance/cfn-hup/cfn-hup.service /etc/systemd/system/cfn-hup.service

systemctl daemon-reload
systemctl enable --now cfn-hup
systemctl start cfn-hup.service
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
#!/bin/bash

source /etc/environment
source /etc/cdk_environment
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
INSTANCE_ID=$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/instance-id)

echo "Sync started at " $(date)
SECONDS=0

s5cmd --log error cp --exclude 'lost+found' $SNAPSHOT_S3_PATH/data/* /data && \
chown -R ethereum:ethereum /data && \
chown -R bcuser:bcuser /data && \
echo "Sync finished at " $(date) && \
echo "$(($SECONDS / 60)) minutes and $(($SECONDS % 60)) seconds elapsed." && \
su ethereum && \
/usr/local/bin/docker-compose -f /home/ethereum/docker-compose.yml up -d && \
su bcuser && \
docker compose -f /home/bcuser/docker-compose.yml up -d && \
aws autoscaling complete-lifecycle-action --lifecycle-action-result CONTINUE --instance-id $INSTANCE_ID --lifecycle-hook-name "$LIFECYCLE_HOOK_NAME" --auto-scaling-group-name "$AUTOSCALING_GROUP_NAME" --region $REGION || \
aws autoscaling complete-lifecycle-action --lifecycle-action-result ABANDON --instance-id $INSTANCE_ID --lifecycle-hook-name "$LIFECYCLE_HOOK_NAME" --auto-scaling-group-name "$AUTOSCALING_GROUP_NAME" --region $REGION
11 changes: 11 additions & 0 deletions lib/ethereum/lib/assets/instance/storage/copy-data-to-s3.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/bash
set +e
source /etc/cdk_environment

/usr/local/bin/docker-compose -f /home/bcuser/docker-compose.yml down
echo "Sync started at " $(date)
s5cmd --log error sync /data $SNAPSHOT_S3_PATH/
echo "Sync finished at " $(date)
sudo touch /data/snapshotted
sudo su bcuser
docker compose -f /home/bcuser/docker-compose.yml up -d
130 changes: 130 additions & 0 deletions lib/ethereum/lib/assets/instance/storage/setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
#!/bin/bash

make_fs () {
# If file system = to ext4 use mkfs.ext4, if xfs use mkfs.xfs
if [ -z "$1" ]; then
echo "Error: No file system type provided."
echo "Usage: make_fs <file system type [ xfs | ext4 ]> <target_volume_id>"
exit 1
fi

if [ -z "$2" ]; then
echo "Error: No target volume ID provided."
echo "Usage: make_fs <file system type [ xfs | ext4 ]> <target_volume_id>"
exit 1
fi

local file_system=$1
local volume_id=$2
if [ "$file_system" == "ext4" ]; then
mkfs -t ext4 "$volume_id"
return "$?"
else
mkfs.xfs -f "$volume_id"
return "$?"
fi
}

# We need an nvme disk that is not mounted and not partitioned
get_all_empty_nvme_disks () {
local all_not_mounted_nvme_disks
local all_mounted_nvme_partitions
local unmounted_nvme_disks=()
local sorted_unmounted_nvme_disks

#The disk will only be mounted when the nvme disk is larger than 100GB to avoid storing blockchain node data directly on the root EBS disk (which is 46GB by default)
all_not_mounted_nvme_disks=$(lsblk -lnb | awk '{if ($7 == "" && $4 > 100000000) {print $1}}' | grep nvme)
all_mounted_nvme_partitions=$(mount | awk '{print $1}' | grep /dev/nvme)
for disk in ${all_not_mounted_nvme_disks[*]}; do
if [[ ! "${all_mounted_nvme_partitions[*]}" =~ $disk ]]; then
unmounted_nvme_disks+=("$disk")
fi
done
# Sort the array
sorted_unmounted_nvme_disks=($(printf '%s\n' "${unmounted_nvme_disks[*]}" | sort))
echo "${sorted_unmounted_nvme_disks[*]}"
}

get_next_empty_nvme_disk () {
local sorted_unmounted_nvme_disks
sorted_unmounted_nvme_disks=($(get_all_empty_nvme_disks))
# Return the first unmounted nvme disk
echo "/dev/${sorted_unmounted_nvme_disks[0]}"
}

# Add input as command line parameters for name of the directory to mount
if [ -n "$1" ]; then
DIR_NAME=$1
else
echo "Error: No data file system mount path is provided."
echo "Usage: instance/storage/setup.sh <file_system_mount_path> <file_system_type [ xfs | ext4 ]> <target_volume_size_in_bytes> "
echo "Default file system type is ext4"
echo "If you skip <target_volume_size_in_bytes>, script will try to use the first unformatted volume ID."
echo "Usage example: instance/storage/setup.sh /data ext4 300000000000000"
exit 1
fi

# Case input for $2 between ext4 and xfs, use ext4 as default
case $2 in
ext4)
echo "File system set to ext4"
FILE_SYSTEM="ext4"
FS_CONFIG="defaults"
;;
xfs)
echo "File system set to xfs"
FILE_SYSTEM="xfs"
FS_CONFIG="noatime,nodiratime,nodiscard" # See more: https://cdrdv2-public.intel.com/686417/rocksdb-benchmark-tuning-guide-on-xeon.pdf
;;
*)
echo "File system set to ext4"
FILE_SYSTEM="ext4"
FS_CONFIG="defaults"
;;
esac

if [ -n "$3" ]; then
VOLUME_SIZE=$3
else
echo "The size of volume for $DIR_NAME is not specified. Will try to guess volume ID."
fi

echo "Checking if $DIR_NAME is mounted, and dont do anything if it is"
if [ $(df --output=target | grep -c "$DIR_NAME") -lt 1 ]; then

if [ -n "$VOLUME_SIZE" ]; then
VOLUME_ID=/dev/$(lsblk -lnb | awk -v VOLUME_SIZE_BYTES="$VOLUME_SIZE" '{if ($4== VOLUME_SIZE_BYTES) {print $1}}')
echo "Data volume size defined, use respective volume id: $VOLUME_ID"
else
VOLUME_ID=$(get_next_empty_nvme_disk)
echo "Data volume size undefined, trying volume id: $VOLUME_ID"
fi

make_fs $FILE_SYSTEM "$VOLUME_ID"

sleep 10
VOLUME_UUID=$(lsblk -fn -o UUID "$VOLUME_ID")
VOLUME_FSTAB_CONF="UUID=$VOLUME_UUID $DIR_NAME $FILE_SYSTEM $FS_CONFIG 0 2"
echo "VOLUME_ID=$VOLUME_ID"
echo "VOLUME_UUID=$VOLUME_UUID"
echo "VOLUME_FSTAB_CONF=$VOLUME_FSTAB_CONF"

# Check if data disc is already in fstab and replace the line if it is with the new disc UUID
echo "Checking fstab for volume $DIR_NAME"
if [ $(grep -c "$DIR_NAME" /etc/fstab) -gt 0 ]; then
SED_REPLACEMENT_STRING="$(grep -n "$DIR_NAME" /etc/fstab | cut -d: -f1)s#.*#$VOLUME_FSTAB_CONF#"
# if file exists, delete it
if [ -f /etc/fstab.bak ]; then
rm /etc/fstab.bak
fi
cp /etc/fstab /etc/fstab.bak
sed -i "$SED_REPLACEMENT_STRING" /etc/fstab
else
echo "$VOLUME_FSTAB_CONF" | tee -a /etc/fstab
fi

mount -a
chown -R bcuser:bcuser "$DIR_NAME"
else
echo "$DIR_NAME volume is mounted, nothing changed"
fi
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ services:
besu_node:
environment:
- "JAVA_OPTS=-Xmx8g"
image: hyperledger/besu:24.3.0
image: hyperledger/besu:25.1.0
container_name: execution
restart: always
command:
[
"--network=mainnet",
"--network=__ETH_NETWORK__",
"--data-path=/var/lib/besu/data",
"--sync-mode=X_SNAP",
"--data-storage-format=BONSAI",
Expand All @@ -22,7 +22,7 @@ services:
"--engine-rpc-enabled=true",
"--Xplugin-rocksdb-high-spec-enabled",
]
user: ethereum:ethereum
user: bcuser:bcuser
volumes:
- /secrets:/var/lib/besu/secrets
- /data/execution/data:/var/lib/besu/data
Expand All @@ -43,22 +43,22 @@ services:
environment:
- "JAVA_OPTS=-Xmx4g"
- "TEKU_OPTS=-XX:-HeapDumpOnOutOfMemoryError"
image: consensys/teku:24.3.0-jdk17
image: consensys/teku:25.1.0-jdk21
container_name: consensus
restart: always
command:
[
"--data-base-path=/var/lib/teku/data",
"--ee-endpoint=http://besu_node:8551",
"--initial-state=https://beaconstate.info/eth/v2/debug/beacon/states/finalized",
"--checkpoint-sync-url=__ETH_CONSENSUS_CHECKPOINT_SYNC_URL__",
"--ee-jwt-secret-file=/var/lib/teku/secrets/jwtsecret",
"--p2p-port=9000",
"--metrics-enabled=true",
"--rest-api-enabled=true",
]
depends_on:
- besu_node
user: ethereum:ethereum
user: bcuser:bcuser
volumes:
- /secrets:/var/lib/teku/secrets
- /data/consensus/data:/var/lib/teku/data
Expand Down
Loading
Loading