Initial commit: add .gitignore and README

This commit is contained in:
defiQUG
2026-02-09 21:51:48 -08:00
commit d4ba3d45e5
174 changed files with 32756 additions and 0 deletions

75
.common.sh Executable file
View File

@@ -0,0 +1,75 @@
#!/bin/sh
me=`basename "$0"`
if [ "$me" = ".common.sh" ];then
echo >&2 "This script is not expected to be run separately."
exit 1
fi
bold=$(tput bold)
normal=$(tput sgr0)
hash docker 2>/dev/null || {
echo >&2 "This script requires Docker but it's not installed."
echo >&2 "Refer to documentation to fulfill requirements."
exit 1
}
docker compose version &>/dev/null
if [ "$?" -eq "1" ];then
echo >&2 "This script requires Docker compose but it's not installed."
echo >&2 "Refer to documentation to fulfill requirements."
exit 1
fi
docker info &>/dev/null
if [ "$?" -eq "1" ];then
echo >&2 "This script requires Docker daemon to run. Start Docker and try again."
echo >&2 "Refer to documentation to fulfill requirements."
exit 1
fi
# We use "SI" measures here because the measurement in the UI and actual bytes
# do not align exactly
PRIVACY_MINIMUM=$(( 6 * 1000 * 1000 * 1000 ))
NORMAL_MINIMUM=$(( 4 * 1000 * 1000 * 1000 ))
dockermem=$(docker info --format '{{.MemTotal}}')
case "$me" in
*privacy* )
if [ $dockermem -lt $PRIVACY_MINIMUM ]; then
echo >&2 "This script requires that docker has at least 6GB of memory available.";
echo >&2 "Refer to documentation to fulfill requirements."
exit 1
fi;
;;
* )
if [ $dockermem -lt $NORMAL_MINIMUM ]; then
echo >&2 "This script requires that docker has at least 4GB of memory available."
echo >&2 "Refer to documentation to fulfill requirements."
exit 1
fi
;;
esac
if [ "${NO_LOCK_REQUIRED}" = "true" ];then
if [ -f ${LOCK_FILE} ];then
echo "Network already in use (${LOCK_FILE} present)." >&2
echo "Restart with ./resume.sh or remove with ./remove.sh before running again." >&2
exit 1
fi
else
composeFile=""
if [ -f ${LOCK_FILE} ]; then
#read the first line of the lock file and store the value as it's the compose file option
composeFile=`sed '1q;d' ${LOCK_FILE}`
else
echo "Network is not running (${LOCK_FILE} not present)." >&2
echo "Run it with ./run.sh first" >&2
exit 1
fi
fi
current_dir=${PWD##*/}

49
.gitignore vendored Normal file
View File

@@ -0,0 +1,49 @@
# Dependencies
node_modules/
.pnpm-store/
vendor/
# Package manager lock files (optional: uncomment to ignore)
# package-lock.json
# yarn.lock
# Environment and secrets
.env
.env.local
.env.*.local
*.env.backup
.env.backup.*
# Logs and temp
*.log
logs/
*.tmp
*.temp
*.tmp.*
# OS
.DS_Store
Thumbs.db
# IDE
.vscode/
.idea/
*.swp
*.swo
*~
# Build / output
dist/
build/
.next/
out/
*.pyc
__pycache__/
.eggs/
*.egg-info/
.coverage
htmlcov/
# Optional
.reports/
reports/

View File

@@ -0,0 +1 @@
docker-compose.yml

108
DOCKER_DNS_FIX.md Normal file
View File

@@ -0,0 +1,108 @@
# Docker DNS Fix for WSL2
## Problem
Docker is failing to pull images with the error:
```
Error response from daemon: Get "https://registry-1.docker.io/v2/": dial tcp: lookup registry-1.docker.io on 10.0.2.3:53: read udp 10.0.2.100:32836->10.0.2.3:53: i/o timeout
```
This is a DNS resolution issue in WSL2.
## Quick Fix (Try This First)
### Option 1: Quick Script
```bash
./quick-dns-fix.sh
```
### Option 2: Manual DNS Fix
```bash
# Fix WSL2 DNS (requires sudo)
sudo bash -c 'cat > /etc/resolv.conf <<EOF
nameserver 8.8.8.8
nameserver 8.8.4.4
nameserver 1.1.1.1
EOF'
# Test DNS
nslookup registry-1.docker.io
```
### Option 3: Docker Desktop Configuration
If using Docker Desktop:
1. Open Docker Desktop
2. Go to **Settings****Docker Engine**
3. Add DNS configuration:
```json
{
"dns": ["8.8.8.8", "8.8.4.4", "1.1.1.1"]
}
```
4. Click **Apply & Restart**
## Full Fix (If Quick Fix Doesn't Work)
Run the comprehensive fix script:
```bash
sudo ./fix-docker-dns.sh
```
This script will:
- Configure WSL2 DNS settings
- Configure Docker daemon DNS
- Restart Docker service
- Test connectivity
## Additional Troubleshooting
### Restart WSL
From Windows PowerShell (as Administrator):
```powershell
wsl --shutdown
```
Then restart your WSL terminal.
### Check Network Connectivity
```bash
# Test basic connectivity
ping 8.8.8.8
# Test DNS resolution
nslookup registry-1.docker.io
host registry-1.docker.io
```
### Check Docker Status
```bash
docker info
docker version
```
### Windows Firewall/Antivirus
- Check if Windows Firewall is blocking Docker
- Temporarily disable antivirus to test
- Add Docker Desktop to firewall exceptions
### Alternative: Use Docker Mirror/Proxy
If you're behind a corporate firewall, you may need to configure a Docker registry mirror or proxy.
## After Fixing
Once DNS is working, try running the Quorum network again:
```bash
cd /home/intlc/projects/quorum-test-network
./run.sh
```
## Prevention
To prevent WSL2 from overwriting `/etc/resolv.conf`:
```bash
sudo bash -c 'cat > /etc/wsl.conf <<EOF
[network]
generateResolvConf = false
EOF'
```
Then manually configure `/etc/resolv.conf` as shown above.

167
METAMASK_SETUP.md Normal file
View File

@@ -0,0 +1,167 @@
# Using Quorum Network with MetaMask
## Overview
Your Quorum test network is running in WSL2, and the RPC endpoints are accessible from Windows. Here's how to configure MetaMask to connect to your local network.
## WSL2 Networking
**Good News**: WSL2 automatically forwards ports to Windows! Services listening on `localhost` in WSL2 are accessible from Windows via `localhost`.
**No Firewall Configuration Needed**: WSL2 handles port forwarding automatically. Docker containers with port mappings like `0.0.0.0:8545->8545/tcp` are accessible from Windows.
## Available RPC Endpoints
### Primary Endpoints (Recommended for MetaMask):
1. **JSON-RPC HTTP**: `http://localhost:8545`
- This is the main RPC endpoint (rpcnode)
- **Use this for MetaMask**
2. **EthSigner Proxy**: `http://localhost:18545`
- Alternative endpoint with transaction signing
- Can also be used with MetaMask
### Validator Endpoints (Optional):
- Validator 1: `http://localhost:21001`
- Validator 2: `http://localhost:21002`
- Validator 3: `http://localhost:21003`
- Validator 4: `http://localhost:21004`
## Setting Up MetaMask
### Step 1: Add Custom Network
1. Open MetaMask in your browser
2. Click the network dropdown (top of MetaMask)
3. Click "Add Network" or "Add a network manually"
### Step 2: Enter Network Details
Fill in the following information:
```
Network Name: Quorum Test Network
New RPC URL: http://localhost:8545
Chain ID: 1337
Currency Symbol: ETH
Block Explorer URL: http://localhost:25000/explorer/nodes
```
**Important Fields:**
- **RPC URL**: `http://localhost:8545` (or `http://localhost:18545` for EthSigner)
- **Chain ID**: `1337` (as configured in your network)
- **Currency Symbol**: `ETH` (or any name you prefer)
- **Block Explorer**: `http://localhost:25000/explorer/nodes` (Quorum Explorer)
### Step 3: Save and Connect
1. Click "Save"
2. MetaMask will switch to your Quorum network
3. You should see the network name in the dropdown
## Testing the Connection
### From Windows Browser/Command Prompt:
1. **Test RPC Endpoint** (from Windows PowerShell or CMD):
```powershell
curl -X POST -H "Content-Type: application/json" --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' http://localhost:8545
```
2. **Or use a browser**:
- Open: http://localhost:25000/explorer/nodes (Quorum Explorer)
- Open: http://localhost:26000 (Blockscout)
- Open: http://localhost:3000 (Grafana)
### From WSL2:
```bash
# Test RPC endpoint
curl -X POST -H "Content-Type: application/json" \
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
http://localhost:8545
# Should return something like:
# {"jsonrpc":"2.0","id":1,"result":"0x..."}
```
## Troubleshooting
### If MetaMask Can't Connect:
1. **Verify RPC is Running**:
```bash
# In WSL2
docker compose ps | grep rpcnode
```
2. **Check Port Binding**:
```bash
# In WSL2
docker ps | grep 8545
```
Should show: `0.0.0.0:8545->8545/tcp`
3. **Test from Windows**:
- Open Windows PowerShell
- Run: `Test-NetConnection -ComputerName localhost -Port 8545`
- Should show `TcpTestSucceeded : True`
4. **Windows Firewall** (if needed):
- Windows Firewall should allow localhost connections by default
- If blocked, add exception for port 8545
- Or temporarily disable firewall to test
5. **WSL2 Port Forwarding**:
- WSL2 should forward ports automatically
- If not working, restart WSL: `wsl --shutdown` (from Windows PowerShell)
### Alternative: Use WSL2 IP Address
If `localhost` doesn't work, you can use the WSL2 IP address:
1. **Find WSL2 IP** (from WSL2):
```bash
hostname -I
# Example output: 192.168.0.3
```
2. **Use in MetaMask**:
- RPC URL: `http://192.168.0.3:8545`
- Replace `192.168.0.3` with your actual WSL2 IP
3. **Note**: This IP may change when WSL2 restarts
## Network Information
- **Chain ID**: 1337
- **Network Name**: Quorum Dev Quickstart
- **Consensus**: QBFT (Quorum Byzantine Fault Tolerance)
- **Genesis Block**: Pre-configured with validators
## Getting Test ETH
Since this is a local test network, you'll need to:
1. **Import a Pre-funded Account**:
- The network has pre-configured accounts with ETH
- Check the `config/nodes/` directory for account files
- Import the private key into MetaMask
2. **Or Mine/Generate ETH**:
- Validators are already running
- You can create new accounts in MetaMask
- Use the network's faucet or transfer from validator accounts
## Additional Resources
- **Quorum Explorer**: http://localhost:25000/explorer/nodes
- **Blockscout**: http://localhost:26000
- **Grafana Dashboard**: http://localhost:3000
- **Prometheus Metrics**: http://localhost:9090
## Security Note
⚠️ **This is a test network** - Never use real private keys or real ETH on this network. It's only for development and testing purposes.

212
README.md Normal file
View File

@@ -0,0 +1,212 @@
# Quorum Dev Quickstart
## Table of Contents
- [Quorum Dev Quickstart](#quorum-dev-quickstart)
- [Table of Contents](#table-of-contents)
- [Prerequisites](#prerequisites)
- [Usage](#usage)
- [Dev Network Setups](#dev-network-setups)
- [i. POA Network ](#i-poa-network-)
- [ii. POA Network with Privacy ](#ii-poa-network-with-privacy-)
- [iii. Smart Contracts \& DApps ](#iii-smart-contracts--dapps-)
- [Moving to production](#moving-to-production)
## Prerequisites
To run these tutorials, you must have the following installed:
- [Docker and Docker-compose](https://docs.docker.com/compose/install/) v2 or higher
| ⚠️ **Note**: If on MacOS or Windows, please ensure that you allow docker to use upto 4G of memory or 6G if running Privacy examples under the _Resources_ section. The [Docker for Mac](https://docs.docker.com/docker-for-mac/) and [Docker Desktop](https://docs.docker.com/docker-for-windows/) sites have details on how to do this at the "Resources" heading |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| ⚠️ **Note**: This has only been tested on Windows 10 Build 18362 and Docker >= 17.12.2 |
| -------------------------------------------------------------------------------------- |
- On Windows ensure that the drive that this repo is cloned onto is a "Shared Drive" with Docker Desktop
- On Windows we recommend running all commands from GitBash
- [Nodejs](https://nodejs.org/en/download/) or [Yarn](https://yarnpkg.com/cli/node)
## Usage
Change directory to the artifacts folder:
`cd quorum-test-network` (default folder location)
**To start services and the network:**
`./run.sh` starts all the docker containers
**To stop services :**
`./stop.sh` stops the entire network, and you can resume where it left off with `./resume.sh`
`./remove.sh ` will first stop and then remove all containers and images
## Dev Network Setups
All our documentation can be found on the [Besu documentation site](https://besu.hyperledger.org/Tutorials/Examples/Private-Network-Example/).
Each quickstart setup is comprised of 4 validators, one RPC node and some monitoring tools like:
- [Alethio Lite Explorer](https://besu.hyperledger.org/en/stable/HowTo/Deploy/Lite-Block-Explorer/) to explore blockchain data at the block, transaction, and account level
- [Metrics monitoring](https://besu.hyperledger.org/en/stable/HowTo/Monitor/Metrics/) via Prometheus and Grafana to give you insights into how the chain is progressing (only with Besu based Quorum)
- Optional [logs monitoring](https://besu.hyperledger.org/en/latest/HowTo/Monitor/Elastic-Stack/) to give you real time logs of the nodes. This feature is enabled with a `-e` flag when starting the sample network
The overall architecture diagrams to visually show components of the blockchain networks is shown below.
**Consensus Algorithm**: The Besu based Quorum variant uses the `IBFT2` consensus mechanism.
**Private TX Manager**: Both blockchain clients use [Tessera](https://docs.tessera.consensys.net/en/latest/)
![Image blockchain](./static/blockchain-network.png)
### i. POA Network <a name="poa-network"></a>
This is the simplest of the networks available and will spin up a blockchain network comprising 4 validators, 1 RPC
node which has an [EthSigner](http://docs.ethsigner.consensys.net/) proxy container linked to it so you can optionally sign transactions. To view the progress
of the network, the Quorum block explorer can be used and is available on `http://localhost:25000`.
Hyperledger Besu based Quorum also deploys monitoring solutions.
You can choose to make metrics monitoring via Prometheus available on `http://localhost:9090`,
paired with Grafana with custom dashboards available on `http://localhost:3000`.
You can also use Splunk to see all logs, traces and metrics available at `http://localhost:8000` (with the credentials admin/quickstart).
Essentially you get everything in the architecture diagram above, bar the yellow privacy block
Use cases:
- you are learning about how Ethereum works
- you are looking to create a Mainnet or Ropsten node but want to see how it works on a smaller scale
- you are a DApp Developer looking for a robust, simple network to use as an experimental testing ground for POCs.
### ii. POA Network with Privacy <a name="poa-network-privacy"></a>
This network is slightly more advanced than the former and you get everything from the POA network above and a few
Ethereum clients each paired with [Tessera](https://docs.tessera.consensys.net/en/latest/) for its Private Transaction Mananger.
As before, to view the progress of the network, the Quorum block explorer can be used and is available on `http://localhost:25000`.
Hyperledger Besu based Quorum also deploys monitoring solutions.
You can choose to make metrics monitoring via Prometheus available on `http://localhost:9090`,
paired with Grafana with custom dashboards available on `http://localhost:3000`.
You can also use Splunk to see all logs, traces and metrics available at `http://localhost:8000` (with the credentials admin/quickstart).
Essentially you get everything in the architecture diagram above.
Use cases:
- you are learning about how Ethereum works
- you are a user looking to execute private transactions at least one other party
- you are looking to create a private Ethereum network with private transactions between two or more parties.
Once the network is up and running you can make public transactions on the chain and interact with the smart contract at its deployed address,
and you can also make private transaction between members and verify that other nodes do not see it.
Under the smart_contracts folder there is a `SimpleStorage` contract which we use for both as an example. The `SimpleStorage` contract will store a value and emit an event with that stored value, you can either observe the events or call the `get` function on the contract to get the value.
For the public transaction:
```
cd smart_contracts
npm install
node scripts/public/public_tx.js
```
which creates an account and then deploys the contract with the account's address. It also initializes the default constructor
with a value (47). Once done, it will call the `get` function on the contract to check the value at the address, and
you should see it return the value. Then it will call the `set` function on the contract and update the value (123)
and then verify the address to make sure its been updated. It will then call `getPastEvents("allEvents", { fromBlock: 0, toBlock: 'latest' })` to fetch all the historical events for that contract, this confirm that events have been emited for both the constructor and the set.
```
node scripts/public/public_tx.js
{
address: '0x36781cB22798149d47c55A228f186F583fA9F64b',
privateKey: '0x6ee9f728b2e4c092243427215ecd12e53b9c0e388388dc899b1438c487c02b61',
signTransaction: [Function: signTransaction],
sign: [Function: sign],
encrypt: [Function: encrypt]
}
Creating transaction...
Signing transaction...
Sending transaction...
tx transactionHash: 0xaf86a44b2a477fbc4a7c9f71eace0753ac1ffc4c446aa779dbb8682bf765e8b9
tx contractAddress: 0xE12f1232aE87862f919efb7Df27DC819F0240F07
Contract deployed at address: 0xE12f1232aE87862f919efb7Df27DC819F0240F07
Use the smart contracts 'get' function to read the contract's constructor initialized value ..
Obtained value at deployed contract is: 47
Use the smart contracts 'set' function to update that value to 123 ..
Verify the updated value that was set ..
Obtained value at deployed contract is: 123
Obtained all value events from deployed contract : [47,123]
```
For the private transaction:
```
cd smart_contracts
npm install
node scripts/privacy/private_tx.js
```
which deploys the contract and sends an arbitrary value (47) from `Member1` to `Member3`. Once done, it queries all three members (tessera)
to check the value at an address, and you should observe that only `Member1` & `Member3` have this information as they were involved in the transaction
and that `Member2` responds with a `0x` to indicate it is unaware of the transaction.
```
node scripts/privacy/private_tx.js
Creating contract...
Getting contractAddress from txHash: 0xc1b57f6a7773fe887afb141a09a573d19cb0fdbb15e0f2b9ed0dfead6f5b5dbf
Waiting for transaction to be mined ...
Address of transaction: 0x8220ca987f7bb7f99815d0ef64e1d8a072a2c167
Use the smart contracts 'get' function to read the contract's constructor initialized value ..
Waiting for transaction to be mined ...
Member1 value from deployed contract is: 0x000000000000000000000000000000000000000000000000000000000000002f
Use the smart contracts 'set' function to update that value to 123 .. - from member1 to member3
Transaction hash: 0x387c6627fe87e235b0f2bbbe1b2003a11b54afc737dca8da4990d3de3197ac5f
Waiting for transaction to be mined ...
Verify the private transaction is private by reading the value from all three members ..
Waiting for transaction to be mined ...
Member1 value from deployed contract is: 0x000000000000000000000000000000000000000000000000000000000000007b
Waiting for transaction to be mined ...
Member2 value from deployed contract is: 0x
Waiting for transaction to be mined ...
Member3 value from deployed contract is: 0x000000000000000000000000000000000000000000000000000000000000007b
```
Further [documentation](https://besu.hyperledger.org/en/stable/Tutorials/Privacy/eeajs-Multinode-example/) for this example and a [video tutorial](https://www.youtube.com/watch?v=Menekt6-TEQ)
is also available.
There is an additional erc20 token example that you can also test with: executing `node example/erc20.js` deploys a `HumanStandardToken` contract and transfers 1 token to Node2.
This can be verified from the `data` field of the `logs` which is `1`.
### iii. Smart Contracts & DApps <a name="poa-network-dapps"></a>
As an example we've included the Truffle Pet-Shop Dapp in the `dapps` folder and here is a [video tutorial](https://www.youtube.com/watch?v=_3E9FRJldj8) you
can follow of deployment to the network and using it. Please import the private key `0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3` to
Metmask **before** proceeding to build and run the DApp with `run-dapp.sh`. Behind the scenes, this has used a smart contract that is compiled and then
deployed (via a migration) to our test network. The source code for the smart contract and the DApp can be found in the folder `dapps/pet-shop`
| ⚠️ **WARNING**: |
| ---
This is a test account only and the private and public keys are publicly visible. **Using test accounts on Ethereum mainnet and production networks can lead to loss of funds and identity fraud.** In this documentation, we only provide test accounts for ease of testing and learning purposes; never use them for other purposes. **Always secure your Ethereum mainnet and any production account properly.** See for instance [MyCrypto "Protecting Yourself and Your Funds" guide](https://support.mycrypto.com/staying-safe/protecting-yourself-and-your-funds). |
![Image dapp](./static/qs-dapp.png)
As seen in the architecture overview diagram you can extend the network with monitoring, logging, smart contracts, DApps and so on
- Once you have a network up and running from above, install [metamask](https://metamask.io/) as an extension in your browser
- Once you have setup your own private account, select 'My Accounts' by clicking on the avatar pic and then 'Import Account' and enter the private key `0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3`
- Build the DApp container and deploy by
```
cd dapps/pet-shop
./run_dapp.sh
```
When that completes open a new tab in your browser and go to `http://localhost:3001` which opens the Truffle pet-shop box app
and you can adopt a pet from there. NOTE: Once you have adopted a pet, you can also go to the block explorer `http://localhost:25000`
and search for the transaction where you can see its details recorded. Metamask will also have a record of any transactions.
## Moving to production
When you are ready to move to production, please create new keys for your nodes using the
[Quorum Genesis Tool](https://www.npmjs.com/package/quorum-genesis-tool) and read through the the
[Besu documentation](https://besu.hyperledger.org/en/latest/HowTo/Deploy/Cloud/)

90
SETUP_COMPLETE.md Normal file
View File

@@ -0,0 +1,90 @@
# Quorum Network Setup - DNS Fix Complete
## ✅ Completed Steps
### 1. DNS Configuration Fixed
- ✅ Updated `/etc/resolv.conf` with reliable DNS servers (8.8.8.8, 8.8.4.4, 1.1.1.1)
- ✅ Created `/etc/wsl.conf` to prevent WSL from overwriting DNS settings
- ✅ DNS fix script created: `fix-dns-now.sh`
### 2. Docker Connectivity Verified
- ✅ Docker is running (version 29.0.2)
- ✅ Docker Compose is working (version v2.40.3)
- ✅ Docker can now pull images (DNS resolution working)
### 3. Network Startup Initiated
- ✅ Lock file removed
- ✅ Docker Compose configuration validated
- ✅ Image pulling started successfully
## 📋 Next Steps
### To Start the Network:
1. **Pull all required images** (this may take a few minutes):
```bash
cd /home/intlc/projects/quorum-test-network
docker compose pull
```
2. **Start the network**:
```bash
./run.sh
```
Or manually:
```bash
docker compose up --detach
```
3. **Check network status**:
```bash
docker compose ps
./list.sh # List endpoints and services
```
### If You Still Encounter DNS Issues:
1. **Configure Docker Desktop DNS** (if using Docker Desktop):
- Open Docker Desktop
- Settings → Docker Engine
- Add: `"dns": ["8.8.8.8", "8.8.4.4", "1.1.1.1"]`
- Apply & Restart
2. **Restart WSL** (from Windows PowerShell):
```powershell
wsl --shutdown
```
Then restart your WSL terminal.
## 📁 Files Created
- `fix-dns-now.sh` - Quick DNS fix script
- `quick-dns-fix.sh` - Comprehensive DNS fix script
- `fix-docker-dns.sh` - Full DNS and Docker daemon configuration
- `DOCKER_DNS_FIX.md` - Complete DNS troubleshooting guide
- `configure-docker-desktop-dns.md` - Docker Desktop DNS configuration guide
- `SETUP_COMPLETE.md` - This file
## 🔍 Network Services
The Quorum network includes:
- **Validators**: validator1, validator2, validator3, validator4
- **RPC Node**: rpcnode
- **EthSigner Proxy**: ethsignerProxy
- **Explorer**: Quorum Explorer
- **Blockscout**: Block explorer
- **Monitoring**: Prometheus, Grafana, Loki, Promtail
## 🌐 Endpoints (After Startup)
- JSON-RPC HTTP: http://localhost:8545
- JSON-RPC WebSocket: ws://localhost:8546
- Web Block Explorer: http://localhost:25000/explorer/nodes
- Grafana: http://localhost:3000
- Prometheus: http://localhost:9090
## ⚠️ Note
The `version` attribute in `docker-compose.yml` is obsolete. You can remove it to eliminate the warning, but it won't affect functionality.

72
STARTUP_STATUS.md Normal file
View File

@@ -0,0 +1,72 @@
# Quorum Network Startup Status
## ✅ Completed Steps
1. **DNS Configuration Fixed**
- ✅ WSL2 DNS updated with reliable servers (8.8.8.8, 8.8.4.4, 1.1.1.1)
- ✅ DNS configuration added to all Docker Compose services
- ✅ Docker can now pull images successfully
2. **Docker Compose Configuration**
- ✅ Removed obsolete `version` attribute
- ✅ Added DNS servers to all 13 services
- ✅ Configuration validated
3. **Image Pulling**
- ✅ Images are being pulled successfully
- ✅ 8+ required images available locally
## 🚀 Starting the Network
The network startup process has been initiated. To check status:
```bash
# Check container status
docker compose ps
# View logs
docker compose logs
# List endpoints
./list.sh
```
## 📋 Services in the Network
- **Validators**: validator1, validator2, validator3, validator4
- **RPC Node**: rpcnode
- **EthSigner Proxy**: ethsignerProxy
- **Explorer**: Quorum Explorer
- **Blockscout**: Block explorer with PostgreSQL
- **Monitoring**: Prometheus, Grafana, Loki, Promtail
## 🌐 Expected Endpoints
Once all containers are running:
- JSON-RPC HTTP: http://localhost:8545
- JSON-RPC WebSocket: ws://localhost:8546
- Web Block Explorer: http://localhost:25000/explorer/nodes
- Grafana: http://localhost:3000
- Prometheus: http://localhost:9090
## ⚠️ Important Notes
1. **DNS Configuration**: All services now have explicit DNS servers configured in docker-compose.yml
2. **Docker Desktop**: If you're using Docker Desktop, you may still want to configure DNS in Docker Desktop settings for better performance (see `URGENT_DOCKER_DNS_FIX.md`)
3. **Startup Time**: The network may take 1-2 minutes to fully start all services
## 🔧 Troubleshooting
If containers don't start:
```bash
# Check logs
docker compose logs [service-name]
# Restart services
docker compose restart
# Remove and recreate
docker compose down
docker compose up --detach
```

69
URGENT_DOCKER_DNS_FIX.md Normal file
View File

@@ -0,0 +1,69 @@
# ⚠️ URGENT: Docker Desktop DNS Configuration Required
## Problem
Docker Desktop is still using the old DNS server (10.0.2.3) which is timing out. The WSL2 DNS fix we applied helps the host, but **Docker Desktop runs in its own VM and needs separate DNS configuration**.
## Solution: Configure Docker Desktop DNS
### Step 1: Open Docker Desktop
1. Look for the Docker Desktop icon in your Windows system tray (bottom right)
2. Right-click and select "Settings" or open Docker Desktop
### Step 2: Navigate to Docker Engine Settings
1. In Docker Desktop, click the **Settings** (⚙️) icon in the top right
2. In the left sidebar, click **"Docker Engine"**
### Step 3: Add DNS Configuration
1. You'll see a JSON configuration editor
2. **Add or update** the `dns` field in the JSON:
**If the JSON is empty or minimal:**
```json
{
"dns": ["8.8.8.8", "8.8.4.4", "1.1.1.1"]
}
```
**If there's already content, add the dns line:**
```json
{
"builder": {
"gc": {
"enabled": true
}
},
"dns": ["8.8.8.8", "8.8.4.4", "1.1.1.1"],
"experimental": false
}
```
### Step 4: Apply and Restart
1. Click **"Apply & Restart"** button
2. Wait for Docker Desktop to restart (this takes 30-60 seconds)
### Step 5: Verify
After Docker Desktop restarts, test in WSL2:
```bash
cd /home/intlc/projects/quorum-test-network
docker pull alpine:latest
```
If this works, the DNS is fixed!
## Alternative: Restart WSL
If you can't access Docker Desktop settings right now:
1. From Windows PowerShell (as Administrator):
```powershell
wsl --shutdown
```
2. Restart your WSL terminal
3. Try the network again
## Quick Reference
- **Docker Desktop Settings Location**: Settings → Docker Engine
- **DNS Servers to Add**: `["8.8.8.8", "8.8.4.4", "1.1.1.1"]`
- **After Configuration**: Click "Apply & Restart"
## Why This is Needed
Docker Desktop runs in a separate VM from WSL2. The DNS configuration we applied to WSL2 doesn't automatically apply to Docker Desktop's VM. Docker Desktop needs its own DNS configuration.

12
attach.sh Executable file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
MEMBER_NUMBER=$1
MEMBER_NODE="member${MEMBER_NUMBER}quorum"
echo "Attempting to connect to $MEMBER_NODE"
if [ -z `docker compose -f docker-compose.yml ps -q ${MEMBER_NODE} 2>/dev/null` ] ; then
echo "$MEMBER_NUMBER is not a valid member node. Must be between 1 and 3" >&2
exit 1
fi
# can also do geth attach http://localhost:8545
docker compose exec ${MEMBER_NODE} /bin/sh -c "geth attach /data/geth.ipc"

91
chainlens/5xx.html Normal file
View File

@@ -0,0 +1,91 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Chainlens | Blockchain Explorer</title>
<meta name="description" content="Blockchain Explorer" />
<meta http-equiv="refresh" content="10" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5.0" />
<link rel="stylesheet" href="https://use.typekit.net/odh5jwh.css" />
<style>
html,
body {
margin: 0;
padding: 0;
font-family: 'Usual', 'Arial', sans-serif;
font-weight: 400;
width: 100%;
height: 100%;
overflow-x: hidden;
}
div.creating-environment {
display: flex;
align-items: center;
justify-content: center;
flex-direction: row;
width: 100%;
height: 100%;
}
div.icon-left {
margin-right: 140px;
}
div.cog {
animation-name: rotation;
animation-duration: 11s;
animation-timing-function: ease;
animation-iteration-count: infinite;
transform-origin: 50% 50%;
}
@keyframes rotation {
0% { transform: rotate(0deg);}
12.5% {transform: rotate(45deg);}
25% {transform: rotate(90deg);}
37.5% {transform: rotate(135deg);}
50% {transform: rotate(180deg);}
62.5% {transform: rotate(225deg);}
75% {transform: rotate(270deg);}
87.5% {transform: rotate(315deg);}
100% {transform: rotate(360deg);}
}
div.text-right {
}
h2 {
font-size: 36px;
line-height: 44px;
font-weight: 600;
}
p {
font-size: 20px;
line-height: 24px;
margin: 12px 0 26px;
color: #9999AA;
}
a.link {
font-size: 12px;
line-height: 16px;
color: #786DFD;
}
</style>
</head>
<body>
<div class="creating-environment">
<div class="icon-left">
<div class="cog">
<svg width="150" height="150" viewBox="0 0 150 150" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M121.5 80.6875L121.437 80.8125C121.687 78.75 121.937 76.625 121.937 74.5C121.937 72.375 121.75 70.375 121.5 68.3125L121.562 68.4375L136.812 56.4375L121.625 30.0625L103.687 37.3125L103.75 37.375C100.5 34.875 96.9375 32.75 93.0625 31.125H93.125L90.25 12H59.8125L57.0625 31.1875H57.125C53.25 32.8125 49.6875 34.9375 46.4375 37.4375L46.5 37.375L28.5 30.0625L13.25 56.4375L28.5 68.4375L28.5625 68.3125C28.3125 70.375 28.125 72.375 28.125 74.5C28.125 76.625 28.3125 78.75 28.625 80.8125L28.5625 80.6875L15.4375 91L13.375 92.625L28.5625 118.875L46.5625 111.688L46.4375 111.438C49.75 114 53.3125 116.125 57.25 117.75H57.0625L59.875 137H90.1875C90.1875 137 90.375 135.875 90.5625 134.375L92.9375 117.812H92.875C96.75 116.188 100.375 114.062 103.687 111.5L103.562 111.75L121.562 118.938L136.75 92.6875C136.75 92.6875 135.875 91.9375 134.687 91.0625L121.5 80.6875ZM75 96.375C62.9375 96.375 53.125 86.5625 53.125 74.5C53.125 62.4375 62.9375 52.625 75 52.625C87.0625 52.625 96.875 62.4375 96.875 74.5C96.875 86.5625 87.0625 96.375 75 96.375Z"
fill="#483AFC"
></path>
</svg>
</div>
</div>
<div class="text-right">
<h2>Your environment is being created</h2>
<p>Give us a few minutes</p>
<a class="link" href="https://chainlens.com/" target="_blank">
Chainlens — web3labs.com
</a>
</div>
</div>
</body>
</html>

21
chainlens/nginx.conf Normal file
View File

@@ -0,0 +1,21 @@
events { }
http {
server {
listen 80;
charset utf-8;
location /api/ {
proxy_pass http://chainlensapi:8090/;
}
location / {
proxy_pass http://chainlensweb:3000/;
}
error_page 500 502 503 504 /5xx.html;
location /5xx.html {
root /www/error_pages/;
}
}
}

View File

@@ -0,0 +1,62 @@
{
"config":{
"chainId":1337,
"londonBlock": 0,
"zeroBaseFee": true,
"clique":{
"blockperiodseconds":15,
"epochlength":30000
}
},
"coinbase":"0x0000000000000000000000000000000000000000",
"difficulty":"0x1",
"extraData":"0x000000000000000000000000000000000000000000000000000000000000000093917cadbace5dfce132b991732c6cda9bcc5b8a0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"gasLimit":"0xa00000",
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce":"0x0",
"timestamp":"0x5c51a607",
"alloc": {
"0xfe3b557e8fb62b89f4916b721be55ceb828dbd73": {
"privateKey": "0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63",
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
"balance": "0x130EE8E7179044400000"
},
"0x627306090abaB3A6e1400e9345bC60c78a8BEf57": {
"privateKey": "0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
"balance": "90000000000000000000000"
},
"0xf17f52151EbEF6C7334FAD080c5704D77216b732": {
"privateKey": "0xae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f",
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored",
"balance": "90000000000000000000000"
},
"0xf0e2db6c8dc6c681bb5d6ad121a107f300e9b2b5": {
"balance": "1000000000000000000000000000"
},
"0xca843569e3427144cead5e4d5999a3d0ccf92b8e": {
"balance": "1000000000000000000000000000"
},
"0x0fbdc686b912d7722dc86510934589e0aaf3b55a": {
"balance": "1000000000000000000000000000"
},
"0xc9c913c8c3c1cd416d80a0abf475db2062f161f6": {
"balance": "1000000000000000000000000000"
},
"0xed9d02e382b34818e88b88a309c7fe71e65f419d": {
"balance": "1000000000000000000000000000"
},
"0xb30f304642de3fee4365ed5cd06ea2e69d3fd0ca": {
"balance": "1000000000000000000000000000"
},
"0x0886328869e4e1f401e1052a5f4aae8b45f42610": {
"balance": "1000000000000000000000000000"
},
"0xf48de4a0c2939e62891f3c6aca68982975477e45": {
"balance": "1000000000000000000000000000"
}
},
"number":"0x0",
"gasUsed":"0x0",
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

42
config/besu/config.toml Normal file
View File

@@ -0,0 +1,42 @@
genesis-file="/config/genesis.json"
node-private-key-file="/opt/besu/keys/nodekey"
logging="INFO"
data-path="/opt/besu/data"
host-allowlist=["*"]
min-gas-price=0
# rpc
rpc-http-enabled=true
rpc-http-host="0.0.0.0"
rpc-http-port=8545
rpc-http-cors-origins=["*"]
# ws
rpc-ws-enabled=true
rpc-ws-host="0.0.0.0"
rpc-ws-port=8546
# graphql
graphql-http-enabled=true
graphql-http-host="0.0.0.0"
graphql-http-port=8547
graphql-http-cors-origins=["*"]
# metrics
metrics-enabled=true
metrics-host="0.0.0.0"
metrics-port=9545
# permissions
permissions-nodes-config-file-enabled=true
permissions-nodes-config-file="/config/permissions_config.toml"
# bootnodes
bootnodes=["enode://8208a3f344695d44e9cf2c023683cbea7b9343e2f70a5e804bd2c93858e945f8f91439eef96a4ab6c47ff06637d6fbe6472f96de1655a1bee57ea896654f3a22@172.16.239.11:30303"]
# Discovery at boot is set to a list of static files, but will also discover new nodes should they be added
# static nodes
static-nodes-file="/config/static-nodes.json"
discovery-enabled=true

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Properties>
<Property name="root.log.level">${env:LOG_LEVEL:-INFO}</Property>
<Property name="root.log.logger">${env:LOGGER:-Console}</Property>
<Property name="host">${env:HOST:-${docker:containerId:-${hostName:-locahost}}}</Property>
<Property name="splunk.url">${env:SPLUNK_URL}</Property>
<Property name="splunk.token">${env:SPLUNK_TOKEN}</Property>
<Property name="splunk.index">${env:SPLUNK_INDEX}</Property>
<Property name="splunk.source">${env:SPLUNK_SOURCE:-besu}</Property>
<Property name="splunk.sourcetype">${env:SPLUNK_SOURCETYPE:-besu}</Property>
<Property name="splunk.batch_size_bytes">${env:SPLUNK_BATCH_SIZE_BYTES:-65536}</Property>
<Property name="splunk.batch_size_count">${env:SPLUNK_BATCH_SIZE_COUNT:-1000}</Property>
<Property name="splunk.batch_interval">${env:SPLUNK_BATCH_INTERVAL:-500}</Property>
<Property name="splunk.disableCertificateValidation">${env:SPLUNK_SKIPTLSVERIFY:-false}</Property>
</Properties>
<Appenders>
<Routing name="Router">
<Routes pattern="$${sys:root.log.logger}">
<Route key="Splunk">
<SplunkHttp name="Splunk"
url="${sys:splunk.url}"
token="${sys:splunk.token}"
host="${sys:host}"
index="${sys:splunk.index}"
source="${sys:splunk.source}"
sourcetype="${sys:splunk.sourcetype}"
messageFormat="text"
batch_size_bytes="${sys:splunk.batch_size_bytes}"
batch_size_count="${sys:splunk.batch_size_count}"
batch_interval="${sys:splunk.batch_interval}"
disableCertificateValidation="${sys:splunk.disableCertificateValidation}">
<PatternLayout pattern="%msg" />
</SplunkHttp>
</Route>
</Routes>
</Routing>
</Appenders>
<Loggers>
<Logger name="org.apache.logging.log4j.status.StatusLogger" level="OFF"/>
<Root level="${sys:root.log.level}">
<AppenderRef ref="Router" />
</Root>
</Loggers>
</Configuration>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO" monitorInterval="5">
<Properties>
<Property name="root.log.level">INFO</Property>
</Properties>
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout alwaysWriteExceptions="false" pattern='{"timestamp":"%d{ISO8601}","container":"${hostName}","level":"%level","thread":"%t","class":"%c{1}","message":"%msg","throwable":"%enc{%throwable}{JSON}"}%n'/>
</Console>
<RollingFile name="RollingFile" fileName="/tmp/besu/besu-${env:HOSTNAME}.log" filePattern="/tmp/besu/besu-${env:HOSTNAME}_%d{yyyy-MM-dd}_%i.log.gz" >
<PatternLayout alwaysWriteExceptions="false" pattern='{"timestamp":"%d{ISO8601}","container":"${hostName}","level":"%level","thread":"%t","class":"%c{1}","message":"%msg","throwable":"%enc{%throwable}{JSON}"}%n'/>
<Policies>
<SizeBasedTriggeringPolicy size="10 MB" />
</Policies>
<DefaultRolloverStrategy max="5" />
</RollingFile>
</Appenders>
<Loggers>
<Root level="${sys:root.log.level}">
<AppenderRef ref="RollingFile" />
<AppenderRef ref="Console" />
</Root>
</Loggers>
</Configuration>

View File

@@ -0,0 +1,11 @@
nodes-allowlist=[
"enode://8208a3f344695d44e9cf2c023683cbea7b9343e2f70a5e804bd2c93858e945f8f91439eef96a4ab6c47ff06637d6fbe6472f96de1655a1bee57ea896654f3a22@172.16.239.11:30303",
"enode://b9050e002aa42464e6b07c811a1f9dfec01249af03f67b753e8415420649b184447bb2a784863ccbf327ad9e31aaba803464979dfe6a7facc669151a5fa6ad1b@172.16.239.12:30303",
"enode://59cf0c623c582fa9b19bdf70fb6bade07f4ae32218dd4d1c7e2c7e65acf87da45cf2ab55d16d27360aafef17622c37c09db60d7680ebcc17b78867f4c05bcaa4@172.16.239.13:30303",
"enode://2fd5b5b6ad529f55b71602026d1849d0036f06482368b5812fa793014195d3571b0840dbc4175617de2a12db8f1222c012420d471ae5c0d982118625cae58868@172.16.239.14:30303",
"enode://86fcc16f4730fbfd238dc17ea552854c0943923bb1d5e886e5601b8d884fb0519060e0023f495dd24ffe60a65660fb7fdcdebfceedd2b3673dfa63658825924b@172.16.239.15:30303",
"enode://98496800174b3c73ae33cba59f8f5e686cd488f7897c2edb52e2cf46383d75cd03dbb58dde07185bc0953f98800ca9a89f4b5ef450c5e51292ea08ec6130ee0c@172.16.239.16:30303",
"enode://ad2c79c6561bc8212c2e8382611c62e406e767d1f3106c68ca206900f575cb8ba9a8be111c645cd9803701d684454c782c40d2361b000a32ed03d26228b30ec1@172.16.239.17:30303",
"enode://af19c92deb635bd7720634de9b2e7908208530d6f5e96eee003a8f1799e5be4037957d7e2fdbe3605e3a38dab05c961679c02133a0e624e23a72f7961e8af6ac@172.16.239.18:30303"
]

View File

@@ -0,0 +1,11 @@
[
"enode://8208a3f344695d44e9cf2c023683cbea7b9343e2f70a5e804bd2c93858e945f8f91439eef96a4ab6c47ff06637d6fbe6472f96de1655a1bee57ea896654f3a22@172.16.239.11:30303",
"enode://b9050e002aa42464e6b07c811a1f9dfec01249af03f67b753e8415420649b184447bb2a784863ccbf327ad9e31aaba803464979dfe6a7facc669151a5fa6ad1b@172.16.239.12:30303",
"enode://59cf0c623c582fa9b19bdf70fb6bade07f4ae32218dd4d1c7e2c7e65acf87da45cf2ab55d16d27360aafef17622c37c09db60d7680ebcc17b78867f4c05bcaa4@172.16.239.13:30303",
"enode://2fd5b5b6ad529f55b71602026d1849d0036f06482368b5812fa793014195d3571b0840dbc4175617de2a12db8f1222c012420d471ae5c0d982118625cae58868@172.16.239.14:30303",
"enode://86fcc16f4730fbfd238dc17ea552854c0943923bb1d5e886e5601b8d884fb0519060e0023f495dd24ffe60a65660fb7fdcdebfceedd2b3673dfa63658825924b@172.16.239.15:30303",
"enode://98496800174b3c73ae33cba59f8f5e686cd488f7897c2edb52e2cf46383d75cd03dbb58dde07185bc0953f98800ca9a89f4b5ef450c5e51292ea08ec6130ee0c@172.16.239.16:30303",
"enode://ad2c79c6561bc8212c2e8382611c62e406e767d1f3106c68ca206900f575cb8ba9a8be111c645cd9803701d684454c782c40d2361b000a32ed03d26228b30ec1@172.16.239.17:30303",
"enode://af19c92deb635bd7720634de9b2e7908208530d6f5e96eee003a8f1799e5be4037957d7e2fdbe3605e3a38dab05c961679c02133a0e624e23a72f7961e8af6ac@172.16.239.18:30303"
]

View File

@@ -0,0 +1,8 @@
const Web3 = require('web3')
// Web3 initialization (should point to the JSON-RPC endpoint)
const web3 = new Web3(new Web3.providers.HttpProvider('http://127.0.0.1:8545'))
var V3KeyStore = web3.eth.accounts.encrypt("797bbe0373132e8c5483515b68ecbb6d3581b56f0205b653ad2b30a559e83891", "Password1");
console.log(JSON.stringify(V3KeyStore));
process.exit();

1
config/ethsigner/key Normal file
View File

@@ -0,0 +1 @@
{"version":3,"id":"0e3d044a-26b4-429f-a5d3-def4a62a77ec","address":"9b790656b9ec0db1936ed84b3bea605873558198","crypto":{"ciphertext":"4d34be50618c36fb57349a8a7dc7b0c46f7c6883c817087863ff4f1fbc957582","cipherparams":{"iv":"723a6a815dcaf255ebd1da1bfb14e1b8"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"dklen":32,"salt":"3d04a3f225d3d5874388484730a30d45627399d69721a7c7fb653f8e0e90a67b","n":8192,"r":8,"p":1},"mac":"5c04e705196b35abace4da61700921d7762dba782ed68a4cd9917dd75badaacb"}}

View File

@@ -0,0 +1 @@
Password1

View File

@@ -0,0 +1,884 @@
{
"__inputs": [
{
"name": "Prometheus",
"label": "Prometheus",
"description": "",
"type": "datasource",
"pluginId": "prometheus",
"pluginName": "Prometheus"
}
],
"__requires": [
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "6.2.2"
},
{
"type": "panel",
"id": "graph",
"name": "Graph",
"version": ""
},
{
"type": "datasource",
"id": "prometheus",
"name": "Prometheus",
"version": "1.0.0"
},
{
"type": "panel",
"id": "table",
"name": "Table",
"version": ""
}
],
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"description": "Provides an overview of Besu nodes",
"editable": true,
"gnetId": 10273,
"graphTooltip": 0,
"id": null,
"iteration": 1560146167177,
"links": [],
"panels": [
{
"columns": [],
"fontSize": "120%",
"gridPos": {
"h": 9,
"w": 24,
"x": 0,
"y": 0
},
"id": 10,
"links": [],
"options": {},
"pageSize": null,
"scroll": true,
"showHeader": true,
"sort": {
"col": 2,
"desc": true
},
"styles": [
{
"alias": "",
"colorMode": null,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "job",
"thresholds": [],
"type": "hidden",
"unit": "short"
},
{
"alias": "Chain Height",
"colorMode": null,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "Value #A",
"thresholds": [],
"type": "number",
"unit": "locale"
},
{
"alias": "Total Difficulty",
"colorMode": null,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "Value #B",
"thresholds": [],
"type": "number",
"unit": "sci"
},
{
"alias": "",
"colorMode": null,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "Time",
"thresholds": [],
"type": "hidden",
"unit": "short"
},
{
"alias": "",
"colorMode": null,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "__name__",
"thresholds": [],
"type": "hidden",
"unit": "short"
},
{
"alias": "Peer Count",
"colorMode": null,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "Value #C",
"thresholds": [
""
],
"type": "number",
"unit": "locale"
},
{
"alias": "Block Time (5m avg)",
"colorMode": null,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "Value #D",
"thresholds": [],
"type": "number",
"unit": "s"
},
{
"alias": "System",
"colorMode": null,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "instance",
"thresholds": [],
"type": "string",
"unit": "short",
"valueMaps": []
},
{
"alias": "Time Since Last Block",
"colorMode": "value",
"colors": [
"rgba(50, 172, 45, 0.97)",
"rgba(237, 129, 40, 0.89)",
"rgba(245, 54, 54, 0.9)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 0,
"mappingType": 1,
"pattern": "Value #E",
"thresholds": [
"120",
"240"
],
"type": "number",
"unit": "dtdurations"
},
{
"alias": "Target Chain Height",
"colorMode": null,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "Value #F",
"thresholds": [],
"type": "number",
"unit": "locale"
},
{
"alias": "Blocks Behind",
"colorMode": "value",
"colors": [
"rgba(50, 172, 45, 0.97)",
"rgba(237, 129, 40, 0.89)",
"rgba(245, 54, 54, 0.9)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 2,
"mappingType": 1,
"pattern": "Value #G",
"thresholds": [
"1",
"10"
],
"type": "number",
"unit": "locale",
"valueMaps": [
{
"text": "Yes",
"value": "1"
},
{
"text": "No",
"value": "0"
}
]
},
{
"alias": "% Peer Limit Used",
"colorMode": "value",
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"dateFormat": "YYYY-MM-DD HH:mm:ss",
"decimals": 0,
"mappingType": 1,
"pattern": "Value #H",
"thresholds": [
"0.25",
"0.75"
],
"type": "number",
"unit": "percentunit"
},
{
"alias": "",
"colorMode": null,
"colors": [
"rgba(245, 54, 54, 0.9)",
"rgba(237, 129, 40, 0.89)",
"rgba(50, 172, 45, 0.97)"
],
"decimals": 2,
"pattern": "/.*/",
"thresholds": [],
"type": "number",
"unit": "short"
}
],
"targets": [
{
"expr": "sum by (instance) (ethereum_blockchain_height{instance=~\"$system\"})",
"format": "table",
"instant": true,
"interval": "",
"intervalFactor": 1,
"legendFormat": "Chain Height",
"refId": "A"
},
{
"expr": "sum by (instance) (ethereum_best_known_block_number{instance=~\"$system\"})",
"format": "table",
"instant": true,
"intervalFactor": 1,
"refId": "F"
},
{
"expr": "sum by (instance) (ethereum_best_known_block_number{instance=~\"$system\"} - ethereum_blockchain_height{instance=~\"$system\"})",
"format": "table",
"instant": true,
"intervalFactor": 1,
"refId": "G"
},
{
"expr": "sum by (instance) (besu_blockchain_difficulty_total{instance=~\"$system\"})",
"format": "table",
"instant": true,
"intervalFactor": 1,
"legendFormat": "Total Difficulty",
"refId": "B"
},
{
"expr": "sum by (instance) (1/rate(ethereum_blockchain_height{instance=~\"$system\"}[5m]))",
"format": "table",
"instant": true,
"intervalFactor": 1,
"refId": "D"
},
{
"expr": "sum by (instance) (time() - besu_blockchain_chain_head_timestamp{instance=~\"$system\"})",
"format": "table",
"instant": true,
"intervalFactor": 1,
"refId": "E"
},
{
"expr": "sum by (instance) (ethereum_peer_count{instance=~\"$system\"})",
"format": "table",
"instant": true,
"intervalFactor": 1,
"legendFormat": "Peer Count",
"refId": "C"
},
{
"expr": "sum by (instance) (ethereum_peer_count{instance=~\"$system\"} / ethereum_peer_limit{instance=~\"$system\"})",
"format": "table",
"instant": true,
"intervalFactor": 1,
"refId": "H"
}
],
"title": "Overview",
"transform": "table",
"type": "table"
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"fill": 1,
"gridPos": {
"h": 14,
"w": 12,
"x": 0,
"y": 9
},
"id": 12,
"legend": {
"alignAsTable": true,
"avg": true,
"current": true,
"max": true,
"min": true,
"rightSide": false,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"options": {},
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "1/rate(ethereum_blockchain_height{instance=~\"$system\"}[5m])",
"format": "time_series",
"interval": "",
"intervalFactor": 1,
"legendFormat": "{{instance}}",
"refId": "A"
},
{
"expr": "",
"format": "time_series",
"intervalFactor": 1,
"refId": "B"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Block Time",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "s",
"label": null,
"logBase": 10,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"fill": 1,
"gridPos": {
"h": 14,
"w": 12,
"x": 12,
"y": 9
},
"id": 13,
"legend": {
"alignAsTable": true,
"avg": true,
"current": true,
"max": true,
"min": true,
"rightSide": false,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"options": {},
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "ethereum_best_known_block_number{instance=~\"$system\"} - ethereum_blockchain_height{instance=~\"$system\"}",
"format": "time_series",
"interval": "",
"intervalFactor": 1,
"legendFormat": "{{instance}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Blocks Behind",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "locale",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"fill": 1,
"gridPos": {
"h": 12,
"w": 24,
"x": 0,
"y": 23
},
"id": 6,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"options": {},
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "irate(process_cpu_seconds_total{instance=~\"$system\"}[1m])",
"format": "time_series",
"intervalFactor": 1,
"legendFormat": "CPU Time IRate [{{instance}}]",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "CPU",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"datasource": "Prometheus",
"fill": 1,
"gridPos": {
"h": 12,
"w": 12,
"x": 0,
"y": 35
},
"id": 8,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": true,
"total": false,
"values": false
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"options": {},
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "rate(jvm_gc_collection_seconds_sum{instance=~\"$system\"}[1m])",
"format": "time_series",
"interval": "",
"intervalFactor": 5,
"legendFormat": "{{gc}} [{{instance}}]",
"metric": "jvm_gc_collection_seconds_sum",
"refId": "A",
"step": 10
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "GC time",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"decimals": null,
"format": "percentunit",
"label": null,
"logBase": 1,
"max": "1",
"min": "0",
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"aliasColors": {},
"bars": false,
"dashLength": 10,
"dashes": false,
"fill": 1,
"gridPos": {
"h": 12,
"w": 12,
"x": 12,
"y": 35
},
"id": 4,
"legend": {
"alignAsTable": true,
"avg": true,
"current": true,
"max": true,
"min": true,
"show": true,
"total": false,
"values": true
},
"lines": true,
"linewidth": 1,
"links": [],
"nullPointMode": "null",
"options": {},
"percentage": false,
"pointradius": 5,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "jvm_memory_bytes_used{instance=~\"$system\", area=\"heap\"} + ignoring(area) jvm_memory_bytes_used{instance=~\"$system\", area=\"nonheap\"}",
"format": "time_series",
"intervalFactor": 5,
"legendFormat": "{{instance}}",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "Memory Used",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"format": "decbytes",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
},
{
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": true
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
}
],
"refresh": "10s",
"schemaVersion": 18,
"style": "dark",
"tags": [
"besu",
"ethereum"
],
"templating": {
"list": [
{
"allValue": null,
"current": {},
"datasource": "Prometheus",
"definition": "ethereum_blockchain_height",
"hide": 0,
"includeAll": true,
"label": "System",
"multi": true,
"name": "system",
"options": [],
"query": "ethereum_blockchain_height",
"refresh": 2,
"regex": "/instance=\"([^\"]*)\"/",
"skipUrlSync": false,
"sort": 5,
"tagValuesQuery": "",
"tags": [],
"tagsQuery": "",
"type": "query",
"useTags": false
}
]
},
"time": {
"from": "now-12h",
"to": "now"
},
"timepicker": {
"refresh_intervals": [
"5s",
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
],
"time_options": [
"5m",
"15m",
"1h",
"6h",
"12h",
"24h",
"2d",
"7d",
"30d"
]
},
"timezone": "",
"title": "Besu Overview",
"uid": "XE4V0WGZz",
"version": 1
}

View File

@@ -0,0 +1,12 @@
---
apiVersion: 1
providers:
- name: 'Prometheus'
orgId: 1
folder: ''
type: file
disableDeletion: false
editable: true
options:
path: /etc/grafana/provisioning/dashboards

View File

@@ -0,0 +1,284 @@
{
"__inputs": [
{
"name": "loki",
"label": "loki",
"description": "",
"type": "datasource",
"pluginId": "loki",
"pluginName": "Loki"
}
],
"__requires": [
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "7.1.0"
},
{
"type": "panel",
"id": "graph",
"name": "Graph",
"version": ""
},
{
"type": "panel",
"id": "logs",
"name": "Logs",
"version": ""
},
{
"type": "datasource",
"id": "loki",
"name": "Loki",
"version": "1.0.0"
}
],
"annotations": {
"list": [
{
"$$hashKey": "object:75",
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"description": "Quorum Log Viewer for Loki",
"editable": false,
"gnetId": 13639,
"graphTooltip": 0,
"id": null,
"iteration": 1608932746420,
"links": [
{
"$$hashKey": "object:59",
"icon": "bolt",
"includeVars": true,
"keepTime": true,
"tags": [],
"targetBlank": true,
"title": "View In Explore",
"type": "link",
"url": "/explore?orgId=1&left=[\"now-1h\",\"now\",\"Loki\",{\"expr\":\"{job=\\\"$app\\\"}\"},{\"ui\":[true,true,true,\"none\"]}]"
},
{
"$$hashKey": "object:61",
"icon": "external link",
"tags": [],
"targetBlank": true,
"title": "Learn LogQL",
"type": "link",
"url": "https://grafana.com/docs/loki/latest/logql/"
}
],
"panels": [
{
"aliasColors": {},
"bars": true,
"dashLength": 10,
"dashes": false,
"datasource": "Loki",
"fieldConfig": {
"defaults": {
"custom": {},
"links": []
},
"overrides": []
},
"fill": 1,
"fillGradient": 0,
"gridPos": {
"h": 3,
"w": 24,
"x": 0,
"y": 0
},
"hiddenSeries": false,
"id": 6,
"legend": {
"avg": false,
"current": false,
"max": false,
"min": false,
"show": false,
"total": false,
"values": false
},
"lines": false,
"linewidth": 1,
"nullPointMode": "null",
"percentage": false,
"pluginVersion": "7.1.0",
"pointradius": 2,
"points": false,
"renderer": "flot",
"seriesOverrides": [],
"spaceLength": 10,
"stack": false,
"steppedLine": false,
"targets": [
{
"expr": "sum(count_over_time({job=\"$app\"} |= \"$search\" [$__interval]))",
"legendFormat": "",
"refId": "A"
}
],
"thresholds": [],
"timeFrom": null,
"timeRegions": [],
"timeShift": null,
"title": "",
"tooltip": {
"shared": true,
"sort": 0,
"value_type": "individual"
},
"type": "graph",
"xaxis": {
"buckets": null,
"mode": "time",
"name": null,
"show": true,
"values": []
},
"yaxes": [
{
"$$hashKey": "object:168",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": false
},
{
"$$hashKey": "object:169",
"format": "short",
"label": null,
"logBase": 1,
"max": null,
"min": null,
"show": false
}
],
"yaxis": {
"align": false,
"alignLevel": null
}
},
{
"datasource": "Loki",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": 25,
"w": 24,
"x": 0,
"y": 3
},
"id": 2,
"maxDataPoints": "",
"options": {
"showLabels": false,
"showTime": true,
"sortOrder": "Descending",
"wrapLogMessage": false
},
"targets": [
{
"expr": "{job=\"$app\"} |= \"$search\" | logfmt",
"hide": false,
"legendFormat": "",
"refId": "A"
}
],
"timeFrom": null,
"timeShift": null,
"title": "",
"transparent": true,
"type": "logs"
}
],
"refresh": false,
"schemaVersion": 26,
"style": "dark",
"tags": [],
"templating": {
"list": [
{
"allValue": null,
"current": {},
"datasource": "Loki",
"definition": "label_values(job)",
"hide": 0,
"includeAll": false,
"label": "App",
"multi": false,
"name": "app",
"options": [],
"query": "label_values(job)",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 0,
"tagValuesQuery": "",
"tags": [],
"tagsQuery": "",
"type": "query",
"useTags": false
},
{
"current": {
"selected": false,
"text": "",
"value": ""
},
"hide": 0,
"label": "String Match",
"name": "search",
"options": [
{
"selected": true,
"text": "",
"value": ""
}
],
"query": "",
"skipUrlSync": false,
"type": "textbox"
}
]
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {
"hidden": false,
"refresh_intervals": [
"10s",
"30s",
"1m",
"5m",
"15m",
"30m",
"1h",
"2h",
"1d"
]
},
"timezone": "",
"title": "Quorum Logs Loki",
"uid": "Ak6eXLsPxFemKYKEXfcH",
"version": 1
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,54 @@
# config file version
apiVersion: 1
# list of datasources that should be deleted from the database
deleteDatasources:
- name: Graphite
orgId: 1
# list of datasources to insert/update depending
# what's available in the database
datasources:
# <string, required> name of the datasource. Required
- name: Loki
# <string, required> datasource type. Required
type: loki
# <string, required> access mode. proxy or direct (Server or Browser in the UI). Required
access: proxy
# <int> org id. will default to orgId 1 if not specified
orgId: 1
# <string> url
url: http://loki:3100/
# <string> Deprecated, use secureJsonData.password
password:
# <string> database user, if used
user:
# <string> database name, if used
database:
# <bool> enable/disable basic auth
basicAuth:
# <string> basic auth username
basicAuthUser:
# <string> Deprecated, use secureJsonData.basicAuthPassword
basicAuthPassword:
# <bool> enable/disable with credentials headers
withCredentials:
# <bool> mark as default datasource. Max one per org
isDefault: false
# <map> fields that will be converted to json and stored in jsonData
jsonData:
graphiteVersion: "1.1"
tlsAuth: false
tlsAuthWithCACert: false
# <string> json object of data that will be encrypted.
secureJsonData:
tlsCACert: "..."
tlsClientCert: "..."
tlsClientKey: "..."
# <string> database password, if used
password:
# <string> basic auth password
basicAuthPassword:
version: 1
# <bool> allow users to edit datasources from the UI.
editable: true

View File

@@ -0,0 +1,54 @@
# config file version
apiVersion: 1
# list of datasources that should be deleted from the database
deleteDatasources:
- name: Graphite
orgId: 1
# list of datasources to insert/update depending
# what's available in the database
datasources:
# <string, required> name of the datasource. Required
- name: Prometheus
# <string, required> datasource type. Required
type: prometheus
# <string, required> access mode. proxy or direct (Server or Browser in the UI). Required
access: proxy
# <int> org id. will default to orgId 1 if not specified
orgId: 1
# <string> url
url: http://prometheus:9090/
# <string> Deprecated, use secureJsonData.password
password:
# <string> database user, if used
user:
# <string> database name, if used
database:
# <bool> enable/disable basic auth
basicAuth:
# <string> basic auth username
basicAuthUser:
# <string> Deprecated, use secureJsonData.basicAuthPassword
basicAuthPassword:
# <bool> enable/disable with credentials headers
withCredentials:
# <bool> mark as default datasource. Max one per org
isDefault: true
# <map> fields that will be converted to json and stored in jsonData
jsonData:
graphiteVersion: "1.1"
tlsAuth: false
tlsAuthWithCACert: false
# <string> json object of data that will be encrypted.
secureJsonData:
tlsCACert: "..."
tlsClientCert: "..."
tlsClientKey: "..."
# <string> database password, if used
password:
# <string> basic auth password
basicAuthPassword:
version: 1
# <bool> allow users to edit datasources from the UI.
editable: true

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"address":"f0e2db6c8dc6c681bb5d6ad121a107f300e9b2b5","crypto":{"cipher":"aes-128-ctr","ciphertext":"f2af258ee3733513333652be19197ae7eace4b5e79a346cf25b02a857e6043f3","cipherparams":{"iv":"587d7faaa6403b8a73273d0ad58dd71f"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"b93c7d69c5bb0a760c3b7fdf791c47896a552c5c977648b392a24d708674dcf3"},"mac":"d83bcb555c92fc5a32ceacabbb6b99f59515ec3986b9fe5995c67e027bd750c8"},"id":"5392d73f-08dd-42b8-bca9-6f6d35c419d9","version":3}

View File

View File

@@ -0,0 +1 @@
0x8bbbb1b345af56b560a5b20bd4b0ed1cd8cc9958a16262bc75118453cb546df7

View File

@@ -0,0 +1 @@
0x13a52aab892e1322e8b52506276363d4754c122e

View File

@@ -0,0 +1 @@
b9a4bd1539c15bcc83fa9078fe89200b6e9e802ae992f13cd83c853f16e8bed4

View File

@@ -0,0 +1 @@
98496800174b3c73ae33cba59f8f5e686cd488f7897c2edb52e2cf46383d75cd03dbb58dde07185bc0953f98800ca9a89f4b5ef450c5e51292ea08ec6130ee0c

View File

@@ -0,0 +1 @@
{"data":{"bytes":"Wl+xSyXVuuqzpvznOS7dOobhcn4C5auxkFRi7yLtgtA="},"type":"unlocked"}

View File

@@ -0,0 +1 @@
BULeR8JyUWhiuuCMU/HLA0Q5pzkYT+cHII3ZKBey3Bo=

View File

@@ -0,0 +1 @@
{"data":{"bytes":"wGEar7J9G0JAgdisp61ZChyrJWeW2QPyKvecjjeVHOY="},"type":"unlocked"}

View File

@@ -0,0 +1 @@
8SjRHlUBe4hAmTk3KDeJ96RhN+s10xRrHDrxEi1O5W0=

View File

@@ -0,0 +1 @@
{"address":"ca843569e3427144cead5e4d5999a3d0ccf92b8e","crypto":{"cipher":"aes-128-ctr","ciphertext":"01d409941ce57b83a18597058033657182ffb10ae15d7d0906b8a8c04c8d1e3a","cipherparams":{"iv":"0bfb6eadbe0ab7ffaac7e1be285fb4e5"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"7b90f455a95942c7c682e0ef080afc2b494ef71e749ba5b384700ecbe6f4a1bf"},"mac":"4cc851f9349972f851d03d75a96383a37557f7c0055763c673e922de55e9e307"},"id":"354e3b35-1fed-407d-a358-889a29111211","version":3}

View File

View File

@@ -0,0 +1 @@
0x4762e04d10832808a0aebdaa79c12de54afbe006bfffd228b3abcc494fe986f9

View File

@@ -0,0 +1 @@
0xe090a28b8a9d0a69ec259cb745036d5d1030e3ea

View File

@@ -0,0 +1 @@
f18166704e19b895c1e2698ebc82b4e007e6d2933f4b31be23662dd0ec602570

View File

@@ -0,0 +1 @@
ad2c79c6561bc8212c2e8382611c62e406e767d1f3106c68ca206900f575cb8ba9a8be111c645cd9803701d684454c782c40d2361b000a32ed03d26228b30ec1

View File

@@ -0,0 +1 @@
{"data":{"bytes":"nDFwJNHSiT1gNzKBy9WJvMhmYRkW3TzFUmPsNzR6oFk="},"type":"unlocked"}

View File

@@ -0,0 +1 @@
QfeDAys9MPDs2XHExtc84jKGHxZg/aj52DTh0vtA3Xc=

View File

@@ -0,0 +1 @@
{"data":{"bytes":"rwfJC1kNa8BjPfc+zZXug+it9sdWa0vbdN6pp6IXlAs="},"type":"unlocked"}

View File

@@ -0,0 +1 @@
2T7xkjblN568N1QmPeElTjoeoNT4tkWYOJYxSMDO5i0=

View File

@@ -0,0 +1 @@
{"address":"0fbdc686b912d7722dc86510934589e0aaf3b55a","crypto":{"cipher":"aes-128-ctr","ciphertext":"6b2c72c6793f3da8185e36536e02f574805e41c18f551f24b58346ef4ecf3640","cipherparams":{"iv":"582f27a739f39580410faa108d5cc59f"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"1a79b0db3f8cb5c2ae4fa6ccb2b5917ce446bd5e42c8d61faeee512b97b4ad4a"},"mac":"cecb44d2797d6946805d5d744ff803805477195fab1d2209eddc3d1158f2e403"},"id":"f7292e90-af71-49af-a5b3-40e8493f4681","version":3}

View File

View File

@@ -0,0 +1 @@
0x61dced5af778942996880120b303fc11ee28cc8e5036d2fdff619b5675ded3f0

View File

@@ -0,0 +1 @@
0x401bdfcd9221cd790e7cd66eece303ed4d4b53b1

View File

@@ -0,0 +1 @@
4107f0b6bf67a3bc679a15fe36f640415cf4da6a4820affaac89c8b280dfd1b3

View File

@@ -0,0 +1 @@
af19c92deb635bd7720634de9b2e7908208530d6f5e96eee003a8f1799e5be4037957d7e2fdbe3605e3a38dab05c961679c02133a0e624e23a72f7961e8af6ac

View File

@@ -0,0 +1 @@
{"data":{"bytes":"tMxUVR8bX7aq/TbpVHc2QV3SN2iUuExBwefAuFsO0Lg="},"type":"unlocked"}

View File

@@ -0,0 +1 @@
1iTZde/ndBHvzhcl7V68x44Vx7pl8nwx9LqnM/AfJUg=

View File

@@ -0,0 +1 @@
{"data":{"bytes":"yLcbICXicELZOnvpkDXB2UkQUiNAMIfsEOsgtFOGkQU="},"type":"unlocked"}

View File

@@ -0,0 +1 @@
3nLS1GSlPs3/AccoZ20WTBrYP/ua5KDlUM1uGrDKHTs=

View File

@@ -0,0 +1 @@
{"address":"c9c913c8c3c1cd416d80a0abf475db2062f161f6","crypto":{"cipher":"aes-128-ctr","ciphertext":"ce8e51e64e9f053414616c62b3924536f3f9144638359e2e2eb00e04ad095b0a","cipherparams":{"iv":"648343bce02b158eda9bb2074f8dc601"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"0982402da973d8176315aeae4415b328a7ce4393e217a0c4f45385f1cee9bc3e"},"mac":"5707caff457f930655849293dc1101e13ab32b175fdf5538869a2a3ffc716d8b"},"id":"8d031b90-ab7f-48dc-9b7a-ce41abd7f9c8","version":3}

View File

View File

@@ -0,0 +1 @@
0x60bbe10a196a4e71451c0f6e9ec9beab454c2a5ac0542aa5b8b733ff5719fec3

View File

@@ -0,0 +1 @@
0xdf8b560be949c229c821731554c33ead5e3888a4

View File

@@ -0,0 +1 @@
0e93a540518eeb673d94fb496b746008ab56605463cb9212493997f5755124d1

View File

@@ -0,0 +1 @@
86fcc16f4730fbfd238dc17ea552854c0943923bb1d5e886e5601b8d884fb0519060e0023f495dd24ffe60a65660fb7fdcdebfceedd2b3673dfa63658825924b

View File

@@ -0,0 +1 @@
{"address":"ed9d02e382b34818e88b88a309c7fe71e65f419d","crypto":{"cipher":"aes-128-ctr","ciphertext":"4e77046ba3f699e744acb4a89c36a3ea1158a1bd90a076d36675f4c883864377","cipherparams":{"iv":"a8932af2a3c0225ee8e872bc0e462c11"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"8ca49552b3e92f79c51f2cd3d38dfc723412c212e702bd337a3724e8937aff0f"},"mac":"6d1354fef5aa0418389b1a5d1f5ee0050d7273292a1171c51fd02f9ecff55264"},"id":"a65d1ac3-db7e-445d-a1cc-b6c5eeaa05e0","version":3}

View File

View File

@@ -0,0 +1 @@
0xe6181caaffff94a09d7e332fc8da9884d99902c7874eb74354bdcadf411929f1

View File

@@ -0,0 +1 @@
0x93917cadbace5dfce132b991732c6cda9bcc5b8a

View File

@@ -0,0 +1 @@
1a2c4ff0f1b38e2322658dba692816138eb22d002515df1fffca21278f406aa9

View File

@@ -0,0 +1 @@
8208a3f344695d44e9cf2c023683cbea7b9343e2f70a5e804bd2c93858e945f8f91439eef96a4ab6c47ff06637d6fbe6472f96de1655a1bee57ea896654f3a22

View File

@@ -0,0 +1 @@
{"address":"b30f304642de3fee4365ed5cd06ea2e69d3fd0ca","crypto":{"cipher":"aes-128-ctr","ciphertext":"cf7f44a86510c497f2a6727f62b8e89c8cd42dbfd8e3377658d659c776c75f30","cipherparams":{"iv":"5d320e3072939817faa2caefdb810239"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"e637b069dacfe77f3a495d8a0d3735544e73973c03e107976545df78e6f04289"},"mac":"e20c97b8ee7d54072b9cfac201d52ef360ecacdbd1f7187e7547b3cd9be00599"},"id":"5bf73d6b-340b-4e9f-a7f3-f9ab41f39726","version":3}

View File

View File

@@ -0,0 +1 @@
0x5ad8b28507578c429dfa9f178d7f742f4861716ee956eb75648a7dbc5ffe915d

View File

@@ -0,0 +1 @@
0x27a97c9aaf04f18f3014c32e036dd0ac76da5f18

View File

@@ -0,0 +1 @@
7f9af699dd2bb1af76c90b3f67183dd48abae509c315eb8f2c55301ad90ba978

View File

@@ -0,0 +1 @@
b9050e002aa42464e6b07c811a1f9dfec01249af03f67b753e8415420649b184447bb2a784863ccbf327ad9e31aaba803464979dfe6a7facc669151a5fa6ad1b

View File

@@ -0,0 +1 @@
{"address":"0886328869e4e1f401e1052a5f4aae8b45f42610","crypto":{"cipher":"aes-128-ctr","ciphertext":"6e0e228b810a88f2cea85c439b005a92bba6220de8cd6ba73a8f7ecd681fde09","cipherparams":{"iv":"5264c2defa48f7f9fa103899acaea021"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"15ef956dab453387ddf7afd6775f3727fb560a77028b5a14d774c3bfff17f101"},"mac":"6ca1d88defee18378789d191ca7b32bbecbd6dd5a6c39427ed45b13c9595edc3"},"id":"734d1189-4e1e-44bf-854a-642485532715","version":3}

View File

View File

@@ -0,0 +1 @@
0xf23f92ed543046498d7616807b18a8f304855cb644df25bc7d0b0b37d8a66019

View File

@@ -0,0 +1 @@
0xce412f988377e31f4d0ff12d74df73b51c42d0ca

View File

@@ -0,0 +1 @@
fe006b00c738e7e5af7f7623290ffc83f394741ae6fb6afc6081cab49e1e1a70

View File

@@ -0,0 +1 @@
59cf0c623c582fa9b19bdf70fb6bade07f4ae32218dd4d1c7e2c7e65acf87da45cf2ab55d16d27360aafef17622c37c09db60d7680ebcc17b78867f4c05bcaa4

View File

@@ -0,0 +1 @@
{"address":"f48de4a0c2939e62891f3c6aca68982975477e45","crypto":{"cipher":"aes-128-ctr","ciphertext":"402a2d8eb1ff6bab713ddb81f68142c4f0113d32e9f0cc8969347a4945cd1e5f","cipherparams":{"iv":"a1f708815212854c0c49081869198dd5"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"a24d95de8680d8d214ae5e9060e919eb563ce513bf63021497e0d218ed68f89d"},"mac":"571164da79472f210caeb45af2b9715fb22eef8d485ce5c6a4e0c52865398ea7"},"id":"d5723032-0a81-46ec-ac34-9d8362e2250c","version":3}

View File

View File

@@ -0,0 +1 @@
0x7f012b2a11fc651c9a73ac13f0a298d89186c23c2c9a0e83206ad6e274ba3fc7

View File

@@ -0,0 +1 @@
0x98c1334496614aed49d2e81526d089f7264fed9c

View File

@@ -0,0 +1 @@
8f6ae009cdbbf6e6fa739b91a4483f251bbe89f6570d34856554533b36c93c55

View File

@@ -0,0 +1 @@
2fd5b5b6ad529f55b71602026d1849d0036f06482368b5812fa793014195d3571b0840dbc4175617de2a12db8f1222c012420d471ae5c0d982118625cae58868

View File

@@ -0,0 +1,136 @@
# my global config
global:
scrape_interval: 15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
# scrape_timeout is set to the global default (10s).
# Alertmanager configuration
alerting:
# alertmanagers:
# - static_configs:
# - targets:
# - alertmanager:9093
# Load rules once and periodically evaluate them according to the global 'evaluation_interval'.
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
# A scrape configuration containing exactly one endpoint to scrape:
# Here it's Prometheus itself.
scrape_configs:
- job_name: validator1
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets: [ validator1:9545 ]
- job_name: validator2
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets: [ validator2:9545 ]
- job_name: validator3
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets: [ validator3:9545 ]
- job_name: validator4
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets: [ validator4:9545 ]
- job_name: rpcnode
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets: [ rpcnode:9545 ]
- job_name: newnode
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets: [ newnode:9545 ]
- job_name: member1besu
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets: [ member1besu:9545 ]
- job_name: member2besu
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets: [ member2besu:9545 ]
- job_name: member3besu
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets: [ member3besu:9545 ]
- job_name: member1tessera
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets: [ member1tessera:9545 ]
- job_name: member2tessera
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets: [ member2tessera:9545 ]
- job_name: member3tessera
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
static_configs:
- targets: [ member3tessera:9545 ]
# all the nodes override themselves if given the same name
# so we use dns based lookup and, replace labels to
# alter the address from 192.168.x.y:9545 to node:192.168.x.y:9545 so they are easier to see in grafana
- job_name: node
scrape_interval: 15s
scrape_timeout: 10s
metrics_path: /metrics
scheme: http
dns_sd_configs:
- names:
- node
type: 'A'
port: 9545
relabel_configs:
- source_labels: [__address__]
regex: '(.*)'
target_label: instance
replacement: "node:$1"

15
config/tessera/Dockerfile Normal file
View File

@@ -0,0 +1,15 @@
ARG TESSERA_VERSION=latest
FROM quorumengineering/tessera:${TESSERA_VERSION}
# develop uses a debain container, all releases use an alpine container - this allows both to be used for the quickstart
# set the version in ../../.env
RUN if [ -e /sbin/apk ] ; then apk add gettext --no-cache ; else apt-get update && apt-get install -y gettext && rm -rf /var/lib/apt/lists/* ; fi
ENV JAVA_OPTS="-Dlogback.configurationFile=/data/logback.xml"
COPY docker-entrypoint.sh /usr/bin/
COPY data data
ENTRYPOINT ["docker-entrypoint.sh"]

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="defaultLogLevel" value="INFO" />
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>{"timestamp":"%d{ISO8601}","container":"${hostName}","level":"%level","thread":"%thread","logger":"%logger{36}","message":"%replace(%replace(%msg){'[\r\n]',''}){'"','\\"'}","throwable":"%replace(%replace(%throwable){'[\r\n]',''}){'"','\\"'}"}%n</pattern>
</encoder>
</appender>
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>/var/log/tessera/tessera-${HOSTNAME}.log</file>
<append>true</append>
<immediateFlush>true</immediateFlush>
<encoder>
<pattern>{"timestamp":"%d{ISO8601}","container":"${hostName}","level":"%level","thread":"%thread","class":"%c{1}","logger":"%logger{36}","message":"%replace(%msg){'[\r\n]', ''}","throwable":"%replace(%replace(%throwable){'[\r\n]',''}){'"','\\"'}"}%n</pattern>
</encoder>
</appender>
<!-- silence hibernate messages that were being created from DefaultCliAdapter -->
<logger name="org.hibernate.validator.internal.util.Version" level="OFF"/>
<logger name="org.hibernate.validator.internal.engine.ConfigurationImpl" level="OFF"/>
<!-- silence noisy logging from regular /partyinfo messages -->
<logger name="com.quorum.tessera.p2p.PartyInfoResource" level="WARN"/>
<root level="${verbosity:-${defaultLogLevel}}">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</root>
</configuration>

View File

@@ -0,0 +1,62 @@
{
"mode": "${TESSERA_MODE}",
"useWhiteList": false,
"jdbc": {
"username": "sa",
"password": "",
"url": "jdbc:h2:./data/tm/db;MODE=Oracle;TRACE_LEVEL_SYSTEM_OUT=0",
"autoCreateTables": true
},
"serverConfigs": [
{
"app": "ThirdParty",
"enabled": true,
"serverAddress": "http://${HOSTNAME}:9080",
"communicationType": "REST"
},
{
"app": "Q2T",
"enabled": true,
"serverAddress": "http://${HOSTNAME}:9101",
"sslConfig": {
"tls": "OFF"
},
"communicationType": "REST"
},
{
"app": "P2P",
"enabled": true,
"serverAddress": "http://${HOSTNAME}:9000",
"sslConfig": {
"tls": "OFF"
},
"communicationType": "REST"
}
],
"peer": [
{
"url": "http://member1tessera:9000"
},
{
"url": "http://member2tessera:9000"
},
{
"url": "http://member3tessera:9000"
}
],
"keys": {
"passwords": [],
"keyData": [
{
"privateKeyPath": "/config/keys/tm.key",
"publicKeyPath": "/config/keys/tm.pub"
}
]
},
"alwaysSendTo": [],
"bootstrapNode": false,
"features": {
"enableRemoteKeyValidation": false,
"enablePrivacyEnhancements": true
}
}

View File

@@ -0,0 +1,12 @@
#!/bin/sh
mkdir -p /var/log/tessera/;
mkdir -p /data/tm/;
envsubst < /data/tessera-config-template.json > /data/tessera-config.json
cat /data/tessera-config.json
exec /tessera/bin/tessera \
-configfile /data/tessera-config.json

View File

@@ -0,0 +1,51 @@
# Docker Desktop DNS Configuration
Since you're using Docker Desktop (default context), you may need to configure DNS in Docker Desktop settings.
## Steps to Configure DNS in Docker Desktop:
1. **Open Docker Desktop**
- Click the Docker Desktop icon in your system tray or start menu
2. **Go to Settings**
- Click the gear icon (⚙️) in the top right
- Or click "Settings" from the Docker Desktop menu
3. **Navigate to Docker Engine**
- In the left sidebar, click "Docker Engine"
4. **Add DNS Configuration**
- In the JSON configuration editor, add or update the `dns` field:
```json
{
"dns": ["8.8.8.8", "8.8.4.4", "1.1.1.1"],
"experimental": false,
"features": {
"buildkit": true
}
}
```
- If there's already content, just add the `"dns"` line to the existing JSON
5. **Apply Changes**
- Click "Apply & Restart" button
- Wait for Docker Desktop to restart
## Alternative: Test Without Docker Desktop Configuration
The WSL2 DNS fix we applied should be sufficient. Try running the network first:
```bash
./run.sh
```
If you still get DNS errors, then configure Docker Desktop DNS as described above.
## Verify Configuration
After configuring, test with:
```bash
docker pull alpine:latest
```
If this works, your Docker DNS is properly configured.

View File

@@ -0,0 +1,74 @@
# Using a DApp to interact with the blockchain
This DApp, uses Hardhat and Ethers.js in combination with a self custodial (also called a user controlled) wallet i.e. Metamask to interact with the chain. As such this process esentially comprises two parts:
1. Deploy the contract to the chain
2. Use the DApp's interface to send and transact on the chain
The `dapps/quorumToken` folder is this structured in this manner (only relevant paths shown):
```
quorumToken
├── hardhat.config.ts // hardhat network config
├── contracts // sample contracts of which we use the QuorumToken.sol
├── scripts // handy scripts eg: to deploy to a chain
├── test // contract tests
└── frontend // DApp done in next.js
├── README.md
├── public
├── src
├── styles
├── tsconfig.json
```
# Contracts
Contracts are written in Solidity and we use the hardhat development environment for testing, deploying etc
The `hardhat.config.js` specifies the networks, accounts, solidity version etc
Install dependencies
```
npm i
```
Compile the contracts and run tests (optional):
```
npx run compile
# As you develop contracts you are using the inbuilt `hardhat` network
npx hardhat test
```
Deploy contracts with:
```
# we specify the network here so the DApp can use the contract, but you can use any network you wish to and remember to connect Metamask to the appropriate network for the DApp
npx hardhat run ./scripts/deploy_quorumtoken.ts --network quickstart
```
_Please remember to save the address returned from the deploy as you will need it for the following steps_
# DApp
We have a sample DApp created that uses Next.js, react and ethers to interact with the quickstart network
```
cd frontend
npm i
npm run dev
```
1. Open up a tab on port 3001 and connect to Metamask.
2. To interact with the DApp you will need to import the test accounts from `hardhat.config.ts`
For brevity they are the following:
```
0x8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63
0xc87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3
0xae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f
```
3. When you connect to Metamask, you are presented with a field to input the address of the deployed contract from the previous step. The app will then fetch the contract data and you can then transfer eth to a new another account.

View File

@@ -0,0 +1,12 @@
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract QuorumToken is ERC20 {
constructor(uint256 initialSupply)
ERC20("QuorumToken", "QT")
{
_mint(msg.sender, initialSupply);
}
}

View File

@@ -0,0 +1,3 @@
{
"extends": "next/core-web-vitals"
}

View File

@@ -0,0 +1,6 @@
/** @type {import('next').NextConfig} */
const nextConfig = {
reactStrictMode: true,
}
module.exports = nextConfig

Some files were not shown because too many files have changed in this diff Show More