Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
32 changes: 23 additions & 9 deletions lib/constructs/single-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,29 @@ export class SingleNodeConstruct extends cdkContructs.Construct {
throw new Error(`Number of data volumes can't be more than 6, current number: ${dataVolumeIndex}`);
}
if (dataVolume.type !== constants.InstanceStoreageDeviceVolumeType) {
const newDataVolume = new ec2.Volume(this, `data-volume-${dataVolumeIndex}`, {
availabilityZone: availabilityZone,
size: cdk.Size.gibibytes(dataVolume.sizeGiB),
volumeType: ec2.EbsDeviceVolumeType[dataVolume.type.toUpperCase() as keyof typeof ec2.EbsDeviceVolumeType],
encrypted: true,
iops: dataVolume.iops,
throughput: dataVolume.throughput,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
let newDataVolume: ec2.Volume;

if (dataVolume.type === ec2.EbsDeviceVolumeType.GP3) {
newDataVolume = new ec2.Volume(this, `data-volume-${dataVolumeIndex}`, {
availabilityZone: availabilityZone,
size: cdk.Size.gibibytes(dataVolume.sizeGiB),
volumeType: ec2.EbsDeviceVolumeType[dataVolume.type.toUpperCase() as keyof typeof ec2.EbsDeviceVolumeType],
encrypted: true,
iops: dataVolume.iops,
throughput: dataVolume.throughput,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
} else {
newDataVolume = new ec2.Volume(this, `data-volume-${dataVolumeIndex}`, {
availabilityZone: availabilityZone,
size: cdk.Size.gibibytes(dataVolume.sizeGiB),
volumeType: ec2.EbsDeviceVolumeType[dataVolume.type.toUpperCase() as keyof typeof ec2.EbsDeviceVolumeType],
encrypted: true,
iops: dataVolume.iops,
removalPolicy: cdk.RemovalPolicy.DESTROY,
});
}


new ec2.CfnVolumeAttachment(this, `data-volume${dataVolumeIndex}-attachment`, {
// Device naming according to https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/device_naming.html
Expand Down
54 changes: 46 additions & 8 deletions lib/ethereum/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ We will use AWS Cloud9 to execute the subsequent commands. Follow the instructio

**NOTE:** In this tutorial we will set all major configuration through environment variables, but you also can modify parameters in `config/config.ts`.

### Deploy Sync Node
### Prepare to deploy nodes

1. Make sure you are in the root directory of the cloned repository

Expand All @@ -75,6 +75,8 @@ We will use AWS Cloud9 to execute the subsequent commands. Follow the instructio

**NOTE:** You may see the following error if the default VPC already exists: `An error occurred (DefaultVpcAlreadyExists) when calling the CreateDefaultVpc operation: A Default VPC already exists for this account in this region.`. That means you can just continue with the following steps.

**NOTE:** The default VPC must have at least two public subnets in different Availability Zones, and public subnet must set `Auto-assign public IPv4 address` to `YES`

3. Configure your setup

Create your own copy of `.env` file and edit it:
Expand All @@ -96,7 +98,42 @@ Create your own copy of `.env` file and edit it:
npx cdk deploy eth-common
```

5. Deploy Sync Node
### Option 1: Single RPC Node

1. Deploy Single RPC Node

```bash
pwd
# Make sure you are in aws-blockchain-node-runners/lib/ethereum
npx cdk deploy eth-single-node --json --outputs-file single-node-deploy.json
```
**NOTE:** The default VPC must have at least two public subnets in different Availability Zones, and public subnet must set `Auto-assign public IPv4 address` to `YES`

2. After starting the node you need to wait for the inital syncronization process to finish. It may take from half a day to about 6-10 days depending on the client combination and the state of the network. You can use Amazon CloudWatch to track the progress. There is a script that publishes CloudWatch metrics every 5 minutes, where you can watch `sync distance` for consensus client and `blocks behind` for execution client. When the node is fully synced those two metrics shold show 0. To see them:

- Navigate to [CloudWatch service](https://console.aws.amazon.com/cloudwatch/) (make sure you are in the region you have specified for `AWS_REGION`)
- Open `Dashboards` and select `eth-sync-node-<your-eth-client-combination>` from the list of dashboards.

4. Once the initial synchronization is done, you should be able to access the RPC API of that node from within the same VPC. The RPC port is not exposed to the Internet. Tun the following query against the private IP of the single RPC node you deployed:

```bash
INSTANCE_ID=$(cat single-node-deploy.json | jq -r '..|.node-instance-id? | select(. != null)')
NODE_INTERNAL_IP=$(aws ec2 describe-instances --instance-ids $INSTANCE_ID --query 'Reservations[*].Instances[*].PrivateIpAddress' --output text)

# We query token balance of Beacon deposit contract: https://etherscan.io/address/0x00000000219ab540356cbb839cbe05303d7705fa
curl http://$NODE_INTERNAL_IP:8545 -X POST -H "Content-Type: application/json" \
--data '{"method":"eth_getBalance","params":["0x00000000219ab540356cBB839Cbe05303d7705Fa", "latest"],"id":1,"jsonrpc":"2.0"}'
```

The result should be like this (the actual balance might change):

```javascript
{"jsonrpc":"2.0","id":1,"result":"0xe791d050f91d9949d344d"}
```

### Option 2: Deploy the Highly Available RPC Nodes

1. Deploy Sync Node

```bash
pwd
Expand All @@ -105,7 +142,7 @@ Create your own copy of `.env` file and edit it:
```
**NOTE:** The default VPC must have at least two public subnets in different Availability Zones, and public subnet must set `Auto-assign public IPv4 address` to `YES`

6. After starting the node you need to wait for the inital syncronization process to finish. It may take from half a day to about 6-10 days depending on the client combination and the state of the network. You can use Amazon CloudWatch to track the progress. There is a script that publishes CloudWatch metrics every 5 minutes, where you can watch `sync distance` for consensus client and `blocks behind` for execution client. When the node is fully synced those two metrics shold show 0. To see them:
2. After starting the node you need to wait for the inital syncronization process to finish. It may take from half a day to about 6-10 days depending on the client combination and the state of the network. You can use Amazon CloudWatch to track the progress. There is a script that publishes CloudWatch metrics every 5 minutes, where you can watch `sync distance` for consensus client and `blocks behind` for execution client. When the node is fully synced those two metrics shold show 0. To see them:

- Navigate to [CloudWatch service](https://console.aws.amazon.com/cloudwatch/) (make sure you are in the region you have specified for `AWS_REGION`)
- Open `Dashboards` and select `eth-sync-node-<your-eth-client-combination>` from the list of dashboards.
Expand All @@ -114,17 +151,15 @@ Once synchronization process is over, the script will automatically stop both cl

Note: the snapshot backup process will automatically run ever day at midnight time of the time zone were the sync node runs. To change the schedule, modify `crontab` of the root user on the node's EC2 instance.

### Deploy the RPC Nodes

1. Configure and deploy 2 RPC Nodes
3. Configure and deploy 2 RPC Nodes

```bash
pwd
# Make sure you are in aws-blockchain-node-runners/lib/ethereum
npx cdk deploy eth-rpc-nodes --json --outputs-file rpc-node-deploy.json
```

2. Give the new RPC nodes about 30 minutes (up to 2 hours for Erigon) to initialize and then run the following query against the load balancer behind the RPC node created
4. Give the new RPC nodes about 30 minutes (up to 2 hours for Erigon) to initialize and then run the following query against the load balancer behind the RPC node created

```bash
export ETH_RPC_ABL_URL=$(cat rpc-node-deploy.json | jq -r '..|.alburl? | select(. != null)')
Expand All @@ -151,7 +186,7 @@ The result should be like this (the actual balance might change):
</body>
```

**NOTE:** By default and for security reasons the load balancer is available only from wihtin the default VPC in the region where it is deployed. It is not available from the Internet and is not open for external connections. Before opening it up please make sure you protect your RPC APIs.
**NOTE:** By default and for security reasons the load balancer is available only from within the default VPC in the region where it is deployed. It is not available from the Internet and is not open for external connections. Before opening it up please make sure you protect your RPC APIs.

### Clearing up and undeploying everything

Expand All @@ -165,6 +200,9 @@ The result should be like this (the actual balance might change):
pwd
# Make sure you are in aws-blockchain-node-runners/lib/ethereum

# Undeploy Single RPC Node
cdk destroy eth-single-node

# Undeploy RPC Nodes
cdk destroy eth-rpc-nodes

Expand Down
18 changes: 16 additions & 2 deletions lib/ethereum/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ 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 { EthSyncNodeStack } from "./lib/sync-node-stack";
import { EthSingleNodeStack } from "./lib/single-node-stack";
import { EthCommonStack } from "./lib/common-stack";
import { EthRpcNodesStack } from "./lib/rpc-nodes-stack";

Expand All @@ -17,11 +18,23 @@ new EthCommonStack(app, "eth-common", {
stackName: `eth-nodes-common`,
});

new EthSyncNodeStack(app, "eth-sync-node", {
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,
nodeRole: <EthNodeRole> "sync-node",
instanceType: config.syncNodeConfig.instanceType,
instanceCpuType: config.syncNodeConfig.instanceCpuType,
dataVolumes: config.syncNodeConfig.dataVolumes,
});

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,
nodeRole: <EthNodeRole> "single-node",
instanceType: config.syncNodeConfig.instanceType,
instanceCpuType: config.syncNodeConfig.instanceCpuType,
dataVolumes: config.syncNodeConfig.dataVolumes,
Expand All @@ -32,6 +45,7 @@ new EthRpcNodesStack(app, "eth-rpc-nodes", {

env: { account: config.baseConfig.accountId, region: config.baseConfig.region },
ethClientCombination: config.baseConfig.clientCombination,
nodeRole: <EthNodeRole> "rpc-node",
instanceType: config.rpcNodeConfig.instanceType,
instanceCpuType: config.rpcNodeConfig.instanceCpuType,
numberOfNodes: config.rpcNodeConfig.numberOfNodes,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:
besu_node:
environment:
- "JAVA_OPTS=-Xmx8g"
image: hyperledger/besu:23.4.5-SNAPSHOT
image: hyperledger/besu:24.1.2-SNAPSHOT
container_name: execution
restart: always
command:
Expand Down Expand Up @@ -43,7 +43,7 @@ services:
environment:
- "JAVA_OPTS=-Xmx4g"
- "TEKU_OPTS=-XX:-HeapDumpOnOutOfMemoryError"
image: consensys/teku:23.6.1-jdk16
image: consensys/teku:24.1.0-jdk17
container_name: consensus
restart: always
command:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
version: "3"
services:
erigon_node:
image: thorax/erigon:2.48.1-__IMAGE_ARCH__
image: thorax/erigon:2.57.0-__IMAGE_ARCH__
container_name: execution
restart: always
command:
Expand Down Expand Up @@ -43,7 +43,7 @@ services:
- "30303:30303/udp"

lighthouse_node:
image: sigp/lighthouse:v4.3.0
image: sigp/lighthouse:v4.5.0
container_name: consensus
restart: always
command:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
version: "3"
services:
erigon_node:
image: thorax/erigon:2.48.1-__IMAGE_ARCH__
image: thorax/erigon:2.57.0-__IMAGE_ARCH__
container_name: execution
restart: always
command:
Expand Down Expand Up @@ -43,7 +43,7 @@ services:
- "30303:30303/udp"

prysm_node:
image: rocketpool/prysm:v4.0.6
image: rocketpool/prysm:v4.2.0
container_name: consensus
restart: always
command:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
version: "3"
services:
geth_node:
image: ethereum/client-go:v1.11.6
image: ethereum/client-go:v1.13.10
container_name: execution
restart: always
command:
Expand Down Expand Up @@ -38,7 +38,7 @@ services:
- "30303:30303/udp"

lighthouse_node:
image: sigp/lighthouse:v4.3.0
image: sigp/lighthouse:v4.5.0
container_name: consensus
restart: always
command:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:
nethermind_node:
environment:
- "DOTNET_BUNDLE_EXTRACT_BASE_DIR=/var/lib/nethermind/data"
image: nethermind/nethermind:1.19.3
image: nethermind/nethermind:1.25.2
container_name: execution
restart: always
command: [
Expand Down Expand Up @@ -54,7 +54,7 @@ services:
environment:
- "JAVA_OPTS=-Xmx4g"
- "TEKU_OPTS=-XX:-HeapDumpOnOutOfMemoryError"
image: consensys/teku:23.6.1-jdk16
image: consensys/teku:24.1.0-jdk17
container_name: consensus
restart: always
command:
Expand Down
14 changes: 7 additions & 7 deletions lib/ethereum/lib/assets/user-data/node.sh
Original file line number Diff line number Diff line change
Expand Up @@ -193,18 +193,18 @@ if [ "$NODE_ROLE" == "sync-node" ]; then
chmod 766 /opt/copy-data-to-s3.sh
fi

if [ "$NODE_ROLE" == "sync-node" ]; then
echo "Sync node. Signaling completion to CloudFormation"
if [ "$NODE_ROLE" == "sync-node" ] || [ "$NODE_ROLE" == "single-node" ]; then
echo "Single node. Signaling completion to CloudFormation"
/opt/aws/bin/cfn-signal --stack $STACK_NAME --resource $RESOURCE_ID --region $REGION
fi

echo "Preparing data volume"

if [ "$NODE_ROLE" == "sync-node" ]; then
if [ "$NODE_ROLE" == "sync-node" ] || [ "$NODE_ROLE" == "single-node" ]; then
# echo "Sync node. Wait for the volume to be attached"
# aws ec2 wait volume-available --volume-ids $DATA_VOLUME_ID --region $REGION

echo "Sync node. Wait for one minute for the volume to be available"
echo "Single node. Wait for one minute for the volume to be available"
sleep 60
fi

Expand Down Expand Up @@ -242,11 +242,11 @@ mkdir -p /data/consensus/data
chown -R ethereum:ethereum /data
chmod -R 755 /data

if [ "$NODE_ROLE" == "sync-node" ]; then
if [ "$NODE_ROLE" == "sync-node" ] || [ "$NODE_ROLE" == "single-node" ]; then
if [ "$AUTOSTART_CONTAINER" == "false" ]; then
echo "Sync node. Autostart disabled. Start docker-compose manually!"
echo "Single node. Autostart disabled. Start docker-compose manually!"
else
echo "Sync node. Autostart enabled. Starting docker-compose in 3 min."
echo "Single node. Autostart enabled. Starting docker-compose in 3 min."
echo "sudo su ethereum && /usr/local/bin/docker-compose -f /home/ethereum/docker-compose.yml up -d" | at now +3 minutes
fi
fi
Expand Down
3 changes: 2 additions & 1 deletion lib/ethereum/lib/common-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ export interface EthCommonStackProps extends cdk.StackProps {}
export class EthCommonStack extends cdk.Stack {
AWS_STACKNAME = cdk.Stack.of(this).stackName;
AWS_ACCOUNT_ID = cdk.Stack.of(this).account;
AWS_REGION = cdk.Stack.of(this).region

constructor(scope: cdkConstructs.Construct, id: string, props: EthCommonStackProps) {
super(scope, id, props);
Expand All @@ -23,7 +24,7 @@ export class EthCommonStack extends cdk.Stack {
});

const snapshotsBucket = new SnapshotsS3BucketConstruct(this, `snapshots-s3-bucket`, {
bucketName: `eth-snapshots-${this.AWS_ACCOUNT_ID}-${this.AWS_STACKNAME}`,
bucketName: `eth-snapshots-${this.AWS_STACKNAME}-${this.AWS_ACCOUNT_ID}-${this.AWS_REGION}`,
});

const s3VPCEndpoint = vpc.addGatewayEndpoint("s3-vpc-endpoint", {
Expand Down
2 changes: 2 additions & 0 deletions lib/ethereum/lib/config/ethConfig.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import * as configTypes from "../../../constructs/config.interface";

export type EthClientCombination = "besu-teku" | "geth-lighthouse" | "erigon-lighthouse" | "erigon-prysm" | "nethermind-teku";

export type EthNodeRole = "sync-node" | "rpc-node" | "single-node";

export interface EthDataVolumeConfig extends configTypes.DataVolumeConfig {
}

Expand Down
10 changes: 6 additions & 4 deletions lib/ethereum/lib/rpc-nodes-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,16 @@ import * as s3Assets from "aws-cdk-lib/aws-s3-assets";
import * as nag from "cdk-nag";
import * as path from "path";
import * as fs from "fs";
import * as config from "./config/ethConfig.interface";
import * as configTypes from "./config/ethConfig.interface";
import { EthNodeSecurityGroupConstruct } from "./constructs/eth-node-security-group"
import { HANodesConstruct } from "../../constructs/ha-rpc-nodes-with-alb"

export interface EthRpcNodesStackProps extends cdk.StackProps {
ethClientCombination: config.EthClientCombination;
ethClientCombination: configTypes.EthClientCombination;
nodeRole: configTypes.EthNodeRole;
instanceType: ec2.InstanceType;
instanceCpuType: ec2.AmazonLinuxCpuType;
dataVolumes: config.EthDataVolumeConfig[],
dataVolumes: configTypes.EthDataVolumeConfig[],
numberOfNodes: number;
albHealthCheckGracePeriodMin: number;
heartBeatDelayMin: number;
Expand All @@ -34,6 +35,7 @@ export class EthRpcNodesStack extends cdk.Stack {
const {
instanceType,
ethClientCombination,
nodeRole,
instanceCpuType,
dataVolumes,
albHealthCheckGracePeriodMin,
Expand Down Expand Up @@ -71,7 +73,7 @@ export class EthRpcNodesStack extends cdk.Stack {
_ETH_CLIENT_COMBINATION_: ethClientCombination,
_STACK_NAME_: STACK_NAME,
_FORMAT_DISK_: "true",
_NODE_ROLE_:"rpc-node",
_NODE_ROLE_: nodeRole,
_AUTOSTART_CONTAINER_: "true",
_NODE_CF_LOGICAL_ID_: "",
_LIFECYCLE_HOOK_NAME_: lifecycleHookName,
Expand Down
Loading