Skip to content

Commit 938dfeb

Browse files
committed
Ethereum. Refactoring, parametrised network ID & Reth support
1 parent 1a91676 commit 938dfeb

Some content is hidden

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

44 files changed

+1007
-561
lines changed

lib/ethereum/.env-sample

Lines changed: 0 additions & 29 deletions
This file was deleted.

lib/ethereum/app.ts

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,71 @@ import 'dotenv/config'
33
import "source-map-support/register";
44
import * as cdk from "aws-cdk-lib";
55
import * as nag from "cdk-nag";
6-
import * as config from "./lib/config/ethConfig";
7-
import { EthNodeRole } from "./lib/config/ethConfig.interface";
6+
import * as config from "./lib/config/node-config";
7+
import { EthNodeRole } from "./lib/config/node-config.interface";
88

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

1313
const app = new cdk.App();
14-
cdk.Tags.of(app).add("Project", "Ethereum");
14+
cdk.Tags.of(app).add("Project", "AWSEthereum");
1515

1616
new EthCommonStack(app, "eth-common", {
17-
env: { account: config.baseConfig.accountId, region: config.baseConfig.region },
1817
stackName: `eth-nodes-common`,
18+
env: { account: config.baseConfig.accountId, region: config.baseConfig.region },
19+
snapshotType: config.baseConfig.snapshotType,
1920
});
2021

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

2425
env: { account: config.baseConfig.accountId, region: config.baseConfig.region },
2526
ethClientCombination: config.baseConfig.clientCombination,
27+
network: config.baseConfig.network,
28+
snapshotType: config.baseConfig.snapshotType,
29+
consensusSnapshotURL: config.baseConfig.consensusSnapshotURL,
30+
executionSnapshotURL: config.baseConfig.executionSnapshotURL,
31+
consensusCheckpointSyncURL: config.baseConfig.consensusCheckpointSyncURL,
2632
nodeRole: <EthNodeRole> "sync-node",
2733
instanceType: config.syncNodeConfig.instanceType,
2834
instanceCpuType: config.syncNodeConfig.instanceCpuType,
29-
dataVolumes: config.syncNodeConfig.dataVolumes,
35+
dataVolume: config.syncNodeConfig.dataVolumes[0],
3036
});
3137

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

3541
env: { account: config.baseConfig.accountId, region: config.baseConfig.region },
3642
ethClientCombination: config.baseConfig.clientCombination,
43+
network: config.baseConfig.network,
44+
snapshotType: config.baseConfig.snapshotType,
45+
consensusSnapshotURL: config.baseConfig.consensusSnapshotURL,
46+
executionSnapshotURL: config.baseConfig.executionSnapshotURL,
47+
consensusCheckpointSyncURL: config.baseConfig.consensusCheckpointSyncURL,
3748
nodeRole: <EthNodeRole> "single-node",
38-
instanceType: config.syncNodeConfig.instanceType,
39-
instanceCpuType: config.syncNodeConfig.instanceCpuType,
40-
dataVolumes: config.syncNodeConfig.dataVolumes,
49+
instanceType: config.rpcNodeConfig.instanceType,
50+
instanceCpuType: config.rpcNodeConfig.instanceCpuType,
51+
dataVolume: config.rpcNodeConfig.dataVolumes[0],
4152
});
4253

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

4657
env: { account: config.baseConfig.accountId, region: config.baseConfig.region },
4758
ethClientCombination: config.baseConfig.clientCombination,
59+
network: config.baseConfig.network,
60+
snapshotType: config.baseConfig.snapshotType,
61+
consensusSnapshotURL: config.baseConfig.consensusSnapshotURL,
62+
executionSnapshotURL: config.baseConfig.executionSnapshotURL,
63+
consensusCheckpointSyncURL: config.baseConfig.consensusCheckpointSyncURL,
4864
nodeRole: <EthNodeRole> "rpc-node",
4965
instanceType: config.rpcNodeConfig.instanceType,
5066
instanceCpuType: config.rpcNodeConfig.instanceCpuType,
5167
numberOfNodes: config.rpcNodeConfig.numberOfNodes,
5268
albHealthCheckGracePeriodMin: config.rpcNodeConfig.albHealthCheckGracePeriodMin,
5369
heartBeatDelayMin: config.rpcNodeConfig.heartBeatDelayMin,
54-
dataVolumes: config.syncNodeConfig.dataVolumes,
70+
dataVolume: config.syncNodeConfig.dataVolumes[0],
5571
});
5672

5773

lib/ethereum/lib/assets/copy-data-to-s3.sh

Lines changed: 0 additions & 11 deletions
This file was deleted.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[cfn-auto-reloader-hook]
2+
triggers=post.update
3+
path=Resources.WebServerHost.Metadata.AWS::CloudFormation::Init
4+
action=/opt/aws/bin/cfn-init -v --stack __AWS_STACK_NAME__ --resource WebServerHost --region __AWS_REGION__
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[main]
2+
stack=__AWS_STACK_ID__
3+
region=__AWS_REGION__
4+
# The interval used to check for changes to the resource metadata in minutes. Default is 15
5+
interval=2
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[Unit]
2+
Description=cfn-hup daemon
3+
[Service]
4+
Type=simple
5+
ExecStart=/usr/local/bin/cfn-hup
6+
Restart=always
7+
[Install]
8+
WantedBy=multi-user.target
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
#!/bin/bash
2+
3+
if [ -n "$1" ]; then
4+
export STACK_ID=$1
5+
else
6+
echo "Error: No Stack ID is provided"
7+
echo "Usage: instance/cfn-hup/setup.sh <stack_id> <aws_region>"
8+
exit 1
9+
fi
10+
11+
if [ -n "$2" ]; then
12+
export AWS_REGION=$2
13+
else
14+
echo "Error: No AWS Region is provided"
15+
echo "Usage: instance/cfn-hup/setup.sh <stack_id> <aws_region>"
16+
exit 1
17+
fi
18+
19+
echo "Install CloudFormation helper scripts"
20+
mkdir -p /opt/aws/
21+
pip3 install --break-system-packages https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz
22+
ln -s /usr/local/init/ubuntu/cfn-hup /etc/init.d/cfn-hup
23+
24+
echo "Configuring CloudFormation helper scripts"
25+
mkdir -p /etc/cfn/
26+
mv /opt/instance/cfn-hup/cfn-hup.conf /etc/cfn/cfn-hup.conf
27+
sed -i "s;__AWS_STACK_ID__;\"$STACK_ID\";g" /etc/cfn/cfn-hup.conf
28+
sed -i "s;__AWS_REGION__;\"$AWS_REGION\";g" /etc/cfn/cfn-hup.conf
29+
30+
mkdir -p /etc/cfn/hooks.d/system
31+
mv /opt/instance/cfn-hup/cfn-auto-reloader.conf /etc/cfn/hooks.d/cfn-auto-reloader.conf
32+
sed -i "s;__AWS_STACK_NAME__;\"$STACK_NAME\";g" /etc/cfn/hooks.d/cfn-auto-reloader.conf
33+
sed -i "s;__AWS_REGION__;\"$AWS_REGION\";g" /etc/cfn/hooks.d/cfn-auto-reloader.conf
34+
35+
echo "Starting CloudFormation helper scripts as a service"
36+
mv /opt/instance/cfn-hup/cfn-hup.service /etc/systemd/system/cfn-hup.service
37+
38+
systemctl daemon-reload
39+
systemctl enable --now cfn-hup
40+
systemctl start cfn-hup.service
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
#!/bin/bash
22

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

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

1010
s5cmd --log error cp --exclude 'lost+found' $SNAPSHOT_S3_PATH/data/* /data && \
11-
chown -R ethereum:ethereum /data && \
11+
chown -R bcuser:bcuser /data && \
1212
echo "Sync finished at " $(date) && \
1313
echo "$(($SECONDS / 60)) minutes and $(($SECONDS % 60)) seconds elapsed." && \
14-
su ethereum && \
15-
/usr/local/bin/docker-compose -f /home/ethereum/docker-compose.yml up -d && \
14+
su bcuser && \
15+
docker compose -f /home/bcuser/docker-compose.yml up -d && \
1616
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 || \
1717
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
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
set +e
3+
source /etc/cdk_environment
4+
5+
/usr/local/bin/docker-compose -f /home/bcuser/docker-compose.yml down
6+
echo "Sync started at " $(date)
7+
s5cmd --log error sync /data $SNAPSHOT_S3_PATH/
8+
echo "Sync finished at " $(date)
9+
sudo touch /data/snapshotted
10+
sudo su bcuser
11+
docker compose -f /home/bcuser/docker-compose.yml up -d
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/bin/bash
2+
3+
make_fs () {
4+
# If file system = to ext4 use mkfs.ext4, if xfs use mkfs.xfs
5+
if [ -z "$1" ]; then
6+
echo "Error: No file system type provided."
7+
echo "Usage: make_fs <file system type [ xfs | ext4 ]> <target_volume_id>"
8+
exit 1
9+
fi
10+
11+
if [ -z "$2" ]; then
12+
echo "Error: No target volume ID provided."
13+
echo "Usage: make_fs <file system type [ xfs | ext4 ]> <target_volume_id>"
14+
exit 1
15+
fi
16+
17+
local file_system=$1
18+
local volume_id=$2
19+
if [ "$file_system" == "ext4" ]; then
20+
mkfs -t ext4 "$volume_id"
21+
return "$?"
22+
else
23+
mkfs.xfs -f "$volume_id"
24+
return "$?"
25+
fi
26+
}
27+
28+
# We need an nvme disk that is not mounted and not partitioned
29+
get_all_empty_nvme_disks () {
30+
local all_not_mounted_nvme_disks
31+
local all_mounted_nvme_partitions
32+
local unmounted_nvme_disks=()
33+
local sorted_unmounted_nvme_disks
34+
35+
#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)
36+
all_not_mounted_nvme_disks=$(lsblk -lnb | awk '{if ($7 == "" && $4 > 100000000) {print $1}}' | grep nvme)
37+
all_mounted_nvme_partitions=$(mount | awk '{print $1}' | grep /dev/nvme)
38+
for disk in ${all_not_mounted_nvme_disks[*]}; do
39+
if [[ ! "${all_mounted_nvme_partitions[*]}" =~ $disk ]]; then
40+
unmounted_nvme_disks+=("$disk")
41+
fi
42+
done
43+
# Sort the array
44+
sorted_unmounted_nvme_disks=($(printf '%s\n' "${unmounted_nvme_disks[*]}" | sort))
45+
echo "${sorted_unmounted_nvme_disks[*]}"
46+
}
47+
48+
get_next_empty_nvme_disk () {
49+
local sorted_unmounted_nvme_disks
50+
sorted_unmounted_nvme_disks=($(get_all_empty_nvme_disks))
51+
# Return the first unmounted nvme disk
52+
echo "/dev/${sorted_unmounted_nvme_disks[0]}"
53+
}
54+
55+
# Add input as command line parameters for name of the directory to mount
56+
if [ -n "$1" ]; then
57+
DIR_NAME=$1
58+
else
59+
echo "Error: No data file system mount path is provided."
60+
echo "Usage: instance/storage/setup.sh <file_system_mount_path> <file_system_type [ xfs | ext4 ]> <target_volume_size_in_bytes> "
61+
echo "Default file system type is ext4"
62+
echo "If you skip <target_volume_size_in_bytes>, script will try to use the first unformatted volume ID."
63+
echo "Usage example: instance/storage/setup.sh /data ext4 300000000000000"
64+
exit 1
65+
fi
66+
67+
# Case input for $2 between ext4 and xfs, use ext4 as default
68+
case $2 in
69+
ext4)
70+
echo "File system set to ext4"
71+
FILE_SYSTEM="ext4"
72+
FS_CONFIG="defaults"
73+
;;
74+
xfs)
75+
echo "File system set to xfs"
76+
FILE_SYSTEM="xfs"
77+
FS_CONFIG="noatime,nodiratime,nodiscard" # See more: https://cdrdv2-public.intel.com/686417/rocksdb-benchmark-tuning-guide-on-xeon.pdf
78+
;;
79+
*)
80+
echo "File system set to ext4"
81+
FILE_SYSTEM="ext4"
82+
FS_CONFIG="defaults"
83+
;;
84+
esac
85+
86+
if [ -n "$3" ]; then
87+
VOLUME_SIZE=$3
88+
else
89+
echo "The size of volume for $DIR_NAME is not specified. Will try to guess volume ID."
90+
fi
91+
92+
echo "Checking if $DIR_NAME is mounted, and dont do anything if it is"
93+
if [ $(df --output=target | grep -c "$DIR_NAME") -lt 1 ]; then
94+
95+
if [ -n "$VOLUME_SIZE" ]; then
96+
VOLUME_ID=/dev/$(lsblk -lnb | awk -v VOLUME_SIZE_BYTES="$VOLUME_SIZE" '{if ($4== VOLUME_SIZE_BYTES) {print $1}}')
97+
echo "Data volume size defined, use respective volume id: $VOLUME_ID"
98+
else
99+
VOLUME_ID=$(get_next_empty_nvme_disk)
100+
echo "Data volume size undefined, trying volume id: $VOLUME_ID"
101+
fi
102+
103+
make_fs $FILE_SYSTEM "$VOLUME_ID"
104+
105+
sleep 10
106+
VOLUME_UUID=$(lsblk -fn -o UUID "$VOLUME_ID")
107+
VOLUME_FSTAB_CONF="UUID=$VOLUME_UUID $DIR_NAME $FILE_SYSTEM $FS_CONFIG 0 2"
108+
echo "VOLUME_ID=$VOLUME_ID"
109+
echo "VOLUME_UUID=$VOLUME_UUID"
110+
echo "VOLUME_FSTAB_CONF=$VOLUME_FSTAB_CONF"
111+
112+
# Check if data disc is already in fstab and replace the line if it is with the new disc UUID
113+
echo "Checking fstab for volume $DIR_NAME"
114+
if [ $(grep -c "$DIR_NAME" /etc/fstab) -gt 0 ]; then
115+
SED_REPLACEMENT_STRING="$(grep -n "$DIR_NAME" /etc/fstab | cut -d: -f1)s#.*#$VOLUME_FSTAB_CONF#"
116+
# if file exists, delete it
117+
if [ -f /etc/fstab.bak ]; then
118+
rm /etc/fstab.bak
119+
fi
120+
cp /etc/fstab /etc/fstab.bak
121+
sed -i "$SED_REPLACEMENT_STRING" /etc/fstab
122+
else
123+
echo "$VOLUME_FSTAB_CONF" | tee -a /etc/fstab
124+
fi
125+
126+
mount -a
127+
chown -R bcuser:bcuser "$DIR_NAME"
128+
else
129+
echo "$DIR_NAME volume is mounted, nothing changed"
130+
fi

0 commit comments

Comments
 (0)