In the first part of this series, we explored the challenges of multi-developer decentralized application (dApp) development and how Continuous Integration and Continuous Deployment (CI/CD) can address them. We also provided a high-level overview of the AWS services involved. This post dives into the implementation details, walking you through the AWS Cloud Development Kit (CDK) stack that installs and configures these services to create a fully functional pipeline.
Core Architecture and AWS CDK Setup
The solution builds upon the architecture introduced in Part 1. The complete AWS CDK code is available in a public GitHub repository. Deploying this stack into your AWS account automatically provisions all the necessary services and initiates the CI/CD pipeline.
The CDK project is organized for clarity and maintainability:
- The main stack constructs are defined in
cdk-stack.ts. - Three folders (
DataSyncTaskExec,ECSTaskExec,EFSManagement) contain Lambda function code supporting the CDK custom resources. - Configuration files and .zip archives are stored in the
resourcesfolder. - The
ShareToWin-dAppdirectory holds a sample dApp used as the foundation for the pipeline.
Setting Up the Ethereum Development Network with Hyperledger Besu
An Ethereum development network is essential for testing and debugging, providing an unlimited supply of test Ether. To support a team of developers, this network must be hosted on accessible infrastructure. This solution uses Hyperledger Besu as the development network, running on Amazon Elastic Container Service (Amazon ECS) with Amazon Elastic File System (Amazon EFS) as the persistent container volume.
The CDK stack creates an ECS cluster and a Fargate task definition to run the Besu container. The task definition is configured with significant CPU and memory resources to ensure smooth operation and uses EFS for storage, ensuring ledger data persists across container restarts.
Besu requires two configuration files at startup: dev.json (the genesis file) and config.toml. These files are copied from an Amazon S3 bucket to the EFS volume using AWS DataSync.
The provided dev.json genesis file pre-configures 10 test Ethereum addresses derived from a Hierarchical Deterministic (HD) wallet using a specific mnemonic phrase. For learning purposes, the CDK project includes a utility to generate a new mnemonic and accounts.
Important Security Note: The mnemonic and associated keys provided in the sample are for development and testing only. They must never be used in a production environment or where real value is involved.
Managing Sensitive Secrets with AWS Secrets Manager
A CI/CD pipeline requires secure handling of sensitive information like Infura billing tokens, mnemonic phrases for testnets/mainnet, and IAM access keys. This solution uses AWS Secrets Manager to store these secrets securely.
The CDK stack creates a Secrets Manager construct with placeholders for these values. Only the development network mnemonic is provided by default; other secrets must be populated by the user after stack deployment. This ensures sensitive production keys are not hardcoded.
Centralized Storage with Amazon S3
The stack creates an S3 bucket (amb-cicd-blog-s3bucket) and populates it with crucial files from the CDK project's resources/BucketFiles folder. These files include:
BlockchainDevLayer.zip: A Lambda layer for dApp dependencies.config.toml&dev.json: Besu node configuration files.deploy.js: A script for deploying smart contracts.hardhat.config.js: Hardhat configuration defining blockchain networks.ShareToWinLambda.zip: Source code for the dApp's supporting Lambda function.
Orchestrating Setup with Custom Resources
The CDK stack uses three custom resource providers, backed by Lambda functions, to configure the Besu network correctly during stack creation/update.
- EFS Folder Creation: Lambda functions create the required
configanddatadirectory structure on the EFS volume after it's provisioned. - S3 to EFS File Sync: A custom resource triggers a DataSync task to copy the essential
config.tomlanddev.jsonfiles from S3 to the EFS volume before the Besu container starts. - ECS Task Execution: Another custom resource is responsible for starting the Fargate task that runs the Besu node. The task's public IP address is exported as a stack output and used by other constructs.
The dApp Code Repository
The pipeline is triggered by changes to a code repository. The sample CDK stack creates an AWS CodeCommit repository and initializes it with a sample dApp. This dApp includes a smart contract (AssetToken.sol) and a Lambda function (index.mjs) that interacts with it.
Developers can work locally using their preferred IDE tools and extensions. They only need to push their unit-tested Solidity files (.sol) to this repository. They must also declare any npm dependencies in the project.json file. The CI/CD pipeline automatically handles dependency injection, compilation, testing, and deployment. 👉 Explore more strategies for team-based dApp development
Automated Build and Deployment with AWS CodeBuild
The heart of the pipeline is two AWS CodeBuild projects:
- One for building and deploying to the Besu development network.
- Another for deploying to the Goerli testnet via Amazon Managed Blockchain.
These projects use buildspec files (besubuildspec.yml and goerlibuildspec.yml) to define the build steps. The process involves:
- Fetching Code: Pulling the latest smart contract code from CodeCommit.
- Installing Dependencies: Installing npm packages for the contract and Hardhat.
- Configuration: Downloading the
hardhat.config.jsanddeploy.jsfiles from S3. - Compilation & Deployment: Using Hardhat to compile the Solidity code and deploy it to the target network (Besu or Goerli).
- Testing: Running the project's test scripts. A failure here fails the entire pipeline.
- dApp Deployment: Upon successful tests, the pipeline packages the smart contract's ABI and the Lambda code into a new ZIP file, deploys it, and updates the Lambda's environment variable with the new contract address.
The pipeline includes a manual approval stage before the Goerli deployment. This is a crucial gate to ensure that secrets for Managed Blockchain (billing token) and Goerli (mnemonic) in Secrets Manager are correctly configured before proceeding to a public testnet.
Frequently Asked Questions
What is the advantage of using a CI/CD pipeline for smart contract development?
A CI/CD pipeline automates the testing and deployment process, significantly reducing human error. It ensures every change is consistently compiled, tested on a development network, and only promoted to testnets after passing all checks and manual approval, enhancing security and reliability.
Can I use Truffle or Foundry instead of Hardhat in this pipeline?
Yes, absolutely. The choice of Hardhat is a design preference in this sample. The CodeBuild project's buildspec commands can be modified to use Truffle or Foundry for compilation, testing, and deployment, making the pipeline framework-agnostic.
How are private keys and mnemonics kept secure in this setup?
Sensitive information is never hardcoded. All secrets, including mnemonics and access keys, are stored securely in AWS Secrets Manager. The CodeBuild projects and Lambda functions retrieve these secrets at runtime using IAM roles with minimal necessary permissions.
What is the purpose of the manual approval stage?
The manual approval stage acts as a final check before deploying to a public testnet like Goerli. It ensures that all configurations are correct and that the team is ready to deploy a new version, preventing accidental or premature deployments.
How does this setup handle multiple developers working on different contracts?
The pipeline is triggered by commits to the main code repository. Developers can work on isolated features or different contracts in branches. Using Git workflows like pull requests, teams can review code before it is merged into the main branch, which then triggers the automated pipeline for integration testing and deployment.
Who is responsible for the cost of running these AWS services?
The account owner where the AWS CDK stack is deployed is responsible for all costs incurred by the services (e.g., ECS, EFS, CodeBuild, Secrets Manager). It is important to monitor usage and clean up resources when not in use to avoid unexpected charges.
Conclusion
This post provided a detailed walkthrough of implementing a CI/CD pipeline for Ethereum smart contract development on AWS. We covered the core architecture, the setup of a shared Hyperledger Besu development network, secure secret management, and the automated build and deployment process using AWS CodeBuild and CodePipeline.
The entire infrastructure is defined as code using the AWS CDK, making it reproducible, versionable, and easy to share. This automated approach streamlines development workflows, enforces best practices, and significantly improves the security and reliability of the smart contract deployment process.