diff --git a/Contributing/Overview.md b/Contributing/Overview.md new file mode 100644 index 000000000..b3843aa24 --- /dev/null +++ b/Contributing/Overview.md @@ -0,0 +1,81 @@ +# Contributing Guide + +Thank you for your interest in contributing to the Chainlink Testing Framework (CTF)! We welcome contributions from the community to improve the framework, add features, fix bugs, and enhance documentation. + +--- + +## How to Contribute + +1. **Fork the repository** and create your branch from `main`. +2. **Open an issue** to discuss your proposed change if it's non-trivial. +3. **Write clear, maintainable code** and add tests for new features or bug fixes. +4. **Document your changes** in the code and/or wiki as appropriate. +5. **Open a Pull Request (PR)** with a clear description of your changes and link to any relevant issues. +6. **Participate in code review** and address feedback promptly. + +--- + +## Code of Conduct + +We are committed to fostering a welcoming and inclusive environment. Please read and follow our [Code of Conduct](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/CODE_OF_CONDUCT.md). + +--- + +## Development Setup + +1. **Clone the repository** + ```bash + git clone https://github.com/smartcontractkit/chainlink-testing-framework.git + cd chainlink-testing-framework + ``` +2. **Install dependencies** + - [Go](https://go.dev/doc/install) 1.21+ + - [Docker](https://www.docker.com/) + - [direnv](https://direnv.net/) (optional, for environment management) + - [nix](https://nixos.org/manual/nix/stable/installation/installation.html) (optional, for dev environments) +3. **Set up your environment** + ```bash + cp Getting-Started/Installation.md . + # Follow the installation instructions for your OS + ``` +4. **Run tests locally** + ```bash + CTF_CONFIGS=smoke.toml go test -v + ``` +5. **Lint and format your code** + ```bash + go fmt ./... + go vet ./... + golangci-lint run + ``` + +--- + +## Pull Request Process + +1. **Open a draft PR** early to get feedback. +2. **Ensure all tests pass** and code is linted. +3. **Describe your changes** clearly in the PR description. +4. **Link to any related issues** (e.g., `Closes #123`). +5. **Request review** from maintainers or relevant code owners. +6. **Address review comments** and update your PR as needed. +7. **Wait for approval and merge** (maintainers will handle merging). + +--- + +## Getting Help + +- **Search issues**: [GitHub Issues](https://github.com/smartcontractkit/chainlink-testing-framework/issues) +- **Ask in discussions**: [GitHub Discussions](https://github.com/smartcontractkit/chainlink-testing-framework/discussions) +- **Open a new issue** for bugs, feature requests, or questions +- **Contact maintainers** via GitHub if you need further assistance + +--- + +## Resources +- [Home](../Home) +- [Installation Guide](../Getting-Started/Installation) +- [API Reference](../Reference/API-Reference) +- [Troubleshooting](../Reference/Troubleshooting) +- [Code of Conduct](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/CODE_OF_CONDUCT.md) +- [LICENSE](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/LICENSE) \ No newline at end of file diff --git a/Examples/Advanced-Examples.md b/Examples/Advanced-Examples.md new file mode 100644 index 000000000..43faba9aa --- /dev/null +++ b/Examples/Advanced-Examples.md @@ -0,0 +1,179 @@ +# Advanced Examples + +This page provides advanced and real-world test scenarios using the Chainlink Testing Framework (CTF). These examples demonstrate how to leverage CTF for complex, production-like workflows. + +--- + +## 1. Multi-Chain Integration Test + +Test a system that interacts with multiple blockchains (e.g., Ethereum and Solana) and verifies cross-chain data flow. + +```toml +[ethereum] + type = "geth" + image = "ethereum/client-go" + tag = "v1.12.0" + pull_image = true + +[solana] + type = "solana" + image = "solanalabs/solana" + tag = "v1.16.0" + pull_image = true + +[chainlink_node] + image = "public.ecr.aws/chainlink/chainlink" + tag = "2.7.0" + pull_image = true +``` + +```go +func TestMultiChainIntegration(t *testing.T) { + type Config struct { + Ethereum *blockchain.Input `toml:"ethereum" validate:"required"` + Solana *blockchain.Input `toml:"solana" validate:"required"` + ChainlinkNode *clnode.Input `toml:"chainlink_node" validate:"required"` + } + in, err := framework.Load[Config](t) + require.NoError(t, err) + eth, err := blockchain.NewBlockchainNetwork(in.Ethereum) + require.NoError(t, err) + sol, err := blockchain.NewBlockchainNetwork(in.Solana) + require.NoError(t, err) + cl, err := clnode.NewChainlinkNode(in.ChainlinkNode) + require.NoError(t, err) + // ... deploy contracts, set up jobs, verify cross-chain data +} +``` + +--- + +## 2. Upgrade and Migration Test + +Test that a Chainlink node or contract can be upgraded without breaking existing functionality. + +```toml +[chainlink_node] + image = "public.ecr.aws/chainlink/chainlink" + tag = "2.6.0" + pull_image = true +``` + +```go +func TestUpgrade(t *testing.T) { + // Deploy with old version + in, err := framework.Load[Config](t) + require.NoError(t, err) + cl, err := clnode.NewChainlinkNode(in.ChainlinkNode) + require.NoError(t, err) + // ... run smoke test + + // Upgrade node + in.ChainlinkNode.Tag = "2.7.0" + clUpgraded, err := clnode.NewChainlinkNode(in.ChainlinkNode) + require.NoError(t, err) + // ... verify data, jobs, and state are preserved +} +``` + +--- + +## 3. Chaos Engineering Test + +Inject failures and network issues to test system resilience using Havoc. + +```go +func TestChaosResilience(t *testing.T) { + in, err := framework.Load[Config](t) + require.NoError(t, err) + cl, err := clnode.NewChainlinkNode(in.ChainlinkNode) + require.NoError(t, err) + // Start chaos experiment + client, err := havoc.NewClient() + require.NoError(t, err) + chaos, err := havoc.NewChaos(client, createNetworkChaos()) + require.NoError(t, err) + err = chaos.Create(context.Background()) + require.NoError(t, err) + // ... run test logic while chaos is active + chaos.Delete(context.Background()) +} +``` + +--- + +## 4. Performance and Load Test + +Use WASP to generate load and measure system performance under stress. + +```go +func TestPerformance(t *testing.T) { + profile := wasp.NewProfile() + generator := wasp.NewGenerator(&wasp.Config{ + T: 60 * time.Second, + RPS: 100, + LoadType: wasp.RPS, + Schedule: wasp.Plain(100, 60*time.Second), + }) + generator.AddRequestFn(func(ctx context.Context) error { + // Simulate contract call or API request + return nil + }) + profile.Add(generator) + _, err := profile.Run(true) + require.NoError(t, err) +} +``` + +--- + +## 5. End-to-End Oracle Test + +Test the full workflow from contract deployment to job fulfillment and data reporting. + +```go +func TestOracleE2E(t *testing.T) { + in, err := framework.Load[Config](t) + require.NoError(t, err) + bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA) + require.NoError(t, err) + cl, err := clnode.NewChainlinkNode(in.ChainlinkNode) + require.NoError(t, err) + // Deploy oracle contract + // Register job on Chainlink node + // Send request and verify fulfillment +} +``` + +--- + +## 6. Real-World: Staging Environment Reuse + +Reuse cached components and substitute staging URLs for persistent environment testing. + +```toml +[blockchain_a] + type = "anvil" + use_cache = true + external_url = "https://staging-eth.example.com" + +[chainlink_node] + use_cache = true + external_url = "https://staging-cl.example.com" +``` + +```go +func TestStagingReuse(t *testing.T) { + in, err := framework.Load[Config](t) + require.NoError(t, err) + // Use staging URLs for integration tests +} +``` + +--- + +## More Examples +- [WASP Load Testing](../Libraries/WASP) +- [Havoc Chaos Testing](../Libraries/Havoc) +- [Seth Ethereum Client](../Libraries/Seth) +- [Framework Examples Directory](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/framework/examples/myproject) \ No newline at end of file diff --git a/Examples/Use-Cases.md b/Examples/Use-Cases.md new file mode 100644 index 000000000..301270c42 --- /dev/null +++ b/Examples/Use-Cases.md @@ -0,0 +1,107 @@ +# Real-World Use Cases + +This page highlights real-world scenarios where the Chainlink Testing Framework (CTF) is used to ensure reliability, performance, and security in production systems. + +--- + +## 1. Production-Grade End-to-End Testing + +**Scenario:** +- A Chainlink-integrated DeFi protocol needs to verify that price feeds, job fulfillment, and contract upgrades work as expected across multiple networks. + +**How CTF Helps:** +- Deploys ephemeral or persistent test environments that mirror production. +- Runs full workflows: contract deployment, job registration, data requests, and fulfillment. +- Validates on-chain and off-chain integration. + +--- + +## 2. CI/CD Pipeline Integration + +**Scenario:** +- Every pull request must pass a suite of integration, performance, and chaos tests before merging. + +**How CTF Helps:** +- Runs in GitHub Actions or other CI systems. +- Uses caching to speed up repeated test runs. +- Fails fast on regressions, configuration errors, or performance degradations. +- Exposes logs and metrics for debugging failed builds. + +--- + +## 3. Protocol Upgrade and Migration Testing + +**Scenario:** +- A new Chainlink node version or smart contract upgrade must be validated for backward compatibility and data integrity. + +**How CTF Helps:** +- Spins up old and new versions side-by-side. +- Runs upgrade and migration scripts. +- Verifies that jobs, data, and state persist across upgrades. +- Detects breaking changes before they reach production. + +--- + +## 4. Cross-Chain and Multi-Network Testing + +**Scenario:** +- A dApp or oracle service operates across Ethereum, Solana, and other chains, requiring cross-chain data flow validation. + +**How CTF Helps:** +- Deploys multiple blockchains in parallel. +- Simulates cross-chain requests and data propagation. +- Validates data consistency and latency across networks. + +--- + +## 5. Oracle Network Resilience and Chaos Engineering + +**Scenario:** +- The reliability of a Chainlink DON (Decentralized Oracle Network) must be tested under network partitions, node failures, and resource exhaustion. + +**How CTF Helps:** +- Uses Havoc to inject network latency, pod failures, and resource limits. +- Monitors system health and recovery. +- Validates that the DON continues to serve data or recovers gracefully. + +--- + +## 6. Performance Benchmarking and Load Testing + +**Scenario:** +- A new Chainlink job type or protocol feature must be benchmarked for throughput and latency under load. + +**How CTF Helps:** +- Uses WASP to generate synthetic or user-based load. +- Collects metrics via Prometheus and visualizes in Grafana. +- Identifies bottlenecks and regression points. + +--- + +## 7. Staging Environment Validation + +**Scenario:** +- Before a mainnet release, the team wants to validate the full stack in a persistent staging environment. + +**How CTF Helps:** +- Reuses cached components and substitutes staging URLs. +- Runs upgrade, chaos, and performance tests against the staging stack. +- Ensures production readiness with minimal manual intervention. + +--- + +## 8. Custom Component and Plugin Testing + +**Scenario:** +- A team develops a custom Chainlink external adapter or plugin and needs to validate it in a realistic environment. + +**How CTF Helps:** +- Easily adds custom components to the test config. +- Runs integration and chaos tests with the new plugin. +- Validates compatibility with existing Chainlink nodes and contracts. + +--- + +## More Use Cases +- [Chainlink Blog: Testing at Scale](https://blog.chain.link/) +- [Framework Examples Directory](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/framework/examples/myproject) \ No newline at end of file diff --git a/Getting-Started/First-Test.md b/Getting-Started/First-Test.md new file mode 100644 index 000000000..49d78c35c --- /dev/null +++ b/Getting-Started/First-Test.md @@ -0,0 +1,328 @@ +# Writing Your First Test + +This guide will walk you through creating your first test with the Chainlink Testing Framework (CTF). We'll start with a simple blockchain test and gradually build up to more complex scenarios. + +## Prerequisites + +Before starting, make sure you have: +- [Installed CTF](Installation) +- Docker running +- A basic understanding of Go testing + +## Basic Test Structure + +CTF tests follow a simple pattern: +1. **Configuration** - Define what components you need +2. **Loading** - Load configuration into your test +3. **Component Creation** - Create and start components +4. **Testing** - Write your test logic +5. **Cleanup** - Clean up resources (automatic with CTF) + +## Step 1: Create Configuration + +Create a `smoke.toml` file in your project root: + +```toml +[blockchain_a] + type = "anvil" +``` + +This configuration tells CTF to create an Anvil blockchain instance. Anvil is a fast Ethereum development node that's perfect for testing. + +## Step 2: Write Your Test + +Create `smoke_test.go`: + +```go +package mymodule_test + +import ( + "github.com/smartcontractkit/chainlink-testing-framework/framework" + "github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain" + "github.com/stretchr/testify/require" + "testing" +) + +// Config defines the structure of your configuration file +type Config struct { + BlockchainA *blockchain.Input `toml:"blockchain_a" validate:"required"` +} + +func TestSmoke(t *testing.T) { + // Load configuration from TOML file + in, err := framework.Load[Config](t) + require.NoError(t, err) + + // Create blockchain network + bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA) + require.NoError(t, err) + + // Your test logic goes here + t.Run("blockchain is running", func(t *testing.T) { + require.NotEmpty(t, bc.Nodes[0].ExternalHTTPUrl) + require.NotEmpty(t, bc.Nodes[0].InternalHTTPUrl) + }) + + t.Run("blockchain is accessible", func(t *testing.T) { + // Test that we can connect to the blockchain + client := bc.GetDefaultClient() + require.NotNil(t, client) + + // Get the latest block number + blockNumber, err := client.BlockNumber(context.Background()) + require.NoError(t, err) + require.GreaterOrEqual(t, blockNumber, uint64(0)) + }) +} +``` + +## Step 3: Run Your Test + +```bash +# Run the test +CTF_CONFIGS=smoke.toml go test -v -run TestSmoke + +# Clean up containers when done +ctf d rm +``` + +## Understanding the Test + +Let's break down what's happening: + +### Configuration Loading +```go +in, err := framework.Load[Config](t) +``` +This loads your `smoke.toml` file and validates it against the `Config` struct. The `validate:"required"` tag ensures the blockchain configuration is present. + +### Component Creation +```go +bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA) +``` +This creates a blockchain network based on your configuration. CTF will: +- Start a Docker container with Anvil +- Configure networking +- Wait for the service to be ready +- Return a network object with connection details + +### Test Logic +```go +t.Run("blockchain is running", func(t *testing.T) { + require.NotEmpty(t, bc.Nodes[0].ExternalHTTPUrl) +}) +``` +This tests that the blockchain is actually running and accessible. The `ExternalHTTPUrl` is the URL you can use to connect to the blockchain from your host machine. + +## Adding More Components + +Let's expand the test to include a Chainlink node: + +### Updated Configuration +```toml +[blockchain_a] + type = "anvil" + +[chainlink_node] + image = "public.ecr.aws/chainlink/chainlink" + tag = "2.7.0" + pull_image = true + node_config = """ + [Log] + Level = "info" + + [WebServer] + TLSListenPort = 0 + Port = 6688 + + [Database] + URL = "postgresql://postgres:password@postgres:5432/chainlink?sslmode=disable" + """ +``` + +### Updated Test +```go +package mymodule_test + +import ( + "context" + "github.com/smartcontractkit/chainlink-testing-framework/framework" + "github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain" + "github.com/smartcontractkit/chainlink-testing-framework/framework/components/clnode" + "github.com/stretchr/testify/require" + "testing" +) + +type Config struct { + BlockchainA *blockchain.Input `toml:"blockchain_a" validate:"required"` + ChainlinkNode *clnode.Input `toml:"chainlink_node" validate:"required"` +} + +func TestSmokeWithChainlink(t *testing.T) { + in, err := framework.Load[Config](t) + require.NoError(t, err) + + // Create blockchain network + bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA) + require.NoError(t, err) + + // Create Chainlink node + cl, err := clnode.NewChainlinkNode(in.ChainlinkNode) + require.NoError(t, err) + + t.Run("blockchain is running", func(t *testing.T) { + require.NotEmpty(t, bc.Nodes[0].ExternalHTTPUrl) + }) + + t.Run("chainlink node is running", func(t *testing.T) { + require.NotEmpty(t, cl.ExternalURL) + + // Test that the Chainlink API is accessible + resp, err := http.Get(cl.ExternalURL + "/health") + require.NoError(t, err) + require.Equal(t, http.StatusOK, resp.StatusCode) + }) + + t.Run("chainlink can connect to blockchain", func(t *testing.T) { + // This would typically involve setting up a job + // and verifying it can interact with the blockchain + require.NotEmpty(t, bc.Nodes[0].InternalHTTPUrl) + }) +} +``` + +## Test Patterns + +### Using Subtests +CTF encourages the use of subtests (`t.Run`) to organize your test logic: + +```go +func TestComprehensive(t *testing.T) { + // Setup + in, err := framework.Load[Config](t) + require.NoError(t, err) + + bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA) + require.NoError(t, err) + + // Test different aspects + t.Run("connectivity", func(t *testing.T) { + // Test network connectivity + }) + + t.Run("functionality", func(t *testing.T) { + // Test core functionality + }) + + t.Run("performance", func(t *testing.T) { + // Test performance characteristics + }) +} +``` + +### Parallel Testing +You can run tests in parallel for faster execution: + +```go +func TestParallel(t *testing.T) { + t.Parallel() // Enable parallel execution + + in, err := framework.Load[Config](t) + require.NoError(t, err) + // ... rest of test +} +``` + +### Test Cleanup +CTF automatically handles cleanup of Docker containers, but you can add custom cleanup: + +```go +func TestWithCleanup(t *testing.T) { + in, err := framework.Load[Config](t) + require.NoError(t, err) + + bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA) + require.NoError(t, err) + + // Add custom cleanup + t.Cleanup(func() { + // Custom cleanup logic + log.Println("Cleaning up custom resources") + }) + + // Your test logic +} +``` + +## Running Tests + +### Basic Test Execution +```bash +# Run all tests +go test -v + +# Run specific test +go test -v -run TestSmoke + +# Run tests with specific config +CTF_CONFIGS=smoke.toml go test -v -run TestSmoke + +# Run tests multiple times +go test -v -run TestSmoke -count 5 +``` + +### Test Flags +```bash +# Run tests with timeout +go test -v -timeout 10m -run TestSmoke + +# Run tests with race detection +go test -v -race -run TestSmoke + +# Run tests with coverage +go test -v -cover -run TestSmoke +``` + +### Environment Variables +```bash +# Set log level +CTF_LOG_LEVEL=debug go test -v -run TestSmoke + +# Disable caching +CTF_DISABLE_CACHE=true go test -v -run TestSmoke + +# Use specific Docker network +CTF_NETWORK_NAME=my-network go test -v -run TestSmoke +``` + +## Next Steps + +Now that you've written your first test, you can: + +1. **Add more components** - Try adding databases, external services, or multiple blockchain networks +2. **Learn about configuration** - Explore the [Configuration Guide](Configuration) for more options +3. **Set up observability** - Add monitoring and logging with the [Environment Setup Guide](Environment-Setup) +4. **Explore advanced patterns** - Check out the [Framework Test Patterns](../Framework/Test-Patterns) +5. **Look at examples** - Browse the [Examples section](../Examples/Basic-Examples) for more complex scenarios + +## Common Issues + +### Test Fails to Start +- Check that Docker is running +- Verify your configuration file syntax +- Ensure all required fields are present + +### Components Not Ready +- CTF automatically waits for components to be ready +- Check logs for component startup issues +- Verify network connectivity + +### Port Conflicts +- CTF automatically assigns ports to avoid conflicts +- If you get port binding errors, check what's using the ports +- Use `ctf d rm` to clean up existing containers + +### Memory Issues +- Some components (like blockchain nodes) can be memory-intensive +- Consider using OrbStack instead of Docker Desktop +- Increase Docker memory limits if needed \ No newline at end of file diff --git a/Getting-Started/Installation.md b/Getting-Started/Installation.md new file mode 100644 index 000000000..f2a9aa5bf --- /dev/null +++ b/Getting-Started/Installation.md @@ -0,0 +1,277 @@ +# Installation Guide + +This guide will help you set up the Chainlink Testing Framework (CTF) on your local machine. + +## Prerequisites + +### Required Software + +1. **Docker** + - **Recommended**: [OrbStack](https://orbstack.dev/) (faster, smaller memory footprint) + - **Alternative**: [Docker Desktop](https://www.docker.com/products/docker-desktop/) + + Tested with: + ``` + Docker version 27.3.1 + OrbStack Version: 1.8.2 (1080200) + ``` + +2. **Golang** + - Install the latest stable version from [go.dev](https://go.dev/doc/install) + - Minimum version: Go 1.21+ + - Recommended: Go 1.22+ + +3. **Git** + - Required for cloning repositories and managing dependencies + +### Optional Software + +- **[direnv](https://direnv.net/)** - Automatically load environment variables +- **[nix](https://nixos.org/manual/nix/stable/installation/installation.html)** - For development dependencies (used by some libraries) + +## Installation Steps + +### 1. Install Docker + +#### macOS +```bash +# Install OrbStack (recommended) +brew install --cask orbstack + +# Or install Docker Desktop +brew install --cask docker +``` + +#### Linux +```bash +# Ubuntu/Debian +curl -fsSL https://get.docker.com -o get-docker.sh +sudo sh get-docker.sh + +# Add user to docker group +sudo usermod -aG docker $USER +``` + +#### Windows +- Download and install [Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/) + +### 2. Install Golang + +#### macOS +```bash +# Using Homebrew +brew install go + +# Or download from go.dev +curl -L https://go.dev/dl/go1.22.0.darwin-amd64.pkg -o go.pkg +sudo installer -pkg go.pkg -target / +``` + +#### Linux +```bash +# Download and install +wget https://go.dev/dl/go1.22.0.linux-amd64.tar.gz +sudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz + +# Add to PATH +echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc +source ~/.bashrc +``` + +#### Windows +- Download from [go.dev](https://go.dev/dl/) and run the installer + +### 3. Download CTF CLI + +The CTF CLI provides helpful commands for managing test environments and observability stacks. + +#### macOS ARM64 (M1/M2/M3) +```bash +curl -L https://github.com/smartcontractkit/chainlink-testing-framework/releases/download/framework/v0.1.8/framework-v0.1.8-darwin-arm64.tar.gz | tar -xz +``` + +#### macOS AMD64 (Intel) +```bash +curl -L https://github.com/smartcontractkit/chainlink-testing-framework/releases/download/framework/v0.1.8/framework-v0.1.8-darwin-amd64.tar.gz | tar -xz +``` + +#### Linux ARM64 +```bash +curl -L https://github.com/smartcontractkit/chainlink-testing-framework/releases/download/framework/v0.1.8/framework-v0.1.8-linux-arm64.tar.gz | tar -xz +``` + +#### Linux AMD64 +```bash +curl -L https://github.com/smartcontractkit/chainlink-testing-framework/releases/download/framework/v0.1.8/framework-v0.1.8-linux-amd64.tar.gz | tar -xz +``` + +#### Windows +```bash +# Using PowerShell +Invoke-WebRequest -Uri "https://github.com/smartcontractkit/chainlink-testing-framework/releases/download/framework/v0.1.8/framework-v0.1.8-windows-amd64.tar.gz" -OutFile "framework.tar.gz" +tar -xzf framework.tar.gz +``` + +### 4. Set Up Environment + +Create a `.envrc` file in your project directory: + +```bash +# Create .envrc file +cat > .envrc << EOF +export TESTCONTAINERS_RYUK_DISABLED=true +EOF + +# Load environment variables +source .envrc +``` + +**Note**: If you have `direnv` installed, the environment variables will be automatically loaded when you enter the directory. + +### 5. Verify Installation + +Test that everything is working: + +```bash +# Check Docker +docker --version +docker run hello-world + +# Check Go +go version + +# Check CTF CLI +./framework --help +``` + +## Project Setup + +### 1. Create a New Project + +```bash +# Create project directory +mkdir my-ctf-project +cd my-ctf-project + +# Initialize Go module +go mod init my-ctf-project + +# Add framework dependency +go get github.com/smartcontractkit/chainlink-testing-framework/framework +``` + +### 2. Create Basic Configuration + +Create a `smoke.toml` file: + +```toml +[blockchain_a] + type = "anvil" +``` + +### 3. Create Your First Test + +Create `smoke_test.go`: + +```go +package mymodule_test + +import ( + "github.com/smartcontractkit/chainlink-testing-framework/framework" + "github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain" + "github.com/stretchr/testify/require" + "testing" +) + +type Config struct { + BlockchainA *blockchain.Input `toml:"blockchain_a" validate:"required"` +} + +func TestSmoke(t *testing.T) { + in, err := framework.Load[Config](t) + require.NoError(t, err) + + bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA) + require.NoError(t, err) + + t.Run("blockchain is running", func(t *testing.T) { + require.NotEmpty(t, bc.Nodes[0].ExternalHTTPUrl) + }) +} +``` + +### 4. Run Your First Test + +```bash +# Run the test +CTF_CONFIGS=smoke.toml go test -v -run TestSmoke + +# Clean up containers +ctf d rm +``` + +## Troubleshooting + +### Common Issues + +#### Docker Permission Issues +```bash +# Add user to docker group (Linux) +sudo usermod -aG docker $USER +newgrp docker + +# Restart Docker Desktop (macOS/Windows) +``` + +#### Port Conflicts +If you get port binding errors: +```bash +# Check what's using the port +lsof -i :8545 # Example for Ethereum RPC port + +# Kill the process or change ports in your config +``` + +#### Memory Issues +If you encounter memory issues with Docker: +- Increase Docker memory limit in Docker Desktop settings +- Use OrbStack instead of Docker Desktop (better performance) + +#### Go Module Issues +```bash +# Clean module cache +go clean -modcache + +# Tidy dependencies +go mod tidy +``` + +### Getting Help + +- Check the [Troubleshooting Guide](../Reference/Troubleshooting) +- Search [existing issues](https://github.com/smartcontractkit/chainlink-testing-framework/issues) +- Create a [new issue](https://github.com/smartcontractkit/chainlink-testing-framework/issues/new) with detailed information + +## Next Steps + +Now that you have CTF installed, you can: + +1. [Write your first test](First-Test) +2. [Learn about configuration](Configuration) +3. [Set up observability](Environment-Setup) +4. [Explore the framework components](../Framework/Components) + +## System Requirements + +### Minimum Requirements +- **CPU**: 2 cores +- **RAM**: 4GB +- **Storage**: 10GB free space +- **OS**: macOS 10.15+, Ubuntu 18.04+, Windows 10+ + +### Recommended Requirements +- **CPU**: 4+ cores +- **RAM**: 8GB+ +- **Storage**: 20GB+ free space +- **OS**: Latest stable versions +- **Docker**: OrbStack (macOS) or Docker Desktop with increased resources \ No newline at end of file diff --git a/Home.md b/Home.md new file mode 100644 index 000000000..8ca3a1686 --- /dev/null +++ b/Home.md @@ -0,0 +1,218 @@ +# Chainlink Testing Framework (CTF) Wiki + +
+ +[![Documentation](https://img.shields.io/badge/Documentation-MDBook-blue?style=for-the-badge)](https://smartcontractkit.github.io/chainlink-testing-framework/overview.html) +[![Framework tag](https://img.shields.io/github/v/tag/smartcontractkit/chainlink-testing-framework?filter=%2Aframework%2A)](https://github.com/smartcontractkit/chainlink-testing-framework/tags) +[![Lib tag](https://img.shields.io/github/v/tag/smartcontractkit/chainlink-testing-framework?filter=%2Alib%2A)](https://github.com/smartcontractkit/chainlink-testing-framework/tags) +[![WASP tag](https://img.shields.io/github/v/tag/smartcontractkit/chainlink-testing-framework?filter=%2Awasp%2A)](https://github.com/smartcontractkit/chainlink-testing-framework/tags) +[![Seth tag](https://img.shields.io/github/v/tag/smartcontractkit/chainlink-testing-framework?filter=%2Aseth%2A)](https://github.com/smartcontractkit/chainlink-testing-framework/tags) +[![Havoc tag](https://img.shields.io/github/v/tag/smartcontractkit/chainlink-testing-framework?filter=%2Ahavoc%2A)](https://github.com/smartcontractkit/chainlink-testing-framework/tags) + +[![Tests](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/test.yaml/badge.svg)](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/test.yaml) +[![Run all linters](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/linters.yml/badge.svg)](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/linters.yml) + +[![Go Report Card](https://goreportcard.com/badge/github.com/smartcontractkit/chainlink-testing-framework)](https://goreportcard.com/report/github.com/smartcontractkit/chainlink-testing-framework) +![Go Version](https://img.shields.io/github/go-mod/go-version/smartcontractkit/chainlink-testing-framework?filename=./lib/go.mod) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) + +
+ +## Overview + +The **Chainlink Testing Framework (CTF)** is a comprehensive blockchain development framework written in Go, designed to help Chainlink developers create extensive integration, end-to-end, performance, and chaos tests to ensure the stability of the Chainlink project. It can also be helpful for developers who want to use Chainlink oracles in their projects or even for those not using Chainlink at all. + +## 🎯 Primary Goals + +- **Reduce complexity** of end-to-end testing +- **Enable tests to run in any environment** +- **Serve as a single source of truth** for system behavior +- **Provide modular, data-driven testing** capabilities +- **Support comprehensive observability** and monitoring + +## 🏗️ Architecture + +The CTF monorepository contains two major pieces: + +### 1. **Framework** - Core Testing Infrastructure +- **Modular component system** for blockchain networks, Chainlink nodes, and other services +- **Configuration-driven testing** with TOML-based configs +- **Component isolation** and replaceability +- **Integrated observability stack** (metrics, logs, traces, profiles) +- **Caching system** for faster test development +- **Quick local environments** (15-second setup with caching) + +### 2. **Libraries** - Specialized Testing Tools +- **[WASP](Libraries/WASP)** - Scalable protocol-agnostic load testing library +- **[Seth](Libraries/Seth)** - Reliable and debug-friendly Ethereum client +- **[Havoc](Libraries/Havoc)** - Chaos testing library for Kubernetes environments + +## 🚀 Key Features + +### Framework Features +- **Straightforward and sequential test composition** - Tests are readable with precise control +- **Modular configuration** - No arcane knowledge required, config reflects components used +- **Component isolation** - Components decoupled via input/output structs +- **Replaceability and extensibility** - Any deployment component can be swapped +- **Quick local environments** - Common setup in just 15 seconds +- **Caching** - Skip setup for faster test development +- **Integrated observability** - Metrics, logs, traces, and profiles + +### Library Features +- **WASP**: Protocol-agnostic load testing with Grafana integration +- **Seth**: Transaction decoding, tracing, gas bumping, and multi-key support +- **Havoc**: Chaos engineering with Chaos Mesh integration + +## 📚 Quick Start + +### Prerequisites +- **Docker** ([OrbStack](https://orbstack.dev/) recommended) or Docker Desktop +- **Golang** (latest stable version) +- **CTF CLI** (download from releases) + +### Basic Setup +```bash +# Create project directory +mkdir my-ctf-project && cd my-ctf-project + +# Initialize Go module +go mod init my-ctf-project + +# Add framework dependency +go get github.com/smartcontractkit/chainlink-testing-framework/framework + +# Download CTF CLI (example for macOS ARM64) +curl -L https://github.com/smartcontractkit/chainlink-testing-framework/releases/download/framework/v0.1.8/framework-v0.1.8-darwin-arm64.tar.gz | tar -xz + +# Set up environment +echo 'export TESTCONTAINERS_RYUK_DISABLED=true' > .envrc +source .envrc +``` + +### Your First Test +```toml +# smoke.toml +[blockchain_a] + type = "anvil" +``` + +```go +// smoke_test.go +package mymodule_test + +import ( + "github.com/smartcontractkit/chainlink-testing-framework/framework" + "github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain" + "github.com/stretchr/testify/require" + "testing" +) + +type Config struct { + BlockchainA *blockchain.Input `toml:"blockchain_a" validate:"required"` +} + +func TestMe(t *testing.T) { + in, err := framework.Load[Config](t) + require.NoError(t, err) + + bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA) + require.NoError(t, err) + + t.Run("test something", func(t *testing.T) { + require.NotEmpty(t, bc.Nodes[0].ExternalHTTPUrl) + }) +} +``` + +```bash +# Run the test +CTF_CONFIGS=smoke.toml go test -v -run TestMe + +# Clean up +ctf d rm +``` + +## 📖 Documentation Structure + +### Getting Started +- [Installation Guide](Getting-Started/Installation) +- [First Test](Getting-Started/First-Test) +- [Configuration Basics](Getting-Started/Configuration) +- [Environment Setup](Getting-Started/Environment-Setup) + +### Framework +- [Framework Overview](Framework/Overview) +- [Component System](Framework/Components) +- [Configuration Management](Framework/Configuration) +- [Observability Stack](Framework/Observability) +- [Caching System](Framework/Caching) +- [Test Patterns](Framework/Test-Patterns) + +### Libraries +- [WASP - Load Testing](Libraries/WASP) +- [Seth - Ethereum Client](Libraries/Seth) +- [Havoc - Chaos Testing](Libraries/Havoc) + +### Advanced Topics +- [Component Development](Advanced/Component-Development) +- [Custom Components](Advanced/Custom-Components) +- [Performance Testing](Advanced/Performance-Testing) +- [Chaos Engineering](Advanced/Chaos-Engineering) +- [CI/CD Integration](Advanced/CI-CD-Integration) + +### Examples & Tutorials +- [Basic Examples](Examples/Basic-Examples) +- [Advanced Examples](Examples/Advanced-Examples) +- [Real-world Use Cases](Examples/Use-Cases) + +### Reference +- [API Reference](Reference/API-Reference) +- [Configuration Reference](Reference/Configuration-Reference) +- [CLI Reference](Reference/CLI-Reference) +- [Troubleshooting](Reference/Troubleshooting) + +## 🎯 Use Cases + +### For Chainlink Developers +- **Integration Testing**: Test Chainlink nodes with various blockchain networks +- **End-to-End Testing**: Complete workflow testing from contract deployment to oracle responses +- **Performance Testing**: Load testing with WASP library +- **Chaos Testing**: Failure scenario testing with Havoc +- **Upgrade Testing**: Version compatibility and migration testing + +### For Blockchain Developers +- **Smart Contract Testing**: Deploy and test contracts with Seth +- **Multi-Chain Testing**: Support for Ethereum, Solana, TON, and more +- **Network Simulation**: Local blockchain networks for development +- **Gas Optimization**: Transaction tracing and gas analysis + +### For DevOps Engineers +- **Infrastructure Testing**: Test deployment configurations +- **Observability**: Integrated monitoring and logging +- **CI/CD Integration**: Automated testing pipelines +- **Environment Management**: Consistent test environments + +## 🤝 Contributing + +We welcome contributions! Please see our [Contributing Guide](Contributing/Overview) for details on: +- Code of Conduct +- Development Setup +- Testing Guidelines +- Pull Request Process +- Issue Reporting + +## 📄 License + +This project is licensed under the MIT License - see the [LICENSE](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/LICENSE) file for details. + +## 🔗 Links + +- **[Repository](https://github.com/smartcontractkit/chainlink-testing-framework)** +- **[Documentation](https://smartcontractkit.github.io/chainlink-testing-framework/)** +- **[Issues](https://github.com/smartcontractkit/chainlink-testing-framework/issues)** +- **[Discussions](https://github.com/smartcontractkit/chainlink-testing-framework/discussions)** +- **[Releases](https://github.com/smartcontractkit/chainlink-testing-framework/releases)** + +--- + +*This wiki provides comprehensive documentation for the Chainlink Testing Framework. For the most up-to-date information, always refer to the [official documentation](https://smartcontractkit.github.io/chainlink-testing-framework/).* \ No newline at end of file diff --git a/Libraries/Havoc.md b/Libraries/Havoc.md new file mode 100644 index 000000000..f99910c1b --- /dev/null +++ b/Libraries/Havoc.md @@ -0,0 +1,751 @@ +# Havoc - Chaos Testing Library + +[![Go Reference](https://pkg.go.dev/badge/github.com/smartcontractkit/chainlink-testing-framework/havoc.svg)](https://pkg.go.dev/github.com/smartcontractkit/chainlink-testing-framework/havoc) + +## Overview + +The `havoc` package is designed to facilitate chaos testing within [Kubernetes](https://kubernetes.io/) environments using [Chaos Mesh](https://chaos-mesh.org/). It offers a structured way to define, execute, and manage chaos experiments as code, directly integrated into Go applications or testing suites, simplifying the creation and control of Chaos Mesh experiments. + +## Goals + +- **Chaos Object Management** - Create, update, pause, resume, and delete chaos experiments using Go structures and methods +- **Lifecycle Hooks** - Utilize chaos listeners to hook into the lifecycle of chaos experiments +- **Different Experiments** - Create and manage different types of chaos experiments to affect network, IO, K8s pods, and more +- **Active Monitoring** - Monitor and react to the status of chaos experiments programmatically +- **Observability Integration** - Structured logging and Grafana annotations for chaos experiment monitoring + +## Key Features + +### 1. **Chaos Experiment Management** +- Create and manage Chaos Mesh experiments programmatically +- Full lifecycle control (create, pause, resume, delete) +- Type-safe experiment configuration + +### 2. **Lifecycle Hooks** +- Implement custom listeners for chaos experiment events +- React to experiment state changes +- Integrate with monitoring and alerting systems + +### 3. **Multiple Experiment Types** +- **Network Chaos** - Network latency, packet loss, bandwidth limits +- **Pod Chaos** - Pod failure, pod kill, container kill +- **IO Chaos** - IO latency, IO error injection +- **Kernel Chaos** - Kernel panic, memory corruption +- **Time Chaos** - Clock skew, time manipulation +- **DNS Chaos** - DNS error injection, DNS spoofing +- **HTTP Chaos** - HTTP error injection, HTTP latency + +### 4. **Observability Integration** +- Structured logging with [zerolog](https://github.com/rs/zerolog) +- Grafana dashboard annotations +- Prometheus metrics integration +- Custom event listeners + +## Requirements + +- [Go](https://go.dev/) 1.21+ +- A Kubernetes cluster with [Chaos Mesh installed](https://chaos-mesh.org/docs/quick-start/) +- [k8s.io/client-go](https://github.com/kubernetes/client-go) for Kubernetes API access + +## Installation + +### Using Go Modules +```bash +go get github.com/smartcontractkit/chainlink-testing-framework/havoc +``` + +### Dependencies +```bash +go get k8s.io/client-go +go get k8s.io/apimachinery +``` + +## Quick Start + +### Basic Chaos Experiment + +```go +package main + +import ( + "context" + "log" + "time" + + "github.com/smartcontractkit/chainlink-testing-framework/havoc" + "github.com/smartcontractkit/chainlink-testing-framework/havoc/chaosmesh" +) + +func main() { + // Create chaos client + client, err := havoc.NewClient() + if err != nil { + log.Fatal(err) + } + + // Define network chaos experiment + networkChaos := &chaosmesh.NetworkChaos{ + ObjectMeta: metav1.ObjectMeta{ + Name: "network-latency-test", + Namespace: "default", + }, + Spec: chaosmesh.NetworkChaosSpec{ + Action: chaosmesh.NetworkDelayAction, + Mode: chaosmesh.OneMode, + Selector: chaosmesh.PodSelectorSpec{ + Namespaces: []string{"default"}, + LabelSelectors: map[string]string{ + "app": "my-app", + }, + }, + Delay: &chaosmesh.DelaySpec{ + Latency: "100ms", + Correlation: "100", + Jitter: "0ms", + }, + Duration: &chaosmesh.Duration{ + Duration: "30s", + }, + }, + } + + // Create chaos experiment + chaos, err := havoc.NewChaos(client, networkChaos) + if err != nil { + log.Fatal(err) + } + + // Start the experiment + err = chaos.Create(context.Background()) + if err != nil { + log.Fatal(err) + } + + log.Println("Chaos experiment started") + + // Wait for experiment to complete + time.Sleep(35 * time.Second) + + // Clean up + err = chaos.Delete(context.Background()) + if err != nil { + log.Printf("Failed to delete chaos experiment: %v", err) + } +} +``` + +### With Lifecycle Listeners + +```go +package main + +import ( + "context" + "log" + "time" + + "github.com/smartcontractkit/chainlink-testing-framework/havoc" + "github.com/smartcontractkit/chainlink-testing-framework/havoc/chaosmesh" +) + +// Custom chaos listener +type MyChaosListener struct{} + +func (l *MyChaosListener) OnChaosCreated(chaos *havoc.Chaos) { + log.Printf("Chaos experiment created: %s", chaos.Name()) +} + +func (l *MyChaosListener) OnChaosStarted(chaos *havoc.Chaos) { + log.Printf("Chaos experiment started: %s", chaos.Name()) +} + +func (l *MyChaosListener) OnChaosPaused(chaos *havoc.Chaos) { + log.Printf("Chaos experiment paused: %s", chaos.Name()) +} + +func (l *MyChaosListener) OnChaosResumed(chaos *havoc.Chaos) { + log.Printf("Chaos experiment resumed: %s", chaos.Name()) +} + +func (l *MyChaosListener) OnChaosDeleted(chaos *havoc.Chaos) { + log.Printf("Chaos experiment deleted: %s", chaos.Name()) +} + +func main() { + client, err := havoc.NewClient() + if err != nil { + log.Fatal(err) + } + + // Create pod chaos experiment + podChaos := &chaosmesh.PodChaos{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod-failure-test", + Namespace: "default", + }, + Spec: chaosmesh.PodChaosSpec{ + Action: chaosmesh.PodFailureAction, + Mode: chaosmesh.OneMode, + Selector: chaosmesh.PodSelectorSpec{ + Namespaces: []string{"default"}, + LabelSelectors: map[string]string{ + "app": "my-app", + }, + }, + Duration: &chaosmesh.Duration{ + Duration: "10s", + }, + }, + } + + // Create chaos with listener + chaos, err := havoc.NewChaos(client, podChaos) + if err != nil { + log.Fatal(err) + } + + // Add custom listener + chaos.AddListener(&MyChaosListener{}) + + // Start experiment + err = chaos.Create(context.Background()) + if err != nil { + log.Fatal(err) + } + + // Wait and clean up + time.Sleep(15 * time.Second) + chaos.Delete(context.Background()) +} +``` + +## Experiment Types + +### Network Chaos + +```go +// Network latency +networkChaos := &chaosmesh.NetworkChaos{ + ObjectMeta: metav1.ObjectMeta{ + Name: "network-latency", + Namespace: "default", + }, + Spec: chaosmesh.NetworkChaosSpec{ + Action: chaosmesh.NetworkDelayAction, + Mode: chaosmesh.OneMode, + Selector: chaosmesh.PodSelectorSpec{ + Namespaces: []string{"default"}, + LabelSelectors: map[string]string{"app": "my-app"}, + }, + Delay: &chaosmesh.DelaySpec{ + Latency: "200ms", + Correlation: "100", + Jitter: "50ms", + }, + Duration: &chaosmesh.Duration{ + Duration: "60s", + }, + }, +} + +// Network packet loss +networkChaos := &chaosmesh.NetworkChaos{ + ObjectMeta: metav1.ObjectMeta{ + Name: "network-loss", + Namespace: "default", + }, + Spec: chaosmesh.NetworkChaosSpec{ + Action: chaosmesh.NetworkLossAction, + Mode: chaosmesh.OneMode, + Selector: chaosmesh.PodSelectorSpec{ + Namespaces: []string{"default"}, + LabelSelectors: map[string]string{"app": "my-app"}, + }, + Loss: &chaosmesh.LossSpec{ + Loss: "50", + Correlation: "100", + }, + Duration: &chaosmesh.Duration{ + Duration: "30s", + }, + }, +} +``` + +### Pod Chaos + +```go +// Pod failure +podChaos := &chaosmesh.PodChaos{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod-failure", + Namespace: "default", + }, + Spec: chaosmesh.PodChaosSpec{ + Action: chaosmesh.PodFailureAction, + Mode: chaosmesh.OneMode, + Selector: chaosmesh.PodSelectorSpec{ + Namespaces: []string{"default"}, + LabelSelectors: map[string]string{"app": "my-app"}, + }, + Duration: &chaosmesh.Duration{ + Duration: "30s", + }, + }, +} + +// Pod kill +podChaos := &chaosmesh.PodChaos{ + ObjectMeta: metav1.ObjectMeta{ + Name: "pod-kill", + Namespace: "default", + }, + Spec: chaosmesh.PodChaosSpec{ + Action: chaosmesh.PodKillAction, + Mode: chaosmesh.OneMode, + Selector: chaosmesh.PodSelectorSpec{ + Namespaces: []string{"default"}, + LabelSelectors: map[string]string{"app": "my-app"}, + }, + }, +} +``` + +### IO Chaos + +```go +// IO latency +ioChaos := &chaosmesh.IOChaos{ + ObjectMeta: metav1.ObjectMeta{ + Name: "io-latency", + Namespace: "default", + }, + Spec: chaosmesh.IOChaosSpec{ + Action: chaosmesh.IODelayAction, + Mode: chaosmesh.OneMode, + Selector: chaosmesh.PodSelectorSpec{ + Namespaces: []string{"default"}, + LabelSelectors: map[string]string{"app": "my-app"}, + }, + Delay: "100ms", + Percent: 50, + Path: "/var/log", + Methods: []string{"read", "write"}, + Duration: &chaosmesh.Duration{ + Duration: "60s", + }, + }, +} +``` + +### Time Chaos + +```go +// Clock skew +timeChaos := &chaosmesh.TimeChaos{ + ObjectMeta: metav1.ObjectMeta{ + Name: "clock-skew", + Namespace: "default", + }, + Spec: chaosmesh.TimeChaosSpec{ + Mode: chaosmesh.OneMode, + Selector: chaosmesh.PodSelectorSpec{ + Namespaces: []string{"default"}, + LabelSelectors: map[string]string{"app": "my-app"}, + }, + TimeOffset: "1h", + Duration: &chaosmesh.Duration{ + Duration: "30s", + }, + }, +} +``` + +## Observability Integration + +### Structured Logging + +Havoc provides structured logging using zerolog: + +```go +import ( + "github.com/rs/zerolog/log" +) + +// Enable debug logging +log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) + +// Chaos events are automatically logged +chaos, err := havoc.NewChaos(client, networkChaos) +if err != nil { + log.Fatal().Err(err).Msg("Failed to create chaos") +} +``` + +### Grafana Annotations + +Integrate with Grafana dashboards: + +```go +import ( + "github.com/smartcontractkit/chainlink-testing-framework/havoc" +) + +// Create Grafana annotator +grafanaAnnotator := havoc.NewSingleLineGrafanaAnnotator( + "http://localhost:3000", + "your-token", + []string{"dashboard-uid"}, +) + +// Add to chaos experiment +chaos.AddListener(grafanaAnnotator) + +// Start experiment (annotations will be created automatically) +err = chaos.Create(context.Background()) +``` + +### Range Annotations + +For experiments with duration, use range annotations: + +```go +// Create range annotator +rangeAnnotator := havoc.NewRangeGrafanaAnnotator( + "http://localhost:3000", + "your-token", + []string{"dashboard-uid"}, +) + +chaos.AddListener(rangeAnnotator) +``` + +## Advanced Usage + +### Multiple Experiments + +```go +func runChaosSuite() error { + client, err := havoc.NewClient() + if err != nil { + return err + } + + experiments := []chaosmesh.Chaos{ + createNetworkLatency(), + createPodFailure(), + createIOChaos(), + } + + for _, exp := range experiments { + chaos, err := havoc.NewChaos(client, exp) + if err != nil { + return err + } + + // Start experiment + err = chaos.Create(context.Background()) + if err != nil { + return err + } + + // Wait for completion + time.Sleep(30 * time.Second) + + // Clean up + chaos.Delete(context.Background()) + } + + return nil +} +``` + +### Conditional Chaos + +```go +func conditionalChaos(condition bool) error { + if !condition { + return nil + } + + client, err := havoc.NewClient() + if err != nil { + return err + } + + chaos, err := havoc.NewChaos(client, createPodChaos()) + if err != nil { + return err + } + + return chaos.Create(context.Background()) +} +``` + +### Chaos with Recovery + +```go +func chaosWithRecovery() error { + client, err := havoc.NewClient() + if err != nil { + return err + } + + chaos, err := havoc.NewChaos(client, createNetworkChaos()) + if err != nil { + return err + } + + // Start chaos + err = chaos.Create(context.Background()) + if err != nil { + return err + } + + // Monitor system health + go func() { + for { + if isSystemUnhealthy() { + // Pause chaos if system is unhealthy + chaos.Pause(context.Background()) + break + } + time.Sleep(5 * time.Second) + } + }() + + // Wait for experiment duration + time.Sleep(60 * time.Second) + + // Clean up + return chaos.Delete(context.Background()) +} +``` + +## Testing Integration + +### With CTF Framework + +```go +func TestChaosResilience(t *testing.T) { + // Setup your application + app := setupApplication(t) + + // Create chaos client + client, err := havoc.NewClient() + require.NoError(t, err) + + // Create chaos experiment + chaos, err := havoc.NewChaos(client, createPodChaos()) + require.NoError(t, err) + + // Start chaos + err = chaos.Create(context.Background()) + require.NoError(t, err) + + // Test application resilience + t.Run("application remains functional", func(t *testing.T) { + // Your resilience test logic here + require.True(t, app.IsHealthy()) + }) + + // Clean up + chaos.Delete(context.Background()) +} +``` + +### With WASP Load Testing + +```go +func TestChaosUnderLoad(t *testing.T) { + // Setup load test + profile := wasp.NewProfile() + generator := wasp.NewGenerator(&wasp.Config{ + T: 60 * time.Second, + RPS: 100, + LoadType: wasp.RPS, + Schedule: wasp.Plain(100, 60*time.Second), + }) + + // Add load test logic + generator.AddRequestFn(func(ctx context.Context) error { + return performRequest(ctx) + }) + + profile.Add(generator) + + // Start chaos in background + go func() { + client, _ := havoc.NewClient() + chaos, _ := havoc.NewChaos(client, createNetworkChaos()) + chaos.Create(context.Background()) + time.Sleep(30 * time.Second) + chaos.Delete(context.Background()) + }() + + // Run load test + _, err := profile.Run(true) + require.NoError(t, err) +} +``` + +## Configuration + +### Kubernetes Client Configuration + +```go +// Use default kubeconfig +client, err := havoc.NewClient() + +// Use custom kubeconfig +client, err := havoc.NewClientWithConfig(&rest.Config{ + Host: "https://kubernetes.example.com", + BearerToken: "your-token", + TLSClientConfig: rest.TLSClientConfig{ + Insecure: true, + }, +}) +``` + +### Chaos Mesh Configuration + +```go +// Configure Chaos Mesh namespace +client, err := havoc.NewClient() +if err != nil { + log.Fatal(err) +} + +// Set Chaos Mesh namespace +client.SetChaosMeshNamespace("chaos-mesh") + +// Configure experiment defaults +client.SetDefaultDuration("30s") +client.SetDefaultMode(chaosmesh.OneMode) +``` + +## Best Practices + +### 1. **Start Small** +- Begin with simple experiments (pod kill, network latency) +- Gradually increase complexity +- Monitor system impact carefully + +### 2. **Use Appropriate Selectors** +```go +Selector: chaosmesh.PodSelectorSpec{ + Namespaces: []string{"production"}, + LabelSelectors: map[string]string{ + "app": "my-app", + "tier": "backend", + }, + // Use specific pod names for targeted testing + Pods: map[string][]string{ + "production": {"my-app-1", "my-app-2"}, + }, +}, +``` + +### 3. **Monitor System Health** +```go +// Add health monitoring to chaos experiments +chaos.AddListener(&HealthMonitor{ + threshold: 0.8, // 80% health threshold + onUnhealthy: func() { + chaos.Pause(context.Background()) + }, +}) +``` + +### 4. **Use Appropriate Durations** +```go +Duration: &chaosmesh.Duration{ + Duration: "30s", // Short for testing + // Duration: "5m", // Longer for stress testing +}, +``` + +### 5. **Clean Up Properly** +```go +defer func() { + if chaos != nil { + chaos.Delete(context.Background()) + } +}() +``` + +## Troubleshooting + +### Common Issues + +#### Chaos Mesh Not Installed +```bash +# Check if Chaos Mesh is installed +kubectl get pods -n chaos-mesh + +# Install Chaos Mesh if needed +curl -sSL https://mirrors.chaos-mesh.org/v2.6.0/crd.yaml | kubectl apply -f - +kubectl apply -f https://mirrors.chaos-mesh.org/v2.6.0/rbac.yaml +kubectl apply -f https://mirrors.chaos-mesh.org/v2.6.0/chaos-mesh.yaml +``` + +#### Permission Issues +```bash +# Check RBAC permissions +kubectl auth can-i create networkchaos --namespace default +kubectl auth can-i create podchaos --namespace default + +# Create necessary RBAC rules +kubectl apply -f rbac.yaml +``` + +#### Experiment Not Working +```bash +# Check experiment status +kubectl get networkchaos -n default +kubectl describe networkchaos network-latency-test + +# Check Chaos Mesh logs +kubectl logs -n chaos-mesh -l app.kubernetes.io/name=chaos-mesh +``` + +### Debug Mode + +Enable debug logging: + +```go +import ( + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +// Enable debug logging +zerolog.SetGlobalLevel(zerolog.DebugLevel) +log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}) +``` + +## Examples + +Check the [examples directory](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/havoc/examples) for comprehensive examples: + +- [Basic chaos experiments](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/havoc/examples/basic) +- [Network chaos testing](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/havoc/examples/network) +- [Pod chaos testing](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/havoc/examples/pod) +- [Integration with CTF](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/havoc/examples/ctf-integration) + +## API Reference + +For detailed API documentation, see the [Go package documentation](https://pkg.go.dev/github.com/smartcontractkit/chainlink-testing-framework/havoc). + +## Contributing + +We welcome contributions! Please see the [Contributing Guide](../../Contributing/Overview) for details on: + +- Code of Conduct +- Development Setup +- Testing Guidelines +- Pull Request Process + +## License + +This project is licensed under the MIT License - see the [LICENSE](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/LICENSE) file for details. \ No newline at end of file diff --git a/Libraries/Seth.md b/Libraries/Seth.md new file mode 100644 index 000000000..b2022f211 --- /dev/null +++ b/Libraries/Seth.md @@ -0,0 +1,708 @@ +# Seth - Ethereum Client Library + +[![Go Report Card](https://goreportcard.com/badge/github.com/smartcontractkit/chainlink-testing-framework/seth)](https://goreportcard.com/report/github.com/smartcontractkit/chainlink-testing-framework/seth) +[![Decoding tests](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/seth-test-decode.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_decode.yml) +[![Tracing tests](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/seth-test-trace.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_trace.yml) +[![Gas bumping tests](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/seth-test-bumping.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_cli.yml) +[![API tests](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/seth-test-api.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_api.yml) +[![CLI tests](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/seth-test-cli.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_cli.yml) +[![Integration tests (testnets)](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/seth-test-decode-testnet.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_decode_testnet.yml) + +## Overview + +Seth is a reliable and debug-friendly Ethereum client library that provides a thin, debuggable wrapper on top of `go-ethereum`. It's designed to make Ethereum development and testing easier by automatically decoding transaction inputs/outputs/logs and providing advanced debugging capabilities. + +## Goals + +- **Thin wrapper** on top of `go-ethereum` with minimal overhead +- **Debuggable** - comprehensive transaction decoding and tracing +- **Battle tested** - extensive test coverage including testnet integration +- **Automatic decoding** - decode all transaction inputs/outputs/logs for all ABIs +- **Simple synchronous API** - easy to use and understand +- **Resilient** - execute transactions even during gas spikes or RPC outages +- **Well tested** - comprehensive e2e test suite for testnet integration + +## Key Features + +### ✅ Implemented Features + +- [x] **Decode named inputs** - Automatically decode function parameters +- [x] **Decode named outputs** - Decode function return values +- [x] **Decode anonymous outputs** - Handle unnamed return values +- [x] **Decode logs** - Parse event logs with full context +- [x] **Decode indexed logs** - Handle indexed event parameters +- [x] **Decode old string reverts** - Parse legacy revert messages +- [x] **Decode new typed reverts** - Handle modern revert types +- [x] **EIP-1559 support** - Full support for London hard fork features +- [x] **Multi-keys client support** - Use multiple private keys +- [x] **CLI to manipulate test keys** - Command-line key management +- [x] **Simple manual gas price estimation** - Built-in gas optimization +- [x] **Decode collided event hashes** - Handle event signature collisions +- [x] **Tracing support (4byte)** - Function signature tracing +- [x] **Tracing support (callTracer)** - Call trace analysis +- [x] **Tracing decoding** - Decode trace results +- [x] **Tracing tests** - Comprehensive tracing test coverage +- [x] **Saving deployed contracts mapping** - Track contract addresses +- [x] **Reading deployed contracts mappings** - Load contract mappings +- [x] **Automatic gas estimator** - Intelligent gas price estimation +- [x] **Block stats CLI** - Block analysis tools +- [x] **Pending nonce checking** - Prevent nonce conflicts +- [x] **DOT graph output** - Visualize transaction traces +- [x] **Gas bumping for slow transactions** - Automatic retry with gas increase + +### 🚧 Planned Features + +- [ ] **Fail over client logic** - Automatic RPC failover +- [ ] **Tracing support (prestate)** - Pre-state tracing + +## Installation + +### Using Go Modules +```bash +go get github.com/smartcontractkit/chainlink-testing-framework/seth +``` + +### Using Nix (Recommended for Development) +```bash +# Install nix +curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install + +# Enter development shell +nix develop +``` + +## Quick Start + +### Basic Usage + +```go +package main + +import ( + "context" + "log" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" +) + +func main() { + // Create a new Seth client + client, err := seth.NewClient("http://localhost:8545") + if err != nil { + log.Fatal(err) + } + + // Get the latest block number + blockNumber, err := client.BlockNumber(context.Background()) + if err != nil { + log.Fatal(err) + } + + log.Printf("Latest block: %d", blockNumber) +} +``` + +### With Configuration + +```go +package main + +import ( + "context" + "log" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" +) + +func main() { + // Create configuration + cfg := seth.DefaultConfig("http://localhost:8545", []string{ + "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", + }) + + // Create client with configuration + client, err := seth.NewClientWithConfig(cfg) + if err != nil { + log.Fatal(err) + } + + // Use the client + balance, err := client.BalanceAt(context.Background(), client.Addresses[0], nil) + if err != nil { + log.Fatal(err) + } + + log.Printf("Balance: %s", balance.String()) +} +``` + +## Configuration + +### Simplified Configuration + +For basic use cases, you can use the simplified configuration: + +```go +cfg := seth.DefaultConfig("ws://localhost:8546", []string{ + "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", +}) +client, err := seth.NewClientWithConfig(cfg) +``` + +This uses reasonable defaults for: +- Gas price estimation +- Transaction timeout +- RPC timeout +- Gas limit estimation + +### TOML Configuration + +For more complex setups, use TOML configuration: + +```toml +# seth.toml +[networks.anvil] +urls = ["http://localhost:8545"] +private_keys = ["ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"] +gas_price_estimation = true +gas_limit_estimation = true +gas_bump_percent = 10 +gas_bump_wei = 1000000000 +max_gas_price = 100000000000 +min_gas_price = 1000000000 +gas_bump_threshold = 3 +rpc_timeout = "30s" +tx_timeout = "5m" +``` + +### Environment Variables + +Seth supports various environment variables for configuration: + +```bash +# Network configuration +SETH_NETWORK=anvil +SETH_URL=http://localhost:8545 +SETH_PRIVATE_KEYS=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + +# Gas configuration +SETH_GAS_PRICE_ESTIMATION=true +SETH_GAS_BUMP_PERCENT=10 +SETH_MAX_GAS_PRICE=100000000000 + +# Timeout configuration +SETH_RPC_TIMEOUT=30s +SETH_TX_TIMEOUT=5m +``` + +## Transaction Decoding + +### Automatic Decoding + +Seth automatically decodes all transaction data when you have the corresponding ABI: + +```go +// Deploy a contract +contractAddress, tx, _, err := counter.DeployCounter(client.NewTXOpts(), client) +if err != nil { + log.Fatal(err) +} + +// Decode the deployment transaction +decodedTx, err := client.Decode(tx, nil) +if err != nil { + log.Fatal(err) +} + +// Print decoded information +fmt.Printf("Contract deployed at: %s\n", contractAddress.Hex()) +fmt.Printf("Gas used: %d\n", decodedTx.GasUsed) +fmt.Printf("Gas price: %s\n", decodedTx.GasPrice.String()) +``` + +### Function Call Decoding + +```go +// Call a function +tx, err := counter.Increment(client.NewTXOpts()) +if err != nil { + log.Fatal(err) +} + +// Decode the transaction +decodedTx, err := client.Decode(tx, nil) +if err != nil { + log.Fatal(err) +} + +// Print function call details +fmt.Printf("Function: %s\n", decodedTx.FunctionName) +fmt.Printf("Inputs: %+v\n", decodedTx.Inputs) +``` + +### Event Log Decoding + +```go +// Get logs for a specific event +logs, err := client.FilterLogs(context.Background(), query) +if err != nil { + log.Fatal(err) +} + +// Decode each log +for _, log := range logs { + decodedLog, err := client.DecodeLog(log) + if err != nil { + continue + } + + fmt.Printf("Event: %s\n", decodedLog.EventName) + fmt.Printf("Data: %+v\n", decodedLog.Data) +} +``` + +## Transaction Tracing + +### Call Tracing + +```go +// Trace a transaction +trace, err := client.TraceTransaction(context.Background(), txHash) +if err != nil { + log.Fatal(err) +} + +// Print trace information +fmt.Printf("Trace type: %s\n", trace.Type) +fmt.Printf("Gas used: %d\n", trace.Result.GasUsed) +fmt.Printf("Calls: %d\n", len(trace.Result.Calls)) +``` + +### Function Signature Tracing + +```go +// Get function signature from 4byte database +signature, err := client.GetFunctionSignature("0xa9059cbb") +if err != nil { + log.Fatal(err) +} + +fmt.Printf("Function signature: %s\n", signature) +``` + +### DOT Graph Generation + +```go +// Generate DOT graph for transaction trace +dotGraph, err := client.GenerateDOTGraph(txHash) +if err != nil { + log.Fatal(err) +} + +// Save to file +err = os.WriteFile("trace.dot", []byte(dotGraph), 0644) +if err != nil { + log.Fatal(err) +} +``` + +## Gas Management + +### Automatic Gas Estimation + +```go +// Enable automatic gas estimation +cfg := seth.DefaultConfig("http://localhost:8545", []string{privateKey}) +cfg.GasPriceEstimation = true +cfg.GasLimitEstimation = true + +client, err := seth.NewClientWithConfig(cfg) +if err != nil { + log.Fatal(err) +} + +// Transactions will automatically use estimated gas +tx, err := counter.Increment(client.NewTXOpts()) +if err != nil { + log.Fatal(err) +} +``` + +### Manual Gas Price Estimation + +```go +// Estimate gas price manually +gasPrice, err := client.EstimateGasPrice(context.Background()) +if err != nil { + log.Fatal(err) +} + +// Use estimated gas price +opts := client.NewTXOpts() +opts.GasPrice = gasPrice + +tx, err := counter.Increment(opts) +if err != nil { + log.Fatal(err) +} +``` + +### Gas Bumping + +```go +// Configure gas bumping +cfg := seth.DefaultConfig("http://localhost:8545", []string{privateKey}) +cfg.GasBumpPercent = 10 +cfg.GasBumpThreshold = 3 + +client, err := seth.NewClientWithConfig(cfg) +if err != nil { + log.Fatal(err) +} + +// If transaction is slow, it will be automatically bumped +tx, err := counter.Increment(client.NewTXOpts()) +if err != nil { + log.Fatal(err) +} +``` + +## Multi-Key Support + +### Using Multiple Keys + +```go +// Create client with multiple keys +cfg := seth.DefaultConfig("http://localhost:8545", []string{ + "key1...", + "key2...", + "key3...", +}) + +client, err := seth.NewClientWithConfig(cfg) +if err != nil { + log.Fatal(err) +} + +// Use specific key for transaction +opts := client.NewTXKeyOpts(1) // Use second key +tx, err := counter.Increment(opts) +if err != nil { + log.Fatal(err) +} +``` + +### Key Management CLI + +```bash +# List all keys +seth keys list + +# Add a new key +seth keys add --private-key=0x... + +# Remove a key +seth keys remove --index=0 + +# Export a key +seth keys export --index=0 +``` + +## Contract Management + +### Contract Mapping + +```go +// Save contract mapping +err := client.SaveContractMapping("Counter", contractAddress, abi) +if err != nil { + log.Fatal(err) +} + +// Load contract mapping +abi, err := client.LoadContractMapping("Counter", contractAddress) +if err != nil { + log.Fatal(err) +} +``` + +### ABI Finder + +```go +// Find ABI by contract name +abi, err := client.FindABI("Counter") +if err != nil { + log.Fatal(err) +} + +// Find ABI by address +abi, err := client.FindABIByAddress(contractAddress) +if err != nil { + log.Fatal(err) +} +``` + +## Block Analysis + +### Block Stats + +```go +// Get block statistics +stats, err := client.GetBlockStats(context.Background(), blockNumber) +if err != nil { + log.Fatal(err) +} + +fmt.Printf("Block %d:\n", stats.BlockNumber) +fmt.Printf(" Transactions: %d\n", stats.TransactionCount) +fmt.Printf(" Gas used: %d\n", stats.GasUsed) +fmt.Printf(" Gas limit: %d\n", stats.GasLimit) +fmt.Printf(" Base fee: %s\n", stats.BaseFee.String()) +``` + +### Block Stats CLI + +```bash +# Get stats for latest block +seth block stats + +# Get stats for specific block +seth block stats --block=12345 + +# Get stats for range of blocks +seth block stats --from=1000 --to=1010 +``` + +## Advanced Features + +### Read-Only Mode + +```go +// Create read-only client +client, err := seth.NewReadOnlyClient("http://localhost:8545") +if err != nil { + log.Fatal(err) +} + +// Only read operations are allowed +balance, err := client.BalanceAt(context.Background(), address, nil) +if err != nil { + log.Fatal(err) +} +``` + +### RPC Traffic Logging + +```go +// Enable RPC traffic logging +cfg := seth.DefaultConfig("http://localhost:8545", []string{privateKey}) +cfg.LogRPC = true + +client, err := seth.NewClientWithConfig(cfg) +if err != nil { + log.Fatal(err) +} + +// All RPC calls will be logged +``` + +### Bulk Transaction Tracing + +```go +// Trace multiple transactions +txHashes := []common.Hash{hash1, hash2, hash3} +traces, err := client.TraceTransactions(context.Background(), txHashes) +if err != nil { + log.Fatal(err) +} + +for i, trace := range traces { + fmt.Printf("Transaction %d: %s\n", i, trace.Type) +} +``` + +## Testing + +### Running Tests + +```bash +# Run all tests +make test + +# Run specific test suite +make test_decode +make test_trace +make test_api +make test_cli + +# Run tests on specific network +make network=Anvil test +make network=Geth test +``` + +### Testnet Integration + +```bash +# Run tests on testnet +make network=Sepolia test +make network=Goerli test +``` + +## Examples + +Check the [examples directory](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/seth/examples) for comprehensive examples: + +- [Basic client usage](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/seth/examples/basic) +- [Contract deployment and interaction](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/seth/examples/contracts) +- [Transaction tracing](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/seth/examples/tracing) +- [Gas optimization](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/seth/examples/gas) + +## CLI Reference + +### Basic Commands + +```bash +# Get help +seth --help + +# Get version +seth version + +# Get network info +seth network info +``` + +### Key Management + +```bash +# List keys +seth keys list + +# Add key +seth keys add --private-key=0x... + +# Remove key +seth keys remove --index=0 + +# Export key +seth keys export --index=0 +``` + +### Block Analysis + +```bash +# Get block stats +seth block stats + +# Get block info +seth block info --block=12345 + +# Get transaction info +seth tx info --hash=0x... +``` + +### Contract Management + +```bash +# Save contract mapping +seth contract save --name=Counter --address=0x... --abi=path/to/abi.json + +# Load contract mapping +seth contract load --name=Counter --address=0x... + +# List contracts +seth contract list +``` + +## Best Practices + +### 1. **Use Configuration Files** +- Store network configurations in TOML files +- Use environment variables for sensitive data +- Version control your configurations + +### 2. **Handle Errors Gracefully** +```go +decodedTx, err := client.Decode(tx, nil) +if err != nil { + // Log error but don't fail the test + log.Printf("Failed to decode transaction: %v", err) + return +} +``` + +### 3. **Use Appropriate Timeouts** +```go +cfg := seth.DefaultConfig("http://localhost:8545", []string{privateKey}) +cfg.RPCTimeout = 30 * time.Second +cfg.TxTimeout = 5 * time.Minute +``` + +### 4. **Monitor Gas Prices** +```go +// Check gas price before sending transaction +gasPrice, err := client.SuggestGasPrice(context.Background()) +if err != nil { + log.Fatal(err) +} + +if gasPrice.Cmp(big.NewInt(100000000000)) > 0 { + log.Printf("High gas price: %s", gasPrice.String()) +} +``` + +### 5. **Use Tracing for Debugging** +```go +// Enable tracing for complex transactions +trace, err := client.TraceTransaction(context.Background(), txHash) +if err != nil { + log.Printf("Failed to trace transaction: %v", err) +} else { + log.Printf("Transaction trace: %+v", trace) +} +``` + +## Troubleshooting + +### Common Issues + +#### Transaction Decoding Fails +- Ensure you have the correct ABI for the contract +- Check if the contract address is correct +- Verify the transaction hash is valid + +#### Gas Estimation Issues +- Check if the RPC node supports gas estimation +- Verify the transaction parameters are valid +- Consider using manual gas price estimation + +#### Tracing Not Available +- Ensure your RPC node supports tracing +- Check if tracing is enabled on the node +- Use a different RPC endpoint if needed + +### Debug Mode + +Enable debug logging: + +```bash +export SETH_LOG_LEVEL=debug +go test -v +``` + +## API Reference + +For detailed API documentation, see the [Go package documentation](https://pkg.go.dev/github.com/smartcontractkit/chainlink-testing-framework/seth). + +## Contributing + +We welcome contributions! Please see the [Contributing Guide](../../Contributing/Overview) for details on: + +- Code of Conduct +- Development Setup +- Testing Guidelines +- Pull Request Process + +## License + +This project is licensed under the MIT License - see the [LICENSE](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/LICENSE) file for details. \ No newline at end of file diff --git a/Libraries/WASP.md b/Libraries/WASP.md new file mode 100644 index 000000000..75f543122 --- /dev/null +++ b/Libraries/WASP.md @@ -0,0 +1,532 @@ +# WASP - Load Testing Library + +
+ +[![Go Report Card](https://goreportcard.com/badge/github.com/smartcontractkit/wasp)](https://goreportcard.com/report/github.com/smartcontractkit/wasp) +[![Component Tests](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/wasp-test.yml/badge.svg)](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/wasp-test.yml) +[![E2E tests](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/wasp-test-e2e.yml/badge.svg)](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/wasp-test-e2e.yml) +![gopherbadger-tag-do-not-edit](https://img.shields.io/badge/Go%20Coverage-80%25-brightgreen.svg?longCache=true&style=flat) + +**Scalable protocol-agnostic load testing library for Go** + +
+ +## Overview + +WASP is a powerful load testing library designed for Go applications that need to test the performance and scalability of their systems. It's particularly well-suited for blockchain and Chainlink applications, but can be used for any protocol or service. + +## Goals + +- **Easy to reuse** any custom client Go code +- **Easy to grasp** - simple API with predictable behavior +- **Slim codebase** (500-1k lines of code) +- **No test harness or CLI** - easy to integrate and run with plain `go test` +- **Predictable performance footprint** - consistent resource usage +- **Easy to create synthetic or user-based scenarios** +- **Scalable in Kubernetes** without complicated configuration +- **Non-opinionated reporting** - push any data to Loki + +## Key Features + +### 1. **Protocol Agnostic** +- Works with any protocol (HTTP, gRPC, WebSocket, custom protocols) +- No built-in assumptions about the target system +- Easy to integrate existing client code + +### 2. **Simple API** +```go +// Basic usage +profile := wasp.NewProfile() +profile.Add(wasp.NewGenerator(config)) +profile.Run(true) +``` + +### 3. **Flexible Configuration** +- Support for various load patterns (constant, ramp-up, step, etc.) +- Configurable RPS (requests per second) and VU (virtual users) +- Time-based and iteration-based test duration + +### 4. **Observability Integration** +- Built-in Grafana dashboard support +- Loki integration for log aggregation +- Prometheus metrics export +- Custom metric collection + +### 5. **Kubernetes Ready** +- Designed for distributed load testing +- No complex UI dependencies +- Easy deployment and scaling + +## Installation + +### Using Go Modules +```bash +go get github.com/smartcontractkit/chainlink-testing-framework/wasp +``` + +### Using Nix (Recommended for Development) +```bash +# Install nix +curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install + +# Enter development shell +nix develop +``` + +## Quick Start + +### Basic Load Test + +```go +package main + +import ( + "context" + "log" + "time" + + "github.com/smartcontractkit/chainlink-testing-framework/wasp" +) + +func main() { + // Create a new load test profile + profile := wasp.NewProfile() + + // Define your load generator + generator := wasp.NewGenerator(&wasp.Config{ + T: 10 * time.Second, // Test duration + RPS: 100, // Requests per second + LoadType: wasp.RPS, + Schedule: wasp.Plain(100, 10*time.Second), + }) + + // Add your custom request function + generator.AddRequestFn(func(ctx context.Context) error { + // Your custom request logic here + // Example: HTTP request, blockchain transaction, etc. + return nil + }) + + // Add generator to profile + profile.Add(generator) + + // Run the load test + _, err := profile.Run(true) + if err != nil { + log.Fatal(err) + } +} +``` + +### HTTP Load Test + +```go +package main + +import ( + "context" + "log" + "net/http" + "time" + + "github.com/smartcontractkit/chainlink-testing-framework/wasp" +) + +func main() { + profile := wasp.NewProfile() + + // Create HTTP client + client := &http.Client{ + Timeout: 30 * time.Second, + } + + generator := wasp.NewGenerator(&wasp.Config{ + T: 30 * time.Second, + RPS: 50, + LoadType: wasp.RPS, + Schedule: wasp.Plain(50, 30*time.Second), + }) + + generator.AddRequestFn(func(ctx context.Context) error { + req, err := http.NewRequestWithContext(ctx, "GET", "http://localhost:8080/api/health", nil) + if err != nil { + return err + } + + resp, err := client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + return nil + }) + + profile.Add(generator) + + _, err := profile.Run(true) + if err != nil { + log.Fatal(err) + } +} +``` + +### Blockchain Load Test + +```go +package main + +import ( + "context" + "log" + "time" + + "github.com/ethereum/go-ethereum/ethclient" + "github.com/smartcontractkit/chainlink-testing-framework/wasp" +) + +func main() { + profile := wasp.NewProfile() + + // Connect to blockchain + client, err := ethclient.Dial("http://localhost:8545") + if err != nil { + log.Fatal(err) + } + + generator := wasp.NewGenerator(&wasp.Config{ + T: 60 * time.Second, + RPS: 10, // Lower RPS for blockchain + LoadType: wasp.RPS, + Schedule: wasp.Plain(10, 60*time.Second), + }) + + generator.AddRequestFn(func(ctx context.Context) error { + // Get latest block number + blockNumber, err := client.BlockNumber(ctx) + if err != nil { + return err + } + + // Your blockchain interaction logic here + log.Printf("Block number: %d", blockNumber) + return nil + }) + + profile.Add(generator) + + _, err = profile.Run(true) + if err != nil { + log.Fatal(err) + } +} +``` + +## Configuration Options + +### Load Types + +#### RPS (Requests Per Second) +```go +config := &wasp.Config{ + T: 30 * time.Second, + RPS: 100, + LoadType: wasp.RPS, + Schedule: wasp.Plain(100, 30*time.Second), +} +``` + +#### VU (Virtual Users) +```go +config := &wasp.Config{ + T: 30 * time.Second, + VU: 50, + LoadType: wasp.VU, + Schedule: wasp.Plain(50, 30*time.Second), +} +``` + +### Load Schedules + +#### Plain (Constant Load) +```go +Schedule: wasp.Plain(100, 30*time.Second) // 100 RPS for 30 seconds +``` + +#### Ramp Up +```go +Schedule: wasp.RampUp(0, 100, 10*time.Second) // Ramp from 0 to 100 RPS over 10 seconds +``` + +#### Step +```go +Schedule: wasp.Step(10, 50, 5*time.Second) // Step from 10 to 50 RPS every 5 seconds +``` + +#### Custom Schedule +```go +Schedule: wasp.Custom([]wasp.Step{ + {Duration: 10 * time.Second, RPS: 10}, + {Duration: 20 * time.Second, RPS: 50}, + {Duration: 10 * time.Second, RPS: 100}, +}) +``` + +## Observability Integration + +### Grafana Dashboard + +WASP provides built-in Grafana dashboard support: + +```go +profile := wasp.NewProfile() + +// Configure Grafana options +grafanaOpts := &wasp.GrafanaOpts{ + GrafanaURL: "http://localhost:3000", + GrafanaToken: "your-token", + AnnotateDashboardUIDs: []string{"wasp-dashboard"}, + CheckDashboardAlertsAfterRun: []string{"wasp-dashboard"}, +} + +// Add Grafana integration +profile.WithGrafana(grafanaOpts) + +// Run with dashboard annotations +_, err := profile.Run(true) +``` + +### Loki Integration + +Send logs to Loki for aggregation: + +```go +// Configure Loki +lokiConfig := &wasp.LokiConfig{ + URL: "http://localhost:3100/loki/api/v1/push", + TenantID: "test-tenant", + BatchWait: 5 * time.Second, + BatchSize: 500 * 1024, + Timeout: 20 * time.Second, +} + +// Add Loki to generator +generator.WithLoki(lokiConfig) +``` + +### Custom Metrics + +Collect custom metrics during your load test: + +```go +generator.AddRequestFn(func(ctx context.Context) error { + start := time.Now() + + // Your request logic here + + duration := time.Since(start) + + // Record custom metric + generator.RecordMetric("custom_duration", duration.Seconds()) + + return nil +}) +``` + +## Advanced Usage + +### Multiple Generators + +```go +profile := wasp.NewProfile() + +// Add different types of load +profile.Add(wasp.NewGenerator(&wasp.Config{ + T: 30 * time.Second, + RPS: 50, + LoadType: wasp.RPS, + Schedule: wasp.Plain(50, 30*time.Second), +})) + +profile.Add(wasp.NewGenerator(&wasp.Config{ + T: 30 * time.Second, + VU: 10, + LoadType: wasp.VU, + Schedule: wasp.Plain(10, 30*time.Second), +})) + +_, err := profile.Run(true) +``` + +### Conditional Requests + +```go +generator.AddRequestFn(func(ctx context.Context) error { + // Add some randomness to your requests + if rand.Float64() < 0.1 { + // 10% of requests are writes + return performWrite(ctx) + } else { + // 90% of requests are reads + return performRead(ctx) + } +}) +``` + +### Request Context + +```go +generator.AddRequestFn(func(ctx context.Context) error { + // Check if context is cancelled + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + // Your request logic here + return nil +}) +``` + +## Kubernetes Deployment + +### Basic Deployment + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: wasp-load-test +spec: + replicas: 3 + selector: + matchLabels: + app: wasp-load-test + template: + metadata: + labels: + app: wasp-load-test + spec: + containers: + - name: wasp + image: your-wasp-image:latest + env: + - name: TARGET_URL + value: "http://your-service:8080" + - name: TEST_DURATION + value: "300s" + - name: RPS + value: "100" +``` + +### Distributed Load Testing + +```go +// Configure for distributed testing +config := &wasp.Config{ + T: 300 * time.Second, + RPS: 100, + LoadType: wasp.RPS, + Schedule: wasp.Plain(100, 300*time.Second), + // Add distributed testing options + Distributed: true, + WorkerID: os.Getenv("WORKER_ID"), + CoordinatorURL: os.Getenv("COORDINATOR_URL"), +} +``` + +## Best Practices + +### 1. **Start Small** +- Begin with low RPS and short duration +- Gradually increase load to find system limits +- Monitor system resources during tests + +### 2. **Use Realistic Scenarios** +- Model real user behavior +- Include think time between requests +- Vary request types and parameters + +### 3. **Monitor System Health** +- Use the integrated observability stack +- Monitor target system metrics +- Set up alerts for critical thresholds + +### 4. **Test in Production-Like Environment** +- Use similar hardware and configuration +- Include all dependencies and services +- Test with realistic data volumes + +### 5. **Document Your Tests** +- Document test scenarios and parameters +- Record baseline performance metrics +- Track performance changes over time + +## Troubleshooting + +### Common Issues + +#### High Memory Usage +```go +// Reduce batch sizes +lokiConfig := &wasp.LokiConfig{ + BatchSize: 100 * 1024, // Smaller batches + BatchWait: 1 * time.Second, +} +``` + +#### Network Timeouts +```go +// Increase timeouts +client := &http.Client{ + Timeout: 60 * time.Second, + Transport: &http.Transport{ + IdleConnTimeout: 30 * time.Second, + }, +} +``` + +#### Loki Connection Issues +```bash +# Check Loki connectivity +curl -X GET "http://localhost:3100/ready" + +# Check WASP logs +WASP_LOG_LEVEL=trace go test -v +``` + +### Debug Mode + +Enable debug logging: + +```bash +export WASP_LOG_LEVEL=debug +go test -v +``` + +## Examples + +Check the [examples directory](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/wasp/examples) for more comprehensive examples: + +- [Basic HTTP load testing](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/wasp/examples/http) +- [Blockchain transaction testing](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/wasp/examples/blockchain) +- [Chainlink oracle testing](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/wasp/examples/chainlink) +- [Kubernetes deployment](https://github.com/smartcontractkit/chainlink-testing-framework/tree/main/wasp/examples/k8s) + +## API Reference + +For detailed API documentation, see the [Go package documentation](https://pkg.go.dev/github.com/smartcontractkit/chainlink-testing-framework/wasp). + +## Contributing + +We welcome contributions! Please see the [Contributing Guide](../../Contributing/Overview) for details on: + +- Code of Conduct +- Development Setup +- Testing Guidelines +- Pull Request Process + +## License + +This project is licensed under the MIT License - see the [LICENSE](https://github.com/smartcontractkit/chainlink-testing-framework/blob/main/LICENSE) file for details. \ No newline at end of file diff --git a/Reference/API-Reference.md b/Reference/API-Reference.md new file mode 100644 index 000000000..0b54e8819 --- /dev/null +++ b/Reference/API-Reference.md @@ -0,0 +1,75 @@ +# API Reference + +This page provides a high-level API reference for the main packages and components in the Chainlink Testing Framework (CTF). + +--- + +## Framework Core + +- **Package:** `github.com/smartcontractkit/chainlink-testing-framework/framework` +- **Key Types:** + - `func Load[T any](t *testing.T) (T, error)` – Loads TOML config into Go struct + - `type Logger` – Structured logging +- **Docs:** [GoDoc](https://pkg.go.dev/github.com/smartcontractkit/chainlink-testing-framework/framework) + +--- + +## Blockchain Components + +- **Package:** `github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain` +- **Key Types:** + - `type Input` – Blockchain config + - `func NewBlockchainNetwork(input *Input) (*Network, error)` + - `type Network` – Blockchain network instance +- **Docs:** [GoDoc](https://pkg.go.dev/github.com/smartcontractkit/chainlink-testing-framework/framework/components/blockchain) + +--- + +## Chainlink Node Components + +- **Package:** `github.com/smartcontractkit/chainlink-testing-framework/framework/components/clnode` +- **Key Types:** + - `type Input` – Chainlink node config + - `func NewChainlinkNode(input *Input) (*Node, error)` + - `type Node` – Chainlink node instance +- **Docs:** [GoDoc](https://pkg.go.dev/github.com/smartcontractkit/chainlink-testing-framework/framework/components/clnode) + +--- + +## WASP (Load Testing) + +- **Package:** `github.com/smartcontractkit/chainlink-testing-framework/wasp` +- **Key Types:** + - `type Profile` – Load test profile + - `type Generator` – Load generator + - `func NewProfile() *Profile` + - `func NewGenerator(cfg *Config) *Generator` +- **Docs:** [GoDoc](https://pkg.go.dev/github.com/smartcontractkit/chainlink-testing-framework/wasp) + +--- + +## Seth (Ethereum Client) + +- **Package:** `github.com/smartcontractkit/chainlink-testing-framework/seth` +- **Key Types:** + - `type Client` – Ethereum client + - `func NewClient(url string) (*Client, error)` + - `func NewClientWithConfig(cfg *Config) (*Client, error)` +- **Docs:** [GoDoc](https://pkg.go.dev/github.com/smartcontractkit/chainlink-testing-framework/seth) + +--- + +## Havoc (Chaos Testing) + +- **Package:** `github.com/smartcontractkit/chainlink-testing-framework/havoc` +- **Key Types:** + - `type Client` – Chaos Mesh client + - `type Chaos` – Chaos experiment + - `func NewClient() (*Client, error)` + - `func NewChaos(client *Client, exp interface{}) (*Chaos, error)` +- **Docs:** [GoDoc](https://pkg.go.dev/github.com/smartcontractkit/chainlink-testing-framework/havoc) + +--- + +## More +- For additional packages (e.g., postgres, s3provider, testreporters), see the [GoDoc index](https://pkg.go.dev/github.com/smartcontractkit/chainlink-testing-framework) \ No newline at end of file diff --git a/Reference/CLI-Reference.md b/Reference/CLI-Reference.md new file mode 100644 index 000000000..b7096e639 --- /dev/null +++ b/Reference/CLI-Reference.md @@ -0,0 +1,66 @@ +# CLI Reference + +This page documents the main commands and options for the Chainlink Testing Framework (CTF) CLI. + +--- + +## Overview + +The CTF CLI is used to manage test environments, observability stacks, and component lifecycles. It is distributed as a standalone binary (`framework`). + +--- + +## Main Commands + +### 1. Environment Management + +- `ctf obs up` – Start the observability stack (Grafana, Loki, Prometheus, Pyroscope) +- `ctf obs down` – Stop the observability stack +- `ctf bs up` – Start the Blockscout stack +- `ctf bs down` – Stop the Blockscout stack + +### 2. Component Management + +- `ctf d rm` – Remove all running containers and clean up resources +- `ctf d ls` – List running containers + +### 3. Test Execution + +- `go test -v` – Run tests (uses CTF config and environment) +- `CTF_CONFIGS=smoke.toml go test -v -run TestName` – Run a specific test with a config + +### 4. Configuration Validation + +- `ctf validate config.toml` – Validate a TOML configuration file + +--- + +## Common Flags + +- `--help` – Show help for any command +- `--version` – Show CLI version +- `--log-level` – Set log verbosity (e.g., `info`, `debug`) +- `--config` – Specify a config file (alternative to `CTF_CONFIGS`) + +--- + +## Usage Examples + +```bash +# Start observability stack +ctf obs up + +# Run a test with a specific config +CTF_CONFIGS=smoke.toml go test -v -run TestSmoke + +# Remove all containers +ctf d rm + +# Validate a config file +ctf validate myconfig.toml +``` + +--- + +## More +- For full CLI documentation, run `ctf --help` or see the [README](https://github.com/smartcontractkit/chainlink-testing-framework#readme) \ No newline at end of file diff --git a/Reference/Troubleshooting.md b/Reference/Troubleshooting.md new file mode 100644 index 000000000..3c7c9a55f --- /dev/null +++ b/Reference/Troubleshooting.md @@ -0,0 +1,80 @@ +# Troubleshooting Guide + +This page lists common issues, error messages, and solutions for the Chainlink Testing Framework (CTF). + +--- + +## 1. Docker Issues + +**Problem:** Docker containers fail to start or are slow. +- **Solution:** + - Ensure Docker is running and has enough resources (CPU, RAM). + - Use OrbStack for better performance on macOS. + - Run `ctf d rm` to clean up old containers. + - Check for port conflicts with `lsof -i :PORT`. + +--- + +## 2. Configuration Errors + +**Problem:** `missing required field` or `validation failed`. +- **Solution:** + - Check your TOML config for typos and required fields. + - Use `ctf validate config.toml` to validate your config. + - Ensure all `validate:"required"` fields are present in your Go struct. + +--- + +## 3. Caching Problems + +**Problem:** Component not updating or stale state. +- **Solution:** + - Set `use_cache = false` in your TOML or `export CTF_DISABLE_CACHE=true`. + - Delete the `.ctf_cache` directory and rerun tests. + +--- + +## 4. Observability Issues + +**Problem:** No logs or metrics in Grafana. +- **Solution:** + - Check that Loki and Prometheus containers are running (`docker ps`). + - Ensure your components are configured to send logs/metrics. + - Check Grafana data source settings. + +--- + +## 5. Test Failures + +**Problem:** Tests fail with `connection refused`, `timeout`, or `not ready` errors. +- **Solution:** + - Increase test timeouts (`go test -timeout 10m`). + - Use `require.Eventually` to wait for readiness. + - Check Docker resource limits and logs. + +--- + +## 6. CI/CD Integration + +**Problem:** Tests pass locally but fail in CI. +- **Solution:** + - Ensure all environment variables are set in CI. + - Use `CTF_DISABLE_CACHE=true` for clean runs. + - Check CI logs for missing dependencies or permissions. + +--- + +## 7. Miscellaneous + +- **Problem:** `permission denied` errors on files or Docker. + - **Solution:** Run with appropriate permissions or fix file ownership. +- **Problem:** `address already in use`. + - **Solution:** Free the port or change the config. + +--- + +## Getting Help +- Check the [README](https://github.com/smartcontractkit/chainlink-testing-framework#readme) +- Search [GitHub Issues](https://github.com/smartcontractkit/chainlink-testing-framework/issues) +- Ask in [Discussions](https://github.com/smartcontractkit/chainlink-testing-framework/discussions) +- Create a new issue with detailed logs and config \ No newline at end of file diff --git a/framework/CACHING.md b/framework/CACHING.md index 726bcb818..06b5f5984 100644 --- a/framework/CACHING.md +++ b/framework/CACHING.md @@ -1,3 +1,107 @@ +# Caching Guide + +Component caching is a powerful feature of the Chainlink Testing Framework (CTF) that enables faster test development by reusing previously deployed components and environments. + +## What is Component Caching? + +- **Component caching** allows you to skip the setup and deployment of components that have not changed since the last test run. +- Cached components are stored on disk and can be reused across multiple test runs, saving time and resources. +- Caching is especially useful for heavy components (e.g., blockchain networks, Chainlink nodes) that are expensive to start from scratch. + +## How Caching Works + +- Each component's configuration and output are hashed to create a unique cache key. +- If a component with the same configuration has already been deployed and cached, CTF will reuse the cached output instead of redeploying. +- If the configuration changes, the cache is invalidated and the component is redeployed. + +## Enabling Caching + +- Caching is enabled by default for most components. +- To enable caching for your custom component, include a `UseCache` field in the output struct and set it to `true`. + +### Example + +```go +type Output struct { + UseCache bool `toml:"use_cache"` + InternalURL string `toml:"internal_url"` + ExternalURL string `toml:"external_url"` +} + +func NewComponent(input *Input) (*Output, error) { + if input.Out != nil && input.Out.UseCache { + return input.Out, nil // Use cached output + } + // Deploy logic here + return &Output{ + UseCache: true, + InternalURL: "http://container:8545", + ExternalURL: "http://localhost:8545", + }, nil +} +``` + +### In TOML + +```toml +[blockchain_a] + type = "anvil" + use_cache = true +``` + +## Disabling Caching + +- To force a fresh deployment, set `use_cache = false` in your TOML or `UseCache: false` in your Go struct. +- You can also disable caching globally with the environment variable: + +```bash +export CTF_DISABLE_CACHE=true +``` + +## Cache Location + +- By default, caches are stored in a `.ctf_cache` directory in your project root. +- You can configure the cache directory via environment variable: + +```bash +export CTF_CACHE_DIR=/path/to/cache +``` + +## When to Use Caching + +- **During development**: Rapidly iterate on tests without waiting for full environment setup +- **For stable components**: Reuse networks, databases, or nodes that don't change between tests +- **In CI/CD**: Use with care; ensure cache invalidation on config changes + +## When Not to Use Caching + +- **When testing upgrades or migrations**: Always start from a clean state +- **When debugging setup issues**: Disable cache to ensure fresh deployments +- **For tests that require full isolation**: Use fresh environments for each run + +## Best Practices + +- **Enable caching** for heavy, stable components during local development +- **Disable caching** for critical tests, upgrades, or when debugging +- **Document** which components are cacheable in your test README +- **Clean cache** periodically to avoid stale state: + +```bash +rm -rf .ctf_cache +``` + +## Troubleshooting + +- **Component not updating**: Check if `use_cache` is set to `true` and try disabling it +- **Stale state**: Clean the cache directory and rerun tests +- **Cache not being used**: Ensure `UseCache` is set in the output struct and TOML + +## Further Reading +- [Component System](Components) +- [Configuration Guide](Configuration) +- [Observability Guide](Observability) +- [Test Patterns](Test-Patterns) + ## Component caching We use component caching to accelerate test development and enforce idempotent test actions. diff --git a/framework/CONFIGURATION.md b/framework/CONFIGURATION.md index e1e2a9023..6c36f0904 100644 --- a/framework/CONFIGURATION.md +++ b/framework/CONFIGURATION.md @@ -1,3 +1,152 @@ +# Configuration Guide + +Configuration is at the heart of the Chainlink Testing Framework (CTF). All components, networks, and services are configured using TOML files, which are loaded into Go structs for type-safe, validated test setup. + +## Configuration Basics + +- **TOML files** define the structure and parameters for all components in your test environment. +- **Go structs** mirror the TOML structure and provide validation. +- **Environment variables** can override or supplement TOML values for dynamic configuration. + +## Example: Minimal Configuration + +```toml +[blockchain_a] + type = "anvil" + image = "ghcr.io/foundry-rs/foundry" + tag = "latest" + pull_image = true + +[chainlink_node] + image = "public.ecr.aws/chainlink/chainlink" + tag = "2.7.0" + pull_image = true +``` + +## Loading Configuration in Go + +```go +type Config struct { + BlockchainA *blockchain.Input `toml:"blockchain_a" validate:"required"` + ChainlinkNode *clnode.Input `toml:"chainlink_node" validate:"required"` +} + +func TestMyTest(t *testing.T) { + in, err := framework.Load[Config](t) + require.NoError(t, err) + // Use in.BlockchainA, in.ChainlinkNode, etc. +} +``` + +## TOML Structure + +- Each top-level section represents a component or service. +- Fields map directly to struct fields in Go. +- Nested tables and arrays are supported for complex configurations. + +### Example: Multi-Node, Multi-Chain + +```toml +[blockchain_a] + type = "anvil" + image = "ghcr.io/foundry-rs/foundry" + tag = "latest" + pull_image = true + +[blockchain_b] + type = "geth" + image = "ethereum/client-go" + tag = "v1.12.0" + pull_image = true + +[[chainlink_nodes]] + image = "public.ecr.aws/chainlink/chainlink" + tag = "2.7.0" + pull_image = true + +[[chainlink_nodes]] + image = "public.ecr.aws/chainlink/chainlink" + tag = "2.7.0" + pull_image = true +``` + +## Field Types + +- **Strings**: `image = "..."`, `tag = "..."` +- **Booleans**: `pull_image = true` +- **Integers**: `port = 8545` +- **Arrays**: `urls = ["http://localhost:8545", "http://localhost:8546"]` +- **Tables**: Nested configuration for complex components + +## Validation + +- Use struct tags like `validate:"required"` to enforce required fields. +- CTF will fail fast if required fields are missing or invalid. + +## Environment Variables + +You can override or supplement TOML configuration with environment variables. This is useful for secrets, dynamic values, or CI/CD integration. + +### Common Environment Variables + +- `CTF_CONFIGS` – Path to the TOML config file(s) +- `CTF_LOG_LEVEL` – Set log verbosity (`info`, `debug`, `warn`, `error`) +- `CTF_DISABLE_CACHE` – Disable component caching (`true`/`false`) +- `CTF_NETWORK_NAME` – Set Docker network name +- `CTF_OBSERVABILITY` – Enable/disable observability stack +- `CTF_GRAFANA_URL` – Override Grafana URL +- `CTF_LOKI_URL` – Override Loki URL +- `CTF_PROMETHEUS_URL` – Override Prometheus URL + +### Example Usage + +```bash +CTF_CONFIGS=smoke.toml CTF_LOG_LEVEL=debug go test -v -run TestSmoke +``` + +## Advanced Configuration + +### 1. **Component Caching** +- Use `out.use_cache = true` in TOML or `UseCache` in Go to enable caching +- See [Caching Guide](Caching) for details + +### 2. **Secrets Management** +- Store secrets in environment variables or external files +- Reference them in TOML using `${ENV_VAR}` syntax if supported by your loader + +### 3. **Multiple Config Files** +- You can specify multiple config files with `CTF_CONFIGS="a.toml,b.toml"` +- Later files override earlier ones for the same fields + +### 4. **Dynamic Configuration in CI/CD** +- Use environment variables to inject dynamic values (e.g., image tags, URLs) +- Example: + ```bash + CTF_CONFIGS=ci.toml CTF_IMAGE_TAG=$GITHUB_SHA go test -v + ``` + +## Best Practices + +- **Keep configs minimal**: Only specify what you need for the test +- **Use arrays/tables** for repeated components (e.g., multiple nodes) +- **Validate** all required fields in your Go structs +- **Document** your config files for team clarity +- **Use environment variables** for secrets and dynamic values +- **Version control** your config files (except secrets) + +## Troubleshooting + +- **Missing fields**: Check for typos or missing required fields in TOML +- **Validation errors**: Ensure all `validate:"required"` fields are present +- **Environment overrides not working**: Confirm variable names and export status +- **Multiple configs**: Order matters; last file wins for duplicate fields + +## Further Reading +- [Component System](Components) +- [Caching Guide](Caching) +- [Observability Guide](Observability) +- [Test Patterns](Test-Patterns) + ## Test Configuration In our framework, components control the configuration. Each component defines an Input that you embed into your test configuration. We automatically ensure consistency between TOML and struct definitions during validation. diff --git a/framework/Observability.md b/framework/Observability.md new file mode 100644 index 000000000..60a0829a7 --- /dev/null +++ b/framework/Observability.md @@ -0,0 +1,136 @@ +# Observability Guide + +Observability is a core feature of the Chainlink Testing Framework (CTF). The framework provides an integrated stack for monitoring, logging, tracing, and profiling your tests and environments. + +## Why Observability? + +- **Debugging**: Quickly identify issues in your tests or deployed components +- **Performance Analysis**: Monitor resource usage, latency, and throughput +- **Reliability**: Detect failures, bottlenecks, and regressions +- **Transparency**: Gain insight into system behavior during tests + +## Observability Stack + +CTF integrates with the following tools: + +- **Grafana**: Dashboards and visualization +- **Loki**: Log aggregation +- **Prometheus**: Metrics collection +- **Jaeger**: Distributed tracing +- **Pyroscope**: Performance profiling + +## Quick Start + +### 1. Spin Up the Observability Stack + +Use the CTF CLI to start the observability stack locally: + +```bash +ctf obs up +``` + +This will launch Grafana, Loki, Prometheus, and Pyroscope (if configured) in Docker containers. + +### 2. Access Grafana + +- Open [http://localhost:3000](http://localhost:3000) in your browser +- Default credentials: `admin` / `admin` (change after first login) +- Explore pre-built dashboards for blockchain, Chainlink nodes, and test metrics + +### 3. Log and Metric Collection + +- All logs from components are sent to Loki +- Metrics are scraped by Prometheus +- Custom metrics and logs can be pushed from your tests + +## Configuration + +You can configure observability endpoints in your TOML or via environment variables: + +```toml +[observability] + grafana_url = "http://localhost:3000" + loki_url = "http://localhost:3100" + prometheus_url = "http://localhost:9090" + pyroscope_url = "http://localhost:4040" +``` + +Or via environment variables: + +```bash +export CTF_GRAFANA_URL=http://localhost:3000 +export CTF_LOKI_URL=http://localhost:3100 +export CTF_PROMETHEUS_URL=http://localhost:9090 +export CTF_PYROSCOPE_URL=http://localhost:4040 +``` + +## Using Observability in Tests + +### Logging + +- Use the built-in logger (`framework/logging`) for structured logs +- All logs are automatically sent to Loki +- You can add custom log fields for better filtering in Grafana + +### Metrics + +- Expose custom metrics from your components or tests +- Use Prometheus client libraries to define and push metrics +- Metrics are visualized in Grafana dashboards + +### Tracing + +- Distributed tracing is enabled for supported components +- Use Jaeger UI to view traces ([http://localhost:16686](http://localhost:16686)) +- Trace requests across services and components + +### Profiling + +- Pyroscope collects CPU and memory profiles +- Access Pyroscope UI at [http://localhost:4040](http://localhost:4040) +- Analyze performance bottlenecks + +## Grafana Dashboards + +- Pre-built dashboards for blockchain, Chainlink nodes, and test metrics +- Custom dashboards can be created for your use case +- Use dashboard annotations to mark test events (e.g., chaos experiments, load tests) + +### Example: Annotating Dashboards + +```go +import ( + "github.com/smartcontractkit/chainlink-testing-framework/wasp" +) + +profile := wasp.NewProfile() +grafanaOpts := &wasp.GrafanaOpts{ + GrafanaURL: "http://localhost:3000", + GrafanaToken: "your-token", + AnnotateDashboardUIDs: []string{"dashboard-uid"}, +} +profile.WithGrafana(grafanaOpts) +``` + +## Best Practices + +- **Always enable observability** in CI and local runs +- **Tag logs and metrics** with test names, component names, and environment +- **Use dashboard annotations** for key events (start/end of tests, chaos, upgrades) +- **Monitor resource usage** to detect leaks or bottlenecks +- **Set up alerts** in Grafana for critical metrics (e.g., error rates, latency) + +## Troubleshooting + +- **No logs in Grafana**: Check Loki container status and log configuration +- **No metrics in dashboards**: Ensure Prometheus is scraping targets +- **No traces in Jaeger**: Verify tracing is enabled and endpoints are correct +- **Pyroscope not collecting**: Check agent configuration and endpoint + +## Further Reading +- [Component System](Components) +- [Configuration Guide](Configuration) +- [Caching Guide](Caching) +- [Test Patterns](Test-Patterns) +- [WASP Load Testing](../Libraries/WASP) +- [Havoc Chaos Testing](../Libraries/Havoc) \ No newline at end of file diff --git a/framework/Overview.md b/framework/Overview.md new file mode 100644 index 000000000..8988d88c1 --- /dev/null +++ b/framework/Overview.md @@ -0,0 +1,252 @@ +# Framework Overview + +The Chainlink Testing Framework (CTF) is designed to reduce the complexity of end-to-end testing, making complex system-level tests appear straightforward. It enables tests to run in any environment and serves as a single source of truth for system behavior as defined by requirements. + +## Core Philosophy + +### 1. **Straightforward and Sequential Test Composition** +Tests are readable and give you precise control over key aspects in a strict step-by-step order. No hidden magic or complex abstractions. + +### 2. **Modular Configuration** +No arcane knowledge of framework settings is required. The configuration is simply a reflection of the components being used in the test. Components declare their own configuration—**what you see is what you get**. + +### 3. **Component Isolation** +Components are decoupled via input/output structs, without exposing internal details. This allows for easy testing and replacement of individual components. + +### 4. **Replaceability and Extensibility** +Since components are decoupled via outputs, any deployment component can be swapped with a real service without altering the test code. + +### 5. **Quick Local Environments** +A common setup can be launched in just **15 seconds** 🚀 (when using caching). + +### 6. **Integrated Observability Stack** +Get all the information you need to develop end-to-end tests: metrics, logs, traces, and profiles. + +## Architecture + +### Component-Based Design + +CTF uses a component-based architecture where each component represents a service or system that can be deployed and tested: + +``` +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ Blockchain │ │ Chainlink Node │ │ Database │ +│ Component │ │ Component │ │ Component │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ + │ │ │ + └───────────────────────┼───────────────────────┘ + │ + ┌─────────────────┐ + │ Test Logic │ + │ (Your Code) │ + └─────────────────┘ +``` + +### Configuration-Driven Testing + +Tests are driven by TOML configuration files that define what components to use and how to configure them: + +```toml +[blockchain_a] + type = "anvil" + +[chainlink_node] + image = "public.ecr.aws/chainlink/chainlink" + tag = "2.7.0" + pull_image = true + +[postgres] + image = "postgres:15" + tag = "15" + pull_image = true +``` + +### Input/Output Pattern + +Each component follows a consistent input/output pattern: + +```go +type Input struct { + // Configuration fields + Image string `toml:"image" validate:"required"` + Tag string `toml:"tag" validate:"required"` + PullImage bool `toml:"pull_image"` + + // Output is embedded for caching + Out *Output `toml:"out"` +} + +type Output struct { + UseCache bool `toml:"use_cache"` + URL string `toml:"url"` + // Other output fields +} +``` + +## Key Features + +### 1. **Docker Integration** +- Automatic container management with [testcontainers-go](https://golang.testcontainers.org/) +- Built-in networking and service discovery +- Automatic cleanup and resource management + +### 2. **Multi-Blockchain Support** +- **Ethereum**: Anvil, Geth, Besu +- **Solana**: Local validator +- **TON**: Local blockchain +- **Aptos**: Local blockchain +- **Sui**: Local blockchain +- **Tron**: Local blockchain + +### 3. **Chainlink Integration** +- Chainlink node deployment and configuration +- Job management and monitoring +- Oracle contract integration +- Multi-node setups (DONs) + +### 4. **Observability Stack** +- **Grafana**: Dashboards and visualization +- **Loki**: Log aggregation +- **Prometheus**: Metrics collection +- **Jaeger**: Distributed tracing +- **Pyroscope**: Performance profiling + +### 5. **Caching System** +- Component state caching for faster test development +- Configurable cache invalidation +- Cross-test cache sharing + +### 6. **CLI Tools** +- Environment management (`ctf obs up`, `ctf bs up`) +- Container cleanup (`ctf d rm`) +- Configuration validation +- Test execution helpers + +## Component Types + +### 1. **Blockchain Components** +- Local blockchain networks for testing +- Support for multiple blockchain types +- Automatic network configuration + +### 2. **Chainlink Components** +- Chainlink node deployment +- Job management +- Oracle contract integration + +### 3. **Infrastructure Components** +- Databases (PostgreSQL, etc.) +- Message queues +- External services +- Monitoring stacks + +### 4. **Custom Components** +- User-defined components +- Integration with external services +- Specialized testing requirements + +## Test Lifecycle + +### 1. **Configuration Loading** +```go +in, err := framework.Load[Config](t) +require.NoError(t, err) +``` + +### 2. **Component Creation** +```go +bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA) +require.NoError(t, err) +``` + +### 3. **Test Execution** +```go +t.Run("test functionality", func(t *testing.T) { + // Your test logic here +}) +``` + +### 4. **Automatic Cleanup** +CTF automatically cleans up resources when tests complete. + +## Environment Support + +### Local Development +- Docker-based local environments +- Fast startup with caching +- Integrated observability + +### CI/CD Integration +- GitHub Actions support +- Parallel test execution +- Resource optimization + +### Kubernetes +- K8s test runner for distributed testing +- Chaos testing with Chaos Mesh +- Scalable test environments + +## Performance Characteristics + +### Startup Time +- **First run**: ~30-60 seconds (depending on Docker image pulls) +- **Cached run**: ~15 seconds +- **Component reuse**: ~5 seconds + +### Resource Usage +- **Memory**: 2-8GB depending on components +- **CPU**: 2-4 cores recommended +- **Storage**: 10-20GB for Docker images and data + +### Scalability +- **Parallel tests**: Full support with resource isolation +- **Large deployments**: Support for complex multi-component setups +- **Distributed testing**: Kubernetes integration for scale-out + +## Best Practices + +### 1. **Configuration Management** +- Use descriptive configuration names +- Validate required fields +- Document configuration options + +### 2. **Test Organization** +- Use subtests for logical grouping +- Keep tests focused and readable +- Use meaningful test names + +### 3. **Resource Management** +- Leverage caching for faster development +- Clean up resources appropriately +- Monitor resource usage + +### 4. **Observability** +- Use the integrated observability stack +- Add custom metrics and logs +- Monitor test performance + +## Comparison with Other Frameworks + +| Feature | CTF | Traditional E2E | Unit Testing | +|---------|-----|-----------------|--------------| +| **Setup Complexity** | Low | High | Very Low | +| **Environment Management** | Automatic | Manual | None | +| **Component Isolation** | Built-in | Manual | Built-in | +| **Observability** | Integrated | External | Limited | +| **Multi-Service Testing** | Native | Complex | Not Applicable | +| **Blockchain Integration** | First-class | External | Not Applicable | + +## Getting Started + +1. **[Installation](../Getting-Started/Installation)** - Set up your development environment +2. **[First Test](../Getting-Started/First-Test)** - Write your first CTF test +3. **[Components](Components)** - Learn about available components +4. **[Configuration](Configuration)** - Understand configuration options +5. **[Observability](Observability)** - Set up monitoring and logging + +## Next Steps + +- Explore the **[Component System](Components)** to understand available components +- Learn about **[Configuration Management](Configuration)** for complex setups +- Set up **[Observability](Observability)** for better debugging +- Check out **[Advanced Patterns](Test-Patterns)** for complex scenarios \ No newline at end of file diff --git a/framework/Test-Patterns.md b/framework/Test-Patterns.md new file mode 100644 index 000000000..499e5bcb8 --- /dev/null +++ b/framework/Test-Patterns.md @@ -0,0 +1,145 @@ +# Test Patterns + +This guide covers common and advanced test patterns for writing robust, maintainable, and efficient tests with the Chainlink Testing Framework (CTF). + +## Subtests + +Subtests help organize your test logic and make it easier to debug and maintain. Use `t.Run` to group related assertions and steps. + +### Example +```go +func TestEndToEnd(t *testing.T) { + in, err := framework.Load[Config](t) + require.NoError(t, err) + bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA) + require.NoError(t, err) + + t.Run("blockchain is running", func(t *testing.T) { + require.NotEmpty(t, bc.Nodes[0].ExternalHTTPUrl) + }) + + t.Run("chainlink node is running", func(t *testing.T) { + cl, err := clnode.NewChainlinkNode(in.ChainlinkNode) + require.NoError(t, err) + require.NotEmpty(t, cl.ExternalURL) + }) +} +``` + +## Parallelism + +CTF supports parallel test execution for faster feedback and better resource utilization. Use `t.Parallel()` to enable parallelism. + +### Example +```go +func TestParallel(t *testing.T) { + t.Parallel() + in, err := framework.Load[Config](t) + require.NoError(t, err) + // ... rest of test +} +``` + +- Use parallelism for independent tests that do not share resources. +- Be careful with shared state (e.g., Docker networks, files). + +## Cleanup + +CTF automatically cleans up Docker containers and resources after tests. You can add custom cleanup logic using `t.Cleanup`. + +### Example +```go +func TestWithCleanup(t *testing.T) { + in, err := framework.Load[Config](t) + require.NoError(t, err) + bc, err := blockchain.NewBlockchainNetwork(in.BlockchainA) + require.NoError(t, err) + + t.Cleanup(func() { + // Custom cleanup logic + log.Println("Cleaning up custom resources") + }) +} +``` + +## Table-Driven Tests + +Table-driven tests are useful for running the same logic with different inputs. + +### Example +```go +testCases := []struct { + name string + config string +}{ + {"anvil", "anvil.toml"}, + {"geth", "geth.toml"}, +} + +for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + in, err := framework.LoadConfig(tc.config) + require.NoError(t, err) + // ... test logic + }) +} +``` + +## Advanced Patterns + +### 1. **Retry Logic** +Use retry logic for flaky external dependencies. +```go +require.Eventually(t, func() bool { + // Check condition + return isReady() +}, 30*time.Second, 2*time.Second) +``` + +### 2. **Timeouts** +Set timeouts for long-running tests. +```bash +go test -v -timeout 10m +``` + +### 3. **Dynamic Configuration** +Use environment variables or flags to inject dynamic values. +```bash +CTF_CONFIGS=smoke.toml CTF_LOG_LEVEL=debug go test -v -run TestSmoke +``` + +### 4. **Observability in Tests** +Add custom logs and metrics for better debugging. +```go +log := logging.NewLogger() +log.Info().Msg("Starting test") +``` + +### 5. **Combining Load and Chaos** +Combine WASP load tests and Havoc chaos experiments for resilience testing. +```go +go func() { + // Start chaos + havoc.NewChaos(...).Create(context.Background()) +}() +profile.Run(true) // WASP load test +``` + +## Best Practices + +- **Organize tests** with subtests and table-driven patterns +- **Use parallelism** for independent tests +- **Clean up** resources with `t.Cleanup` +- **Add observability** (logs, metrics, traces) to all tests +- **Document** test cases and expected outcomes +- **Use caching** for faster development, but disable for critical tests +- **Monitor** resource usage and test performance + +## Further Reading +- [Component System](Components) +- [Configuration Guide](Configuration) +- [Observability Guide](Observability) +- [Caching Guide](Caching) +- [WASP Load Testing](../Libraries/WASP) +- [Havoc Chaos Testing](../Libraries/Havoc) \ No newline at end of file diff --git a/framework/cmd/blockscout/docker-compose.yml b/framework/cmd/blockscout/docker-compose.yml new file mode 100755 index 000000000..975a66aa6 --- /dev/null +++ b/framework/cmd/blockscout/docker-compose.yml @@ -0,0 +1,88 @@ +services: + redis-db: + extends: + file: ./services/redis.yml + service: redis-db + + db-init: + extends: + file: ./services/db.yml + service: db-init + + db: + depends_on: + db-init: + condition: service_completed_successfully + extends: + file: ./services/db.yml + service: db + + backend: + depends_on: + - db + - redis-db + extends: + file: ./services/backend.yml + service: backend + links: + - db:database + environment: + ETHEREUM_JSONRPC_VARIANT: 'geth' + ETHEREUM_JSONRPC_HTTP_URL: ${BLOCKSCOUT_RPC_URL} + ETHEREUM_JSONRPC_TRACE_URL: ${BLOCKSCOUT_RPC_URL} + CHAIN_ID: ${BLOCKSCOUT_CHAIN_ID:-1337} + + visualizer: + extends: + file: ./services/visualizer.yml + service: visualizer + + sig-provider: + extends: + file: ./services/sig-provider.yml + service: sig-provider + + frontend: + depends_on: + - backend + extends: + file: ./services/frontend.yml + service: frontend + + stats-db-init: + extends: + file: ./services/stats.yml + service: stats-db-init + + stats-db: + depends_on: + stats-db-init: + condition: service_completed_successfully + extends: + file: ./services/stats.yml + service: stats-db + + stats: + depends_on: + - stats-db + - backend + extends: + file: ./services/stats.yml + service: stats + + user-ops-indexer: + depends_on: + - db + - backend + extends: + file: ./services/user-ops-indexer.yml + service: user-ops-indexer + + proxy: + depends_on: + - backend + - frontend + - stats + extends: + file: ./services/nginx.yml + service: proxy diff --git a/framework/cmd/blockscout/envs/common-blockscout.env b/framework/cmd/blockscout/envs/common-blockscout.env new file mode 100755 index 000000000..35872c4e7 --- /dev/null +++ b/framework/cmd/blockscout/envs/common-blockscout.env @@ -0,0 +1,468 @@ +ETHEREUM_JSONRPC_VARIANT=geth +ETHEREUM_JSONRPC_HTTP_URL=http://host.docker.internal:8545/ +# ETHEREUM_JSONRPC_FALLBACK_HTTP_URL= +DATABASE_URL=postgresql://blockscout:ceWb1MeLBEeOIfk65gU8EjF8@db:5432/blockscout +# DATABASE_EVENT_URL= +# DATABASE_QUEUE_TARGET +# TEST_DATABASE_URL= +# TEST_DATABASE_READ_ONLY_API_URL= +ETHEREUM_JSONRPC_TRACE_URL=http://host.docker.internal:8545/ +# ETHEREUM_JSONRPC_FALLBACK_TRACE_URL= +# ETHEREUM_JSONRPC_FALLBACK_ETH_CALL_URL= +# ETHEREUM_JSONRPC_ETH_CALL_URL= +# ETHEREUM_JSONRPC_HTTP_TIMEOUT= +# CHAIN_TYPE= +NETWORK= +SUBNETWORK=Awesome chain +LOGO=/images/blockscout_logo.svg +# ETHEREUM_JSONRPC_WS_URL= +# ETHEREUM_JSONRPC_FALLBACK_WS_URL= +# ETHEREUM_JSONRPC_WS_RETRY_INTERVAL= +ETHEREUM_JSONRPC_TRANSPORT=http +ETHEREUM_JSONRPC_DISABLE_ARCHIVE_BALANCES=false +# ETHEREUM_JSONRPC_ARCHIVE_BALANCES_WINDOW=200 +# ETHEREUM_JSONRPC_HTTP_HEADERS= +# ETHEREUM_JSONRPC_WAIT_PER_TIMEOUT= +# ETHEREUM_JSONRPC_GETH_TRACE_BY_BLOCK= +# ETHEREUM_JSONRPC_GETH_ALLOW_EMPTY_TRACES= +IPC_PATH= +NETWORK_PATH=/ +BLOCKSCOUT_HOST= +BLOCKSCOUT_PROTOCOL= +SECRET_KEY_BASE=56NtB48ear7+wMSf0IQuWDAAazhpb31qyc7GiyspBP2vh7t5zlCsF5QDv76chXeN +# CHECK_ORIGIN= +PORT=4000 +COIN_NAME= +# METADATA_CONTRACT= +# VALIDATORS_CONTRACT= +# KEYS_MANAGER_CONTRACT= +# REWARDS_CONTRACT= +# TOKEN_BRIDGE_CONTRACT= +EMISSION_FORMAT=DEFAULT +# CHAIN_SPEC_PATH= +# SUPPLY_MODULE= +COIN= +EXCHANGE_RATES_COIN= +# EXCHANGE_RATES_SOURCE= +# EXCHANGE_RATES_SECONDARY_COIN_SOURCE= +# EXCHANGE_RATES_MARKET_CAP_SOURCE= +# EXCHANGE_RATES_TVL_SOURCE= +# EXCHANGE_RATES_PRICE_SOURCE= +# EXCHANGE_RATES_COINGECKO_COIN_ID= +# EXCHANGE_RATES_COINGECKO_SECONDARY_COIN_ID= +# EXCHANGE_RATES_COINGECKO_API_KEY= +# EXCHANGE_RATES_COINGECKO_BASE_URL= +# EXCHANGE_RATES_COINGECKO_BASE_PRO_URL= +# EXCHANGE_RATES_COINMARKETCAP_BASE_URL= +# EXCHANGE_RATES_COINMARKETCAP_API_KEY= +# EXCHANGE_RATES_COINMARKETCAP_COIN_ID= +# EXCHANGE_RATES_COINMARKETCAP_SECONDARY_COIN_ID= +# EXCHANGE_RATES_CRYPTOCOMPARE_SECONDARY_COIN_SYMBOL= +# EXCHANGE_RATES_CRYPTORANK_SECONDARY_COIN_ID= +# EXCHANGE_RATES_CRYPTORANK_PLATFORM_ID= +# EXCHANGE_RATES_CRYPTORANK_BASE_URL= +# EXCHANGE_RATES_CRYPTORANK_API_KEY= +# EXCHANGE_RATES_CRYPTORANK_COIN_ID= +# EXCHANGE_RATES_CRYPTORANK_LIMIT= +# TOKEN_EXCHANGE_RATES_SOURCE= +# EXCHANGE_RATES_COINGECKO_PLATFORM_ID= +# TOKEN_EXCHANGE_RATE_INTERVAL= +# TOKEN_EXCHANGE_RATE_REFETCH_INTERVAL= +# TOKEN_EXCHANGE_RATE_MAX_BATCH_SIZE= +# DISABLE_TOKEN_EXCHANGE_RATE= +POOL_SIZE=80 +POOL_SIZE_API=10 +ECTO_USE_SSL=false +# DATADOG_HOST= +# DATADOG_PORT= +# SPANDEX_BATCH_SIZE= +# SPANDEX_SYNC_THRESHOLD= +HEART_BEAT_TIMEOUT=30 +# HEART_COMMAND= +# BLOCKSCOUT_VERSION= +RELEASE_LINK= +BLOCK_TRANSFORMER=base +# BLOCK_RANGES= +# FIRST_BLOCK= +# LAST_BLOCK= +# TRACE_BLOCK_RANGES= +# TRACE_FIRST_BLOCK= +# TRACE_LAST_BLOCK= +# FOOTER_CHAT_LINK= +# FOOTER_FORUM_LINK_ENABLED= +# FOOTER_FORUM_LINK= +# FOOTER_TELEGRAM_LINK_ENABLED= +# FOOTER_TELEGRAM_LINK= +# FOOTER_GITHUB_LINK= +FOOTER_LOGO=/images/blockscout_logo.svg +FOOTER_LINK_TO_OTHER_EXPLORERS=false +FOOTER_OTHER_EXPLORERS={} +SUPPORTED_CHAINS={} +CACHE_BLOCK_COUNT_PERIOD=7200 +CACHE_TXS_COUNT_PERIOD=7200 +CACHE_ADDRESS_SUM_PERIOD=3600 +CACHE_TOTAL_GAS_USAGE_PERIOD=3600 +CACHE_ADDRESS_TRANSACTIONS_GAS_USAGE_COUNTER_PERIOD=1800 +CACHE_TOKEN_HOLDERS_COUNTER_PERIOD=3600 +CACHE_TOKEN_TRANSFERS_COUNTER_PERIOD=3600 +CACHE_ADDRESS_WITH_BALANCES_UPDATE_INTERVAL=1800 +CACHE_AVERAGE_BLOCK_PERIOD=1800 +CACHE_MARKET_HISTORY_PERIOD=21600 +CACHE_ADDRESS_TRANSACTIONS_COUNTER_PERIOD=1800 +CACHE_ADDRESS_TOKENS_USD_SUM_PERIOD=3600 +CACHE_ADDRESS_TOKEN_TRANSFERS_COUNTER_PERIOD=1800 +# CACHE_TRANSACTIONS_24H_STATS_PERIOD= +# CACHE_FRESH_PENDING_TRANSACTIONS_COUNTER_PERIOD= +# TOKEN_BALANCE_ON_DEMAND_FETCHER_THRESHOLD= +# COIN_BALANCE_ON_DEMAND_FETCHER_THRESHOLD= +# CONTRACT_CODE_ON_DEMAND_FETCHER_THRESHOLD= +# TOKEN_INSTANCE_METADATA_REFETCH_ON_DEMAND_FETCHER_THRESHOLD= +TOKEN_METADATA_UPDATE_INTERVAL=172800 +CONTRACT_VERIFICATION_ALLOWED_SOLIDITY_EVM_VERSIONS=homestead,tangerineWhistle,spuriousDragon,byzantium,constantinople,petersburg,istanbul,berlin,london,paris,shanghai,cancun,default +CONTRACT_VERIFICATION_ALLOWED_VYPER_EVM_VERSIONS=byzantium,constantinople,petersburg,istanbul,berlin,paris,shanghai,cancun,default +# CONTRACT_VERIFICATION_MAX_LIBRARIES=10 +CONTRACT_MAX_STRING_LENGTH_WITHOUT_TRIMMING=2040 +# CONTRACT_DISABLE_INTERACTION= +# CONTRACT_AUDIT_REPORTS_AIRTABLE_URL= +# CONTRACT_AUDIT_REPORTS_AIRTABLE_API_KEY= +# CONTRACT_CERTIFIED_LIST= +UNCLES_IN_AVERAGE_BLOCK_TIME=false +DISABLE_WEBAPP=false +API_V2_ENABLED=true +API_V1_READ_METHODS_DISABLED=false +API_V1_WRITE_METHODS_DISABLED=false +# API_RATE_LIMIT_DISABLED=true +# API_SENSITIVE_ENDPOINTS_KEY= +API_RATE_LIMIT_TIME_INTERVAL=1s +API_RATE_LIMIT_BY_IP_TIME_INTERVAL=5m +API_RATE_LIMIT=50 +API_RATE_LIMIT_BY_KEY=50 +API_RATE_LIMIT_BY_WHITELISTED_IP=50 +API_RATE_LIMIT_WHITELISTED_IPS= +API_RATE_LIMIT_STATIC_API_KEY= +API_RATE_LIMIT_UI_V2_WITH_TOKEN=5 +API_RATE_LIMIT_BY_IP=3000 +API_NO_RATE_LIMIT_API_KEY= +# API_GRAPHQL_ENABLED= +# API_GRAPHQL_MAX_COMPLEXITY= +# API_GRAPHQL_TOKEN_LIMIT= +# API_GRAPHQL_DEFAULT_TRANSACTION_HASH= +# API_GRAPHQL_RATE_LIMIT_DISABLED= +# API_GRAPHQL_RATE_LIMIT= +# API_GRAPHQL_RATE_LIMIT_BY_KEY= +# API_GRAPHQL_RATE_LIMIT_TIME_INTERVAL= +# API_GRAPHQL_RATE_LIMIT_BY_IP= +# API_GRAPHQL_RATE_LIMIT_BY_IP_TIME_INTERVAL= +# API_GRAPHQL_RATE_LIMIT_STATIC_API_KEY= +DISABLE_INDEXER=false +DISABLE_REALTIME_INDEXER=false +DISABLE_CATCHUP_INDEXER=false +INDEXER_DISABLE_ADDRESS_COIN_BALANCE_FETCHER=false +INDEXER_DISABLE_TOKEN_INSTANCE_REALTIME_FETCHER=false +INDEXER_DISABLE_TOKEN_INSTANCE_RETRY_FETCHER=false +INDEXER_DISABLE_TOKEN_INSTANCE_SANITIZE_FETCHER=false +INDEXER_DISABLE_TOKEN_INSTANCE_LEGACY_SANITIZE_FETCHER=false +INDEXER_DISABLE_PENDING_TRANSACTIONS_FETCHER=false +INDEXER_DISABLE_INTERNAL_TRANSACTIONS_FETCHER=false +# INDEXER_DISABLE_CATALOGED_TOKEN_UPDATER_FETCHER= +# INDEXER_DISABLE_BLOCK_REWARD_FETCHER= +# INDEXER_DISABLE_EMPTY_BLOCKS_SANITIZER= +# INDEXER_DISABLE_WITHDRAWALS_FETCHER= +# INDEXER_DISABLE_REPLACED_TRANSACTION_FETCHER= +# INDEXER_CATCHUP_BLOCKS_BATCH_SIZE= +# INDEXER_CATCHUP_BLOCKS_CONCURRENCY= +# INDEXER_CATCHUP_BLOCK_INTERVAL= +# INDEXER_EMPTY_BLOCKS_SANITIZER_INTERVAL= +# INDEXER_INTERNAL_TRANSACTIONS_BATCH_SIZE= +# INDEXER_INTERNAL_TRANSACTIONS_CONCURRENCY= +# ETHEREUM_JSONRPC_DEBUG_TRACE_TRANSACTION_TIMEOUT= +# INDEXER_BLOCK_REWARD_BATCH_SIZE= +# INDEXER_BLOCK_REWARD_CONCURRENCY= +# INDEXER_TOKEN_INSTANCE_USE_BASE_URI_RETRY= +# INDEXER_TOKEN_INSTANCE_RETRY_REFETCH_INTERVAL= +# INDEXER_TOKEN_INSTANCE_RETRY_BATCH_SIZE=10 +# INDEXER_TOKEN_INSTANCE_RETRY_CONCURRENCY= +# INDEXER_TOKEN_INSTANCE_REALTIME_BATCH_SIZE=1 +# INDEXER_TOKEN_INSTANCE_REALTIME_CONCURRENCY= +# INDEXER_TOKEN_INSTANCE_SANITIZE_BATCH_SIZE=10 +# INDEXER_TOKEN_INSTANCE_SANITIZE_CONCURRENCY= +# INDEXER_TOKEN_INSTANCE_LEGACY_SANITIZE_BATCH_SIZE=10 +# INDEXER_TOKEN_INSTANCE_LEGACY_SANITIZE_CONCURRENCY=10 +# INDEXER_DISABLE_TOKEN_INSTANCE_ERC_1155_SANITIZE_FETCHER=false +# INDEXER_DISABLE_TOKEN_INSTANCE_ERC_721_SANITIZE_FETCHER=false +# INDEXER_TOKEN_INSTANCE_ERC_1155_SANITIZE_CONCURRENCY=2 +# INDEXER_TOKEN_INSTANCE_ERC_1155_SANITIZE_BATCH_SIZE=10 +# INDEXER_TOKEN_INSTANCE_ERC_721_SANITIZE_CONCURRENCY=2 +# INDEXER_TOKEN_INSTANCE_ERC_721_SANITIZE_BATCH_SIZE=10 +# INDEXER_TOKEN_INSTANCE_ERC_721_SANITIZE_TOKENS_BATCH_SIZE=100 +# TOKEN_INSTANCE_OWNER_MIGRATION_CONCURRENCY=5 +# TOKEN_INSTANCE_OWNER_MIGRATION_BATCH_SIZE=50 +# INDEXER_COIN_BALANCES_BATCH_SIZE= +# INDEXER_COIN_BALANCES_CONCURRENCY= +# INDEXER_RECEIPTS_BATCH_SIZE= +# INDEXER_RECEIPTS_CONCURRENCY= +# INDEXER_TOKEN_CONCURRENCY= +# INDEXER_TOKEN_BALANCES_BATCH_SIZE= +# INDEXER_TOKEN_BALANCES_CONCURRENCY= +# INDEXER_TOKEN_BALANCES_MAX_REFETCH_INTERVAL= +# INDEXER_TOKEN_BALANCES_EXPONENTIAL_TIMEOUT_COEFF= +# INDEXER_TX_ACTIONS_ENABLE= +# INDEXER_TX_ACTIONS_MAX_TOKEN_CACHE_SIZE= +# INDEXER_TX_ACTIONS_REINDEX_FIRST_BLOCK= +# INDEXER_TX_ACTIONS_REINDEX_LAST_BLOCK= +# INDEXER_TX_ACTIONS_REINDEX_PROTOCOLS= +# INDEXER_TX_ACTIONS_AAVE_V3_POOL_CONTRACT= +# INDEXER_POLYGON_EDGE_L1_RPC= +# INDEXER_POLYGON_EDGE_L1_EXIT_HELPER_CONTRACT= +# INDEXER_POLYGON_EDGE_L1_WITHDRAWALS_START_BLOCK= +# INDEXER_POLYGON_EDGE_L1_STATE_SENDER_CONTRACT= +# INDEXER_POLYGON_EDGE_L1_DEPOSITS_START_BLOCK= +# INDEXER_POLYGON_EDGE_L2_STATE_SENDER_CONTRACT= +# INDEXER_POLYGON_EDGE_L2_WITHDRAWALS_START_BLOCK= +# INDEXER_POLYGON_EDGE_L2_STATE_RECEIVER_CONTRACT= +# INDEXER_POLYGON_EDGE_L2_DEPOSITS_START_BLOCK= +# INDEXER_POLYGON_EDGE_ETH_GET_LOGS_RANGE_SIZE= +# INDEXER_POLYGON_ZKEVM_BATCHES_ENABLED= +# INDEXER_POLYGON_ZKEVM_BATCHES_CHUNK_SIZE= +# INDEXER_POLYGON_ZKEVM_BATCHES_RECHECK_INTERVAL= +# INDEXER_POLYGON_ZKEVM_L1_RPC= +# INDEXER_POLYGON_ZKEVM_L1_BRIDGE_START_BLOCK= +# INDEXER_POLYGON_ZKEVM_L1_BRIDGE_CONTRACT= +# INDEXER_POLYGON_ZKEVM_L1_BRIDGE_NATIVE_SYMBOL= +# INDEXER_POLYGON_ZKEVM_L1_BRIDGE_NATIVE_DECIMALS= +# INDEXER_POLYGON_ZKEVM_L1_BRIDGE_NETWORK_ID= +# INDEXER_POLYGON_ZKEVM_L1_BRIDGE_ROLLUP_INDEX= +# INDEXER_POLYGON_ZKEVM_L2_BRIDGE_START_BLOCK= +# INDEXER_POLYGON_ZKEVM_L2_BRIDGE_CONTRACT= +# INDEXER_POLYGON_ZKEVM_L2_BRIDGE_NETWORK_ID= +# INDEXER_POLYGON_ZKEVM_L2_BRIDGE_ROLLUP_INDEX= +# INDEXER_ZKSYNC_BATCHES_ENABLED= +# INDEXER_ZKSYNC_BATCHES_CHUNK_SIZE= +# INDEXER_ZKSYNC_NEW_BATCHES_MAX_RANGE= +# INDEXER_ZKSYNC_NEW_BATCHES_RECHECK_INTERVAL= +# INDEXER_ZKSYNC_L1_RPC= +# INDEXER_ZKSYNC_BATCHES_STATUS_RECHECK_INTERVAL= +# INDEXER_ARBITRUM_ARBSYS_CONTRACT= +# INDEXER_ARBITRUM_NODE_INTERFACE_CONTRACT= +# INDEXER_ARBITRUM_L1_RPC= +# INDEXER_ARBITRUM_L1_RPC_CHUNK_SIZE= +# INDEXER_ARBITRUM_L1_RPC_HISTORICAL_BLOCKS_RANGE= +# INDEXER_ARBITRUM_L1_ROLLUP_CONTRACT= +# INDEXER_ARBITRUM_L1_ROLLUP_INIT_BLOCK= +# INDEXER_ARBITRUM_L1_COMMON_START_BLOCK= +# INDEXER_ARBITRUM_L1_FINALIZATION_THRESHOLD= +# INDEXER_ARBITRUM_ROLLUP_CHUNK_SIZE= +# INDEXER_ARBITRUM_BATCHES_TRACKING_ENABLED= +# INDEXER_ARBITRUM_BATCHES_TRACKING_RECHECK_INTERVAL= +# INDEXER_ARBITRUM_NEW_BATCHES_LIMIT= +# INDEXER_ARBITRUM_MISSING_BATCHES_RANGE= +# INDEXER_ARBITRUM_BATCHES_TRACKING_MESSAGES_TO_BLOCKS_SHIFT= +# INDEXER_ARBITRUM_CONFIRMATIONS_TRACKING_FINALIZED= +# INDEXER_ARBITRUM_BATCHES_TRACKING_L1_FINALIZATION_CHECK_ENABLED= +# INDEXER_ARBITRUM_BRIDGE_MESSAGES_TRACKING_ENABLED= +# INDEXER_ARBITRUM_TRACKING_MESSAGES_ON_L1_RECHECK_INTERVAL= +# INDEXER_ARBITRUM_MISSED_MESSAGES_RECHECK_INTERVAL= +# INDEXER_ARBITRUM_MISSED_MESSAGES_BLOCKS_DEPTH= +# CELO_CORE_CONTRACTS= +# INDEXER_CELO_VALIDATOR_GROUP_VOTES_BATCH_SIZE=200000 +# INDEXER_DISABLE_CELO_EPOCH_FETCHER=false +# INDEXER_DISABLE_CELO_VALIDATOR_GROUP_VOTES_FETCHER=false +# BERYX_API_TOKEN= +# BERYX_API_BASE_URL= +# FILECOIN_NETWORK_PREFIX=f +# FILECOIN_PENDING_ADDRESS_OPERATIONS_MIGRATION_BATCH_SIZE= +# FILECOIN_PENDING_ADDRESS_OPERATIONS_MIGRATION_CONCURRENCY= +# INDEXER_DISABLE_FILECOIN_ADDRESS_INFO_FETCHER=false +# INDEXER_FILECOIN_ADDRESS_INFO_CONCURRENCY=1 +# INDEXER_REALTIME_FETCHER_MAX_GAP= +# INDEXER_FETCHER_INIT_QUERY_LIMIT= +# INDEXER_TOKEN_BALANCES_FETCHER_INIT_QUERY_LIMIT= +# INDEXER_COIN_BALANCES_FETCHER_INIT_QUERY_LIMIT= +# INDEXER_GRACEFUL_SHUTDOWN_PERIOD= +# INDEXER_INTERNAL_TRANSACTIONS_FETCH_ORDER= +# INDEXER_SYSTEM_MEMORY_PERCENTAGE= +# WITHDRAWALS_FIRST_BLOCK= +# INDEXER_OPTIMISM_L1_RPC= +# INDEXER_OPTIMISM_L1_SYSTEM_CONFIG_CONTRACT= +# INDEXER_OPTIMISM_L1_BATCH_BLOCKS_CHUNK_SIZE= +# INDEXER_OPTIMISM_L2_BATCH_GENESIS_BLOCK_NUMBER= +# INDEXER_OPTIMISM_L1_OUTPUT_ORACLE_CONTRACT= +# INDEXER_OPTIMISM_L2_WITHDRAWALS_START_BLOCK= +# INDEXER_OPTIMISM_L2_MESSAGE_PASSER_CONTRACT= +# INDEXER_OPTIMISM_L1_DEPOSITS_BATCH_SIZE= +# INDEXER_OPTIMISM_L1_DEPOSITS_TRANSACTION_TYPE= +# INDEXER_SCROLL_L1_RPC= +# INDEXER_SCROLL_L1_MESSENGER_CONTRACT= +# INDEXER_SCROLL_L1_MESSENGER_START_BLOCK= +# INDEXER_SCROLL_L1_CHAIN_CONTRACT= +# INDEXER_SCROLL_L1_BATCH_START_BLOCK= +# INDEXER_SCROLL_L2_MESSENGER_CONTRACT= +# INDEXER_SCROLL_L2_MESSENGER_START_BLOCK= +# INDEXER_SCROLL_L2_GAS_ORACLE_CONTRACT= +# INDEXER_SCROLL_L1_ETH_GET_LOGS_RANGE_SIZE= +# INDEXER_SCROLL_L2_ETH_GET_LOGS_RANGE_SIZE= +# SCROLL_L2_CURIE_UPGRADE_BLOCK= +# SCROLL_L1_SCALAR_INIT= +# SCROLL_L1_OVERHEAD_INIT= +# SCROLL_L1_COMMIT_SCALAR_INIT= +# SCROLL_L1_BLOB_SCALAR_INIT= +# SCROLL_L1_BASE_FEE_INIT= +# SCROLL_L1_BLOB_BASE_FEE_INIT= +# ROOTSTOCK_REMASC_ADDRESS= +# ROOTSTOCK_BRIDGE_ADDRESS= +# ROOTSTOCK_LOCKED_BTC_CACHE_PERIOD= +# ROOTSTOCK_LOCKING_CAP= +# INDEXER_DISABLE_ROOTSTOCK_DATA_FETCHER= +# INDEXER_ROOTSTOCK_DATA_FETCHER_INTERVAL= +# INDEXER_ROOTSTOCK_DATA_FETCHER_BATCH_SIZE= +# INDEXER_ROOTSTOCK_DATA_FETCHER_CONCURRENCY= +# INDEXER_ROOTSTOCK_DATA_FETCHER_DB_BATCH_SIZE= +# INDEXER_BEACON_RPC_URL=http://localhost:5052 +# INDEXER_DISABLE_BEACON_BLOB_FETCHER= +# INDEXER_BEACON_BLOB_FETCHER_SLOT_DURATION=12 +# INDEXER_BEACON_BLOB_FETCHER_REFERENCE_SLOT=8000000 +# INDEXER_BEACON_BLOB_FETCHER_REFERENCE_TIMESTAMP=1702824023 +# INDEXER_BEACON_BLOB_FETCHER_START_BLOCK=19200000 +# INDEXER_BEACON_BLOB_FETCHER_END_BLOCK=0 +# TOKEN_ID_MIGRATION_FIRST_BLOCK= +# TOKEN_ID_MIGRATION_CONCURRENCY= +# TOKEN_ID_MIGRATION_BATCH_SIZE= +# MISSING_BALANCE_OF_TOKENS_WINDOW_SIZE= +# INDEXER_INTERNAL_TRANSACTIONS_TRACER_TYPE= +# WEBAPP_URL= +# API_URL= +SHOW_ADDRESS_MARKETCAP_PERCENTAGE=true +CHECKSUM_ADDRESS_HASHES=true +CHECKSUM_FUNCTION=eth +DISABLE_EXCHANGE_RATES=true +TXS_STATS_ENABLED=true +SHOW_PRICE_CHART=false +SHOW_PRICE_CHART_LEGEND=false +SHOW_TXS_CHART=true +TXS_HISTORIAN_INIT_LAG=0 +TXS_STATS_DAYS_TO_COMPILE_AT_INIT=10 +COIN_BALANCE_HISTORY_DAYS=90 +APPS_MENU=true +EXTERNAL_APPS=[] +# GAS_PRICE= +# GAS_PRICE_ORACLE_CACHE_PERIOD= +# GAS_PRICE_ORACLE_SIMPLE_TRANSACTION_GAS= +# GAS_PRICE_ORACLE_NUM_OF_BLOCKS= +# GAS_PRICE_ORACLE_SAFELOW_PERCENTILE= +# GAS_PRICE_ORACLE_AVERAGE_PERCENTILE= +# GAS_PRICE_ORACLE_FAST_PERCENTILE= +# GAS_PRICE_ORACLE_SAFELOW_TIME_COEFFICIENT= +# GAS_PRICE_ORACLE_AVERAGE_TIME_COEFFICIENT= +# GAS_PRICE_ORACLE_FAST_TIME_COEFFICIENT= +# RESTRICTED_LIST= +# RESTRICTED_LIST_KEY= +SHOW_MAINTENANCE_ALERT=false +MAINTENANCE_ALERT_MESSAGE= +CHAIN_ID= +MAX_SIZE_UNLESS_HIDE_ARRAY=50 +HIDE_BLOCK_MINER=false +# HIDE_SCAM_ADDRESSES= +DISPLAY_TOKEN_ICONS=false +RE_CAPTCHA_SECRET_KEY= +RE_CAPTCHA_CLIENT_KEY= +RE_CAPTCHA_V3_SECRET_KEY= +RE_CAPTCHA_V3_CLIENT_KEY= +RE_CAPTCHA_DISABLED=false +# RE_CAPTCHA_CHECK_HOSTNAME +JSON_RPC= +# API_RATE_LIMIT_HAMMER_REDIS_URL=redis://redis-db:6379/1 +# API_RATE_LIMIT_IS_BLOCKSCOUT_BEHIND_PROXY=false +API_RATE_LIMIT_UI_V2_TOKEN_TTL_IN_SECONDS=18000 +FETCH_REWARDS_WAY=trace_block +MICROSERVICE_SC_VERIFIER_ENABLED=true +# MICROSERVICE_SC_VERIFIER_URL=http://smart-contract-verifier:8050/ +# MICROSERVICE_SC_VERIFIER_TYPE=sc_verifier +MICROSERVICE_SC_VERIFIER_URL=https://eth-bytecode-db.services.blockscout.com/ +MICROSERVICE_SC_VERIFIER_TYPE=eth_bytecode_db +MICROSERVICE_ETH_BYTECODE_DB_INTERVAL_BETWEEN_LOOKUPS=10m +MICROSERVICE_ETH_BYTECODE_DB_MAX_LOOKUPS_CONCURRENCY=10 +MICROSERVICE_VISUALIZE_SOL2UML_ENABLED=true +MICROSERVICE_VISUALIZE_SOL2UML_URL=http://visualizer:8050/ +MICROSERVICE_SIG_PROVIDER_ENABLED=true +MICROSERVICE_SIG_PROVIDER_URL=http://sig-provider:8050/ +# MICROSERVICE_BENS_URL= +# MICROSERVICE_BENS_ENABLED= +MICROSERVICE_ACCOUNT_ABSTRACTION_ENABLED=false +MICROSERVICE_ACCOUNT_ABSTRACTION_URL=http://user-ops-indexer:8050/ +# MICROSERVICE_METADATA_URL= +# MICROSERVICE_METADATA_ENABLED= +DECODE_NOT_A_CONTRACT_CALLS=true +# DATABASE_READ_ONLY_API_URL= +# ACCOUNT_DATABASE_URL= +# ACCOUNT_POOL_SIZE= +# ACCOUNT_AUTH0_DOMAIN= +# ACCOUNT_AUTH0_CLIENT_ID= +# ACCOUNT_AUTH0_CLIENT_SECRET= +# ACCOUNT_PUBLIC_TAGS_AIRTABLE_URL= +# ACCOUNT_PUBLIC_TAGS_AIRTABLE_API_KEY= +# ACCOUNT_SENDGRID_API_KEY= +# ACCOUNT_SENDGRID_SENDER= +# ACCOUNT_SENDGRID_TEMPLATE= +# ACCOUNT_VERIFICATION_EMAIL_RESEND_INTERVAL= +# ACCOUNT_OTP_RESEND_INTERVAL= +# ACCOUNT_PRIVATE_TAGS_LIMIT=2000 +# ACCOUNT_WATCHLIST_ADDRESSES_LIMIT=15 +ACCOUNT_CLOAK_KEY= +ACCOUNT_ENABLED=false +ACCOUNT_REDIS_URL=redis://redis-db:6379 +EIP_1559_ELASTICITY_MULTIPLIER=2 +# MIXPANEL_TOKEN= +# MIXPANEL_URL= +# AMPLITUDE_API_KEY= +# AMPLITUDE_URL= +# IPFS_GATEWAY_URL= +# IPFS_GATEWAY_URL_PARAM_KEY= +# IPFS_GATEWAY_URL_PARAM_VALUE= +# IPFS_GATEWAY_URL_PARAM_LOCATION= +# IPFS_PUBLIC_GATEWAY_URL= +# ADDRESSES_TABS_COUNTERS_TTL=10m +# DENORMALIZATION_MIGRATION_BATCH_SIZE= +# DENORMALIZATION_MIGRATION_CONCURRENCY= +# TOKEN_TRANSFER_TOKEN_TYPE_MIGRATION_BATCH_SIZE= +# TOKEN_TRANSFER_TOKEN_TYPE_MIGRATION_CONCURRENCY= +# SANITIZE_INCORRECT_NFT_BATCH_SIZE= +# SANITIZE_INCORRECT_NFT_CONCURRENCY= +# MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_CONCURRENCY= +# MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_BATCH_SIZE= +# MIGRATION_RESTORE_OMITTED_WETH_TOKEN_TRANSFERS_TIMEOUT= +# MIGRATION_SANITIZE_DUPLICATED_LOG_INDEX_LOGS_CONCURRENCY= +# MIGRATION_SANITIZE_DUPLICATED_LOG_INDEX_LOGS_BATCH_SIZE= +# MIGRATION_REFETCH_CONTRACT_CODES_BATCH_SIZE= +# MIGRATION_REFETCH_CONTRACT_CODES_CONCURRENCY= +SOURCIFY_INTEGRATION_ENABLED=false +SOURCIFY_SERVER_URL= +SOURCIFY_REPO_URL= +SHOW_TENDERLY_LINK=false +TENDERLY_CHAIN_PATH= +# SOLIDITYSCAN_PLATFORM_ID= +# SOLIDITYSCAN_CHAIN_ID= +# SOLIDITYSCAN_API_TOKEN= +# NOVES_FI_BASE_API_URL= +# NOVES_FI_CHAIN_NAME= +# NOVES_FI_API_TOKEN= +# ZERION_BASE_API_URL= +# ZERION_API_TOKEN= +# BRIDGED_TOKENS_ENABLED= +# BRIDGED_TOKENS_ETH_OMNI_BRIDGE_MEDIATOR= +# BRIDGED_TOKENS_BSC_OMNI_BRIDGE_MEDIATOR= +# BRIDGED_TOKENS_POA_OMNI_BRIDGE_MEDIATOR= +# BRIDGED_TOKENS_AMB_BRIDGE_MEDIATORS +# BRIDGED_TOKENS_FOREIGN_JSON_RPC +# MUD_INDEXER_ENABLED= +# MUD_DATABASE_URL= +# MUD_POOL_SIZE=50 +# WETH_TOKEN_TRANSFERS_FILTERING_ENABLED=false +# WHITELISTED_WETH_CONTRACTS= +# SANITIZE_INCORRECT_WETH_BATCH_SIZE=100 +# SANITIZE_INCORRECT_WETH_CONCURRENCY=1 +# PUBLIC_METRICS_ENABLED= +# PUBLIC_METRICS_UPDATE_PERIOD_HOURS= +# CSV_EXPORT_LIMIT= +# SHRINK_INTERNAL_TRANSACTIONS_ENABLED= +# SHRINK_INTERNAL_TRANSACTIONS_BATCH_SIZE= +# SHRINK_INTERNAL_TRANSACTIONS_CONCURRENCY= diff --git a/framework/cmd/blockscout/envs/common-frontend.env b/framework/cmd/blockscout/envs/common-frontend.env new file mode 100755 index 000000000..a3dcded76 --- /dev/null +++ b/framework/cmd/blockscout/envs/common-frontend.env @@ -0,0 +1,17 @@ +NEXT_PUBLIC_API_HOST=localhost +NEXT_PUBLIC_API_PROTOCOL=http +NEXT_PUBLIC_STATS_API_HOST=http://localhost:8080 +NEXT_PUBLIC_NETWORK_NAME=Awesome chain +NEXT_PUBLIC_NETWORK_SHORT_NAME=Awesome chain +NEXT_PUBLIC_NETWORK_ID=5 +NEXT_PUBLIC_NETWORK_CURRENCY_NAME=Ether +NEXT_PUBLIC_NETWORK_CURRENCY_SYMBOL=ETH +NEXT_PUBLIC_NETWORK_CURRENCY_DECIMALS=18 +NEXT_PUBLIC_API_BASE_PATH=/ +NEXT_PUBLIC_APP_HOST=localhost +NEXT_PUBLIC_APP_PROTOCOL=http +NEXT_PUBLIC_HOMEPAGE_CHARTS=['daily_txs'] +NEXT_PUBLIC_VISUALIZE_API_HOST=http://localhost:8081 +NEXT_PUBLIC_IS_TESTNET=true +NEXT_PUBLIC_API_WEBSOCKET_PROTOCOL=ws +NEXT_PUBLIC_API_SPEC_URL=https://raw.githubusercontent.com/blockscout/blockscout-api-v2-swagger/main/swagger.yaml \ No newline at end of file diff --git a/framework/cmd/blockscout/envs/common-smart-contract-verifier.env b/framework/cmd/blockscout/envs/common-smart-contract-verifier.env new file mode 100755 index 000000000..5fdf805ec --- /dev/null +++ b/framework/cmd/blockscout/envs/common-smart-contract-verifier.env @@ -0,0 +1,39 @@ +# Those are examples of existing configuration variables and their default values. +# When uncommented, they would overwrite corresponding values from `base.toml` +# configuration file. + +SMART_CONTRACT_VERIFIER__SERVER__HTTP__ENABLED=true +SMART_CONTRACT_VERIFIER__SERVER__HTTP__ADDR=0.0.0.0:8050 +SMART_CONTRACT_VERIFIER__SERVER__HTTP__MAX_BODY_SIZE=2097152 + +SMART_CONTRACT_VERIFIER__SERVER__GRPC__ENABLED=false +SMART_CONTRACT_VERIFIER__SERVER__GRPC__ADDR=0.0.0.0:8051 + +SMART_CONTRACT_VERIFIER__SOLIDITY__ENABLED=true +SMART_CONTRACT_VERIFIER__SOLIDITY__COMPILERS_DIR=/tmp/solidity-compilers +SMART_CONTRACT_VERIFIER__SOLIDITY__REFRESH_VERSIONS_SCHEDULE=0 0 * * * * * + +# It depends on the OS you are running the service on +SMART_CONTRACT_VERIFIER__SOLIDITY__FETCHER__LIST__LIST_URL=https://solc-bin.ethereum.org/linux-amd64/list.json +#SMART_CONTRACT_VERIFIER__SOLIDITY__FETCHER__LIST__LIST_URL=https://solc-bin.ethereum.org/macosx-amd64/list.json +#SMART_CONTRACT_VERIFIER__SOLIDITY__FETCHER__LIST__LIST_URL=https://solc-bin.ethereum.org/windows-amd64/list.json + +SMART_CONTRACT_VERIFIER__VYPER__ENABLED=true +SMART_CONTRACT_VERIFIER__VYPER__COMPILERS_DIR=/tmp/vyper-compilers +SMART_CONTRACT_VERIFIER__VYPER__REFRESH_VERSIONS_SCHEDULE=0 0 * * * * * + +# It depends on the OS you are running the service on +SMART_CONTRACT_VERIFIER__VYPER__FETCHER__LIST__LIST_URL=https://raw.githubusercontent.com/blockscout/solc-bin/main/vyper.list.json +#SMART_CONTRACT_VERIFIER__VYPER__FETCHER__LIST__LIST_URL=https://raw.githubusercontent.com/blockscout/solc-bin/main/vyper.macos.list.json + +SMART_CONTRACT_VERIFIER__SOURCIFY__ENABLED=true +SMART_CONTRACT_VERIFIER__SOURCIFY__API_URL=https://sourcify.dev/server/ +SMART_CONTRACT_VERIFIER__SOURCIFY__VERIFICATION_ATTEMPTS=3 +SMART_CONTRACT_VERIFIER__SOURCIFY__REQUEST_TIMEOUT=10 + +SMART_CONTRACT_VERIFIER__METRICS__ENABLED=false +SMART_CONTRACT_VERIFIER__METRICS__ADDR=0.0.0.0:6060 +SMART_CONTRACT_VERIFIER__METRICS__ROUTE=/metrics + +SMART_CONTRACT_VERIFIER__JAEGER__ENABLED=false +SMART_CONTRACT_VERIFIER__JAEGER__AGENT_ENDPOINT=localhost:6831 diff --git a/framework/cmd/blockscout/envs/common-stats.env b/framework/cmd/blockscout/envs/common-stats.env new file mode 100755 index 000000000..f5eed636b --- /dev/null +++ b/framework/cmd/blockscout/envs/common-stats.env @@ -0,0 +1,27 @@ +# Those are examples of existing configuration variables and their default values. +# When uncommented, they would overwrite corresponding values from `base.toml` +# configuration file. + +STATS__SERVER__HTTP__ENABLED=true +STATS__SERVER__HTTP__ADDR=0.0.0.0:8050 +STATS__SERVER__HTTP__MAX_BODY_SIZE=2097152 + +STATS__SERVER__GRPC__ENABLED=false +STATS__SERVER__GRPC__ADDR=0.0.0.0:8051 + +STATS__DB_URL= +STATS__BLOCKSCOUT_DB_URL= +STATS__CREATE_DATABASE=false +STATS__RUN_MIGRATIONS=false +STATS__DEFAULT_SCHEDULE=0 0 1 * * * * +STATS__FORCE_UPDATE_ON_START=false + +STATS__METRICS__ENABLED=false +STATS__METRICS__ADDR=0.0.0.0:6060 +STATS__METRICS__ROUTE=/metrics + +STATS__JAEGER__ENABLED=false +STATS__JAEGER__AGENT_ENDPOINT=localhost:6831 + +STATS__TRACING__ENABLED=true +STATS__TRACING__FORMAT=default diff --git a/framework/cmd/blockscout/envs/common-user-ops-indexer.env b/framework/cmd/blockscout/envs/common-user-ops-indexer.env new file mode 100755 index 000000000..3345cba48 --- /dev/null +++ b/framework/cmd/blockscout/envs/common-user-ops-indexer.env @@ -0,0 +1,48 @@ +## Those are examples of existing configuration variables and their default values. +## When uncommented, they would overwrite corresponding values from `base.toml` +## configuration file. + +USER_OPS_INDEXER__SERVER__HTTP__ENABLED=true +USER_OPS_INDEXER__SERVER__HTTP__ADDR=0.0.0.0:8050 +USER_OPS_INDEXER__SERVER__HTTP__MAX_BODY_SIZE=2097152 +USER_OPS_INDEXER__SERVER__GRPC__ENABLED=false +USER_OPS_INDEXER__SERVER__GRPC__ADDR=0.0.0.0:8051 + +USER_OPS_INDEXER__API__MAX_PAGE_SIZE=100 + +## (required) no default value available +USER_OPS_INDEXER__INDEXER__RPC_URL="" +USER_OPS_INDEXER__INDEXER__CONCURRENCY=20 +USER_OPS_INDEXER__INDEXER__ENTRYPOINTS__V06=true +USER_OPS_INDEXER__INDEXER__ENTRYPOINTS__V07=true + +USER_OPS_INDEXER__INDEXER__REALTIME__ENABLED=true + +USER_OPS_INDEXER__INDEXER__PAST_RPC_LOGS_INDEXER__ENABLED=true +USER_OPS_INDEXER__INDEXER__PAST_RPC_LOGS_INDEXER__BLOCK_RANGE=1000 + +USER_OPS_INDEXER__INDEXER__PAST_DB_LOGS_INDEXER__ENABLED=true +USER_OPS_INDEXER__INDEXER__PAST_DB_LOGS_INDEXER__START_BLOCK=-100000 +USER_OPS_INDEXER__INDEXER__PAST_DB_LOGS_INDEXER__END_BLOCK=0 + +## (required) no default value available +USER_OPS_INDEXER__DATABASE__CONNECT__URL="" +# OR +#USER_OPS_INDEXER__DATABASE__CONNECT__KV__HOST= +#USER_OPS_INDEXER__DATABASE__CONNECT__KV__PORT= +#USER_OPS_INDEXER__DATABASE__CONNECT__KV__USER= +#USER_OPS_INDEXER__DATABASE__CONNECT__KV__PASSWORD= +#USER_OPS_INDEXER__DATABASE__CONNECT__KV__DBNAME= + +USER_OPS_INDEXER__DATABASE__CREATE_DATABASE=false +USER_OPS_INDEXER__DATABASE__RUN_MIGRATIONS=false + +USER_OPS_INDEXER__METRICS__ENABLED=true +USER_OPS_INDEXER__METRICS__ADDR=0.0.0.0:6060 +USER_OPS_INDEXER__METRICS__ROUTE=/metrics + +USER_OPS_INDEXER__JAEGER__ENABLED=false +USER_OPS_INDEXER__JAEGER__AGENT_ENDPOINT=localhost:6831 + +USER_OPS_INDEXER__TRACING__ENABLED=true +USER_OPS_INDEXER__TRACING__FORMAT=default diff --git a/framework/cmd/blockscout/envs/common-visualizer.env b/framework/cmd/blockscout/envs/common-visualizer.env new file mode 100755 index 000000000..b4fd47084 --- /dev/null +++ b/framework/cmd/blockscout/envs/common-visualizer.env @@ -0,0 +1 @@ +VISUALIZER__SERVER__GRPC__ENABLED=false diff --git a/framework/cmd/blockscout/proxy/default.conf.template b/framework/cmd/blockscout/proxy/default.conf.template new file mode 100755 index 000000000..dbd5180d1 --- /dev/null +++ b/framework/cmd/blockscout/proxy/default.conf.template @@ -0,0 +1,93 @@ +map $http_upgrade $connection_upgrade { + + default upgrade; + '' close; +} + +server { + listen 80; + server_name localhost; + proxy_http_version 1.1; + + location ~ ^/(api|socket|sitemap.xml|auth/auth0|auth/auth0/callback|auth/logout) { + proxy_pass ${BACK_PROXY_PASS}; + proxy_http_version 1.1; + proxy_set_header Host "$host"; + proxy_set_header X-Real-IP "$remote_addr"; + proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for"; + proxy_set_header X-Forwarded-Proto "$scheme"; + proxy_set_header Upgrade "$http_upgrade"; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + } + location / { + proxy_pass ${FRONT_PROXY_PASS}; + proxy_http_version 1.1; + proxy_set_header Host "$host"; + proxy_set_header X-Real-IP "$remote_addr"; + proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for"; + proxy_set_header X-Forwarded-Proto "$scheme"; + proxy_set_header Upgrade "$http_upgrade"; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + } +} +server { + listen 8080; + server_name localhost; + proxy_http_version 1.1; + proxy_hide_header Access-Control-Allow-Origin; + proxy_hide_header Access-Control-Allow-Methods; + add_header 'Access-Control-Allow-Origin' 'http://localhost' always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + add_header 'Access-Control-Allow-Methods' 'PUT, GET, POST, OPTIONS, DELETE, PATCH' always; + + location / { + proxy_pass http://stats:8050/; + proxy_http_version 1.1; + proxy_set_header Host "$host"; + proxy_set_header X-Real-IP "$remote_addr"; + proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for"; + proxy_set_header X-Forwarded-Proto "$scheme"; + proxy_set_header Upgrade "$http_upgrade"; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + } +} +server { + listen 8081; + server_name localhost; + proxy_http_version 1.1; + proxy_hide_header Access-Control-Allow-Origin; + proxy_hide_header Access-Control-Allow-Methods; + add_header 'Access-Control-Allow-Origin' 'http://localhost' always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + add_header 'Access-Control-Allow-Methods' 'PUT, GET, POST, OPTIONS, DELETE, PATCH' always; + add_header 'Access-Control-Allow-Headers' 'DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,x-csrf-token' always; + + location / { + proxy_pass http://visualizer:8050/; + proxy_http_version 1.1; + proxy_buffering off; + proxy_set_header Host "$host"; + proxy_set_header X-Real-IP "$remote_addr"; + proxy_connect_timeout 30m; + proxy_read_timeout 30m; + proxy_send_timeout 30m; + proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for"; + proxy_set_header X-Forwarded-Proto "$scheme"; + proxy_set_header Upgrade "$http_upgrade"; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' 'http://localhost' always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + add_header 'Access-Control-Allow-Methods' 'PUT, GET, POST, OPTIONS, DELETE, PATCH' always; + add_header 'Access-Control-Allow-Headers' 'DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,x-csrf-token' always; + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain charset=UTF-8'; + add_header 'Content-Length' 0; + return 204; + } + } +} \ No newline at end of file diff --git a/framework/cmd/blockscout/proxy/microservices.conf.template b/framework/cmd/blockscout/proxy/microservices.conf.template new file mode 100755 index 000000000..708812f57 --- /dev/null +++ b/framework/cmd/blockscout/proxy/microservices.conf.template @@ -0,0 +1,65 @@ +map $http_upgrade $connection_upgrade { + + default upgrade; + '' close; +} + +server { + listen 8080; + server_name localhost; + proxy_http_version 1.1; + proxy_hide_header Access-Control-Allow-Origin; + proxy_hide_header Access-Control-Allow-Methods; + add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + add_header 'Access-Control-Allow-Methods' 'PUT, GET, POST, OPTIONS, DELETE, PATCH' always; + + location / { + proxy_pass http://stats:8050/; + proxy_http_version 1.1; + proxy_set_header Host "$host"; + proxy_set_header X-Real-IP "$remote_addr"; + proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for"; + proxy_set_header X-Forwarded-Proto "$scheme"; + proxy_set_header Upgrade "$http_upgrade"; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + } +} +server { + listen 8081; + server_name localhost; + proxy_http_version 1.1; + proxy_hide_header Access-Control-Allow-Origin; + proxy_hide_header Access-Control-Allow-Methods; + add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + add_header 'Access-Control-Allow-Methods' 'PUT, GET, POST, OPTIONS, DELETE, PATCH' always; + add_header 'Access-Control-Allow-Headers' 'DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,x-csrf-token' always; + + location / { + proxy_pass http://visualizer:8050/; + proxy_http_version 1.1; + proxy_buffering off; + proxy_set_header Host "$host"; + proxy_set_header X-Real-IP "$remote_addr"; + proxy_connect_timeout 30m; + proxy_read_timeout 30m; + proxy_send_timeout 30m; + proxy_set_header X-Forwarded-For "$proxy_add_x_forwarded_for"; + proxy_set_header X-Forwarded-Proto "$scheme"; + proxy_set_header Upgrade "$http_upgrade"; + proxy_set_header Connection $connection_upgrade; + proxy_cache_bypass $http_upgrade; + if ($request_method = 'OPTIONS') { + add_header 'Access-Control-Allow-Origin' 'http://localhost:3000' always; + add_header 'Access-Control-Allow-Credentials' 'true' always; + add_header 'Access-Control-Allow-Methods' 'PUT, GET, POST, OPTIONS, DELETE, PATCH' always; + add_header 'Access-Control-Allow-Headers' 'DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization,x-csrf-token' always; + add_header 'Access-Control-Max-Age' 1728000; + add_header 'Content-Type' 'text/plain charset=UTF-8'; + add_header 'Content-Length' 0; + return 204; + } + } +} \ No newline at end of file diff --git a/framework/cmd/blockscout/services/backend.yml b/framework/cmd/blockscout/services/backend.yml new file mode 100755 index 000000000..0231c3fc0 --- /dev/null +++ b/framework/cmd/blockscout/services/backend.yml @@ -0,0 +1,16 @@ +version: '3.9' + +services: + backend: + image: blockscout/blockscout:6.9.0.commit.4100e959 + pull_policy: always + restart: always + stop_grace_period: 5m + container_name: 'backend' + command: sh -c "bin/blockscout eval \"Elixir.Explorer.ReleaseTasks.create_and_migrate()\" && bin/blockscout start" + extra_hosts: + - 'host.docker.internal:host-gateway' + env_file: + - ../envs/common-blockscout.env + volumes: + - ./logs/:/app/logs/ \ No newline at end of file diff --git a/framework/cmd/blockscout/services/db.yml b/framework/cmd/blockscout/services/db.yml new file mode 100755 index 000000000..430409bbe --- /dev/null +++ b/framework/cmd/blockscout/services/db.yml @@ -0,0 +1,35 @@ +version: '3.9' + +services: + db-init: + image: postgres:15 + volumes: + - ./blockscout-db-data:/var/lib/postgresql/data + entrypoint: + - sh + - -c + - | + chown -R 2000:2000 /var/lib/postgresql/data + + db: + image: postgres:15 + user: 2000:2000 + shm_size: 256m + restart: always + container_name: 'db' + command: postgres -c 'max_connections=200' -c 'client_connection_check_interval=60000' + environment: + POSTGRES_DB: 'blockscout' + POSTGRES_USER: 'blockscout' + POSTGRES_PASSWORD: 'ceWb1MeLBEeOIfk65gU8EjF8' + ports: + - target: 5432 + published: 7432 + volumes: + - ./blockscout-db-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U blockscout -d blockscout"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s diff --git a/framework/cmd/blockscout/services/frontend.yml b/framework/cmd/blockscout/services/frontend.yml new file mode 100755 index 000000000..1d0a23776 --- /dev/null +++ b/framework/cmd/blockscout/services/frontend.yml @@ -0,0 +1,11 @@ +version: '3.9' + +services: + frontend: + image: ghcr.io/blockscout/frontend:v1.36.2 + pull_policy: always + platform: linux/amd64 + restart: always + container_name: 'frontend' + env_file: + - ../envs/common-frontend.env diff --git a/framework/cmd/blockscout/services/nginx.yml b/framework/cmd/blockscout/services/nginx.yml new file mode 100755 index 000000000..bc225c908 --- /dev/null +++ b/framework/cmd/blockscout/services/nginx.yml @@ -0,0 +1,20 @@ +version: '3.9' + +services: + proxy: + image: nginx + container_name: proxy + extra_hosts: + - 'host.docker.internal:host-gateway' + volumes: + - "../proxy:/etc/nginx/templates" + environment: + BACK_PROXY_PASS: ${BACK_PROXY_PASS:-http://backend:4000} + FRONT_PROXY_PASS: ${FRONT_PROXY_PASS:-http://frontend:3000} + ports: + - target: 80 + published: 80 + - target: 8080 + published: 8080 + - target: 8081 + published: 8081 diff --git a/framework/cmd/blockscout/services/redis.yml b/framework/cmd/blockscout/services/redis.yml new file mode 100755 index 000000000..93f616686 --- /dev/null +++ b/framework/cmd/blockscout/services/redis.yml @@ -0,0 +1,9 @@ +version: '3.9' + +services: + redis-db: + image: 'redis:alpine' + container_name: redis-db + command: redis-server + volumes: + - ./redis-data:/data diff --git a/framework/cmd/blockscout/services/sig-provider.yml b/framework/cmd/blockscout/services/sig-provider.yml new file mode 100755 index 000000000..74271747e --- /dev/null +++ b/framework/cmd/blockscout/services/sig-provider.yml @@ -0,0 +1,9 @@ +version: '3.9' + +services: + sig-provider: + image: ghcr.io/blockscout/sig-provider:v1.1.1 + pull_policy: always + platform: linux/amd64 + restart: always + container_name: 'sig-provider' diff --git a/framework/cmd/blockscout/services/smart-contract-verifier.yml b/framework/cmd/blockscout/services/smart-contract-verifier.yml new file mode 100755 index 000000000..1efa66013 --- /dev/null +++ b/framework/cmd/blockscout/services/smart-contract-verifier.yml @@ -0,0 +1,11 @@ +version: '3.9' + +services: + smart-contract-verifier: + image: ghcr.io/blockscout/smart-contract-verifier:v1.9.2 + pull_policy: always + platform: linux/amd64 + restart: always + container_name: 'smart-contract-verifier' + env_file: + - ../envs/common-smart-contract-verifier.env diff --git a/framework/cmd/blockscout/services/stats.yml b/framework/cmd/blockscout/services/stats.yml new file mode 100755 index 000000000..286813906 --- /dev/null +++ b/framework/cmd/blockscout/services/stats.yml @@ -0,0 +1,51 @@ +version: '3.9' + +services: + stats-db-init: + image: postgres:15 + volumes: + - ./stats-db-data:/var/lib/postgresql/data + entrypoint: + - sh + - -c + - | + chown -R 2000:2000 /var/lib/postgresql/data + + stats-db: + image: postgres:15 + user: 2000:2000 + shm_size: 256m + restart: always + container_name: 'stats-db' + command: postgres -c 'max_connections=200' + environment: + POSTGRES_DB: 'stats' + POSTGRES_USER: 'stats' + POSTGRES_PASSWORD: 'n0uejXPl61ci6ldCuE2gQU5Y' + ports: + - target: 5432 + published: 7433 + volumes: + - ./stats-db-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U stats -d stats"] + interval: 10s + timeout: 5s + retries: 5 + start_period: 10s + + stats: + image: ghcr.io/blockscout/stats:v2.2.3 + pull_policy: always + platform: linux/amd64 + restart: always + container_name: 'stats' + extra_hosts: + - 'host.docker.internal:host-gateway' + env_file: + - ../envs/common-stats.env + environment: + - STATS__DB_URL=${STATS__DB_URL:-postgres://stats:n0uejXPl61ci6ldCuE2gQU5Y@stats-db:5432/stats} + - STATS__BLOCKSCOUT_DB_URL=${STATS__BLOCKSCOUT_DB_URL:-postgresql://blockscout:ceWb1MeLBEeOIfk65gU8EjF8@db:5432/blockscout} + - STATS__CREATE_DATABASE=${STATS__CREATE_DATABASE:-true} + - STATS__RUN_MIGRATIONS=${STATS__RUN_MIGRATIONS:-true} diff --git a/framework/cmd/blockscout/services/user-ops-indexer.yml b/framework/cmd/blockscout/services/user-ops-indexer.yml new file mode 100755 index 000000000..7918aa7ef --- /dev/null +++ b/framework/cmd/blockscout/services/user-ops-indexer.yml @@ -0,0 +1,17 @@ +version: '3.9' + +services: + user-ops-indexer: + image: ghcr.io/blockscout/user-ops-indexer:v1.3.0 + pull_policy: always + platform: linux/amd64 + restart: always + container_name: 'user-ops-indexer' + extra_hosts: + - 'host.docker.internal:host-gateway' + env_file: + - ../envs/common-user-ops-indexer.env + environment: + - USER_OPS_INDEXER__INDEXER__RPC_URL=${USER_OPS_INDEXER__INDEXER__RPC_URL:-ws://host.docker.internal:8545/} + - USER_OPS_INDEXER__DATABASE__CONNECT__URL=${USER_OPS_INDEXER__DATABASE__CONNECT__URL:-postgresql://blockscout:ceWb1MeLBEeOIfk65gU8EjF8@db:5432/blockscout} + - USER_OPS_INDEXER__DATABASE__RUN_MIGRATIONS=true diff --git a/framework/cmd/blockscout/services/visualizer.yml b/framework/cmd/blockscout/services/visualizer.yml new file mode 100755 index 000000000..d58c1b7f7 --- /dev/null +++ b/framework/cmd/blockscout/services/visualizer.yml @@ -0,0 +1,11 @@ +version: '3.9' + +services: + visualizer: + image: ghcr.io/blockscout/visualizer:v0.2.1 + pull_policy: always + platform: linux/amd64 + restart: always + container_name: 'visualizer' + env_file: + - ../envs/common-visualizer.env diff --git a/framework/cmd/compose/conf/defaults.ini b/framework/cmd/compose/conf/defaults.ini new file mode 100755 index 000000000..b2034acc9 --- /dev/null +++ b/framework/cmd/compose/conf/defaults.ini @@ -0,0 +1,1238 @@ +##################### Grafana Configuration Defaults ##################### +# +# Do not modify this file in grafana installs +# + +# possible values : production, development +app_mode = production + +# instance name, defaults to HOSTNAME environment variable value or hostname if HOSTNAME var is empty +instance_name = ${HOSTNAME} + +# force migration will run migrations that might cause dataloss +force_migration = false + +#################################### Paths ############################### +[paths] +# Path to where grafana can store temp files, sessions, and the sqlite3 db (if that is used) +data = data + +# Temporary files in `data` directory older than given duration will be removed +temp_data_lifetime = 24h + +# Directory where grafana can store logs +logs = data/log + +# Directory where grafana will automatically scan and look for plugins +plugins = data/plugins + +# folder that contains provisioning config files that grafana will apply on startup and while running. +provisioning = conf/provisioning + +#################################### Server ############################## +[server] +# Protocol (http, https, h2, socket) +protocol = http + +# The ip address to bind to, empty will bind to all interfaces +http_addr = + +# The http port to use +http_port = 3000 + +# The public facing domain name used to access grafana from a browser +domain = localhost + +# Redirect to correct domain if host header does not match domain +# Prevents DNS rebinding attacks +enforce_domain = false + +# The full public facing url +root_url = %(protocol)s://%(domain)s:%(http_port)s/ + +# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons. +serve_from_sub_path = false + +# Log web requests +router_logging = false + +# the path relative working path +static_root_path = public + +# enable gzip +enable_gzip = false + +# https certs & key file +cert_file = +cert_key = + +# Unix socket path +socket = /tmp/grafana.sock + +# CDN Url +cdn_url = + +# Sets the maximum time in minutes before timing out read of an incoming request and closing idle connections. +# `0` means there is no timeout for reading the request. +read_timeout = 0 + +#################################### Database ############################ +[database] +# You can configure the database connection by specifying type, host, name, user and password +# as separate properties or as on string using the url property. + +# Either "mysql", "postgres" or "sqlite3", it's your choice +type = sqlite3 +host = 127.0.0.1:3306 +name = grafana +user = root +# If the password contains # or ; you have to wrap it with triple quotes. Ex """#password;""" +password = +# Use either URL or the previous fields to configure the database +# Example: mysql://user:secret@host:port/database +url = + +# Max idle conn setting default is 2 +max_idle_conn = 2 + +# Max conn setting default is 0 (mean not set) +max_open_conn = + +# Connection Max Lifetime default is 14400 (means 14400 seconds or 4 hours) +conn_max_lifetime = 14400 + +# Set to true to log the sql calls and execution times. +log_queries = + +# For "postgres", use either "disable", "require" or "verify-full" +# For "mysql", use either "true", "false", or "skip-verify". +ssl_mode = disable + +# Database drivers may support different transaction isolation levels. +# Currently, only "mysql" driver supports isolation levels. +# If the value is empty - driver's default isolation level is applied. +# For "mysql" use "READ-UNCOMMITTED", "READ-COMMITTED", "REPEATABLE-READ" or "SERIALIZABLE". +isolation_level = + +ca_cert_path = +client_key_path = +client_cert_path = +server_cert_name = + +# For "sqlite3" only, path relative to data_path setting +path = grafana.db + +# For "sqlite3" only. cache mode setting used for connecting to the database +cache_mode = private + +# For "mysql" only if lockingMigration feature toggle is set. How many seconds to wait before failing to lock the database for the migrations, default is 0. +locking_attempt_timeout_sec = 0 + +#################################### Cache server ############################# +[remote_cache] +# Either "redis", "memcached" or "database" default is "database" +type = database + +# cache connectionstring options +# database: will use Grafana primary database. +# redis: config like redis server e.g. `addr=127.0.0.1:6379,pool_size=100,db=0,ssl=false`. Only addr is required. ssl may be 'true', 'false', or 'insecure'. +# memcache: 127.0.0.1:11211 +connstr = + +#################################### Data proxy ########################### +[dataproxy] + +# This enables data proxy logging, default is false +logging = false + +# How long the data proxy waits to read the headers of the response before timing out, default is 30 seconds. +# This setting also applies to core backend HTTP data sources where query requests use an HTTP client with timeout set. +timeout = 30 + +# How long the data proxy waits to establish a TCP connection before timing out, default is 10 seconds. +dialTimeout = 10 + +# How many seconds the data proxy waits before sending a keepalive request. +keep_alive_seconds = 30 + +# How many seconds the data proxy waits for a successful TLS Handshake before timing out. +tls_handshake_timeout_seconds = 10 + +# How many seconds the data proxy will wait for a server's first response headers after +# fully writing the request headers if the request has an "Expect: 100-continue" +# header. A value of 0 will result in the body being sent immediately, without +# waiting for the server to approve. +expect_continue_timeout_seconds = 1 + +# Optionally limits the total number of connections per host, including connections in the dialing, +# active, and idle states. On limit violation, dials will block. +# A value of zero (0) means no limit. +max_conns_per_host = 0 + +# The maximum number of idle connections that Grafana will keep alive. +max_idle_connections = 100 + +# How many seconds the data proxy keeps an idle connection open before timing out. +idle_conn_timeout_seconds = 90 + +# If enabled and user is not anonymous, data proxy will add X-Grafana-User header with username into the request. +send_user_header = false + +# Limit the amount of bytes that will be read/accepted from responses of outgoing HTTP requests. +response_limit = 0 + +# Limits the number of rows that Grafana will process from SQL data sources. +row_limit = 1000000 + +#################################### Analytics ########################### +[analytics] +# Server reporting, sends usage counters to stats.grafana.org every 24 hours. +# No ip addresses are being tracked, only simple counters to track +# running instances, dashboard and error counts. It is very helpful to us. +# Change this option to false to disable reporting. +reporting_enabled = true + +# The name of the distributor of the Grafana instance. Ex hosted-grafana, grafana-labs +reporting_distributor = grafana-labs + +# Set to false to disable all checks to https://grafana.com +# for new versions of grafana. The check is used +# in some UI views to notify that a grafana update exists. +# This option does not cause any auto updates, nor send any information +# only a GET request to https://raw.githubusercontent.com/grafana/grafana/main/latest.json to get the latest version. +check_for_updates = true + +# Set to false to disable all checks to https://grafana.com +# for new versions of plugins. The check is used +# in some UI views to notify that a plugin update exists. +# This option does not cause any auto updates, nor send any information +# only a GET request to https://grafana.com to get the latest versions. +check_for_plugin_updates = true + +# Google Analytics universal tracking code, only enabled if you specify an id here +google_analytics_ua_id = + +# Google Tag Manager ID, only enabled if you specify an id here +google_tag_manager_id = + +# Rudderstack write key, enabled only if rudderstack_data_plane_url is also set +rudderstack_write_key = + +# Rudderstack data plane url, enabled only if rudderstack_write_key is also set +rudderstack_data_plane_url = + +# Rudderstack SDK url, optional, only valid if rudderstack_write_key and rudderstack_data_plane_url is also set +rudderstack_sdk_url = + +# Rudderstack Config url, optional, used by Rudderstack SDK to fetch source config +rudderstack_config_url = + +# Application Insights connection string. Specify an URL string to enable this feature. +application_insights_connection_string = + +# Optional. Specifies an Application Insights endpoint URL where the endpoint string is wrapped in backticks ``. +application_insights_endpoint_url = + +# Controls if the UI contains any links to user feedback forms +feedback_links_enabled = true + +#################################### Security ############################ +[security] +# disable creation of admin user on first start of grafana +disable_initial_admin_creation = false + +# default admin user, created on startup +admin_user = admin + +# default admin password, can be changed before first start of grafana, or in profile settings +admin_password = admin + +# used for signing +secret_key = SW2YcwTIb9zpOOhoPsMm + +# current key provider used for envelope encryption, default to static value specified by secret_key +encryption_provider = secretKey.v1 + +# list of configured key providers, space separated (Enterprise only): e.g., awskms.v1 azurekv.v1 +available_encryption_providers = + +# disable gravatar profile images +disable_gravatar = false + +# data source proxy whitelist (ip_or_domain:port separated by spaces) +data_source_proxy_whitelist = + +# disable protection against brute force login attempts +disable_brute_force_login_protection = false + +# set to true if you host Grafana behind HTTPS. default is false. +cookie_secure = false + +# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict", "none" and "disabled" +cookie_samesite = lax + +# set to true if you want to allow browsers to render Grafana in a ,