diff --git a/infrastructure/charts/deploy-contracts.py b/infrastructure/charts/deploy-contracts.py new file mode 100644 index 000000000..1f1596078 --- /dev/null +++ b/infrastructure/charts/deploy-contracts.py @@ -0,0 +1,156 @@ +#!/usr/bin/env python3 +# deploy_contracts.py +# +# Flow: +# 1. download keystores, verify, print Oracle & Sender addresses +# 2. git clone mev-commit +# 3. forge install (at repo root) +# 4. forge clean / build (in contracts/) +# 5. port-forward & deploy contracts, parse logs โ†’ JSONs + +import os, re, sys, json, subprocess, signal, time, socket, argparse +from datetime import datetime +from pathlib import Path +from tempfile import NamedTemporaryFile + +# ---------- Defaults ---------- +REPO_URL = "https://github.com/primev/mev-commit.git" +REPO_BRANCH = "main" + +ORACLE_KEYSTORE_URL = "https://storage.googleapis.com/devnet-artifacts/keystores/erigon-keystores/keystore2/UTC--2025-06-24T18-20-44.554617000Z--1c533735c11dd317bc816629f86e00f479d097a3" +SENDER_KEYSTORE_URL = "https://storage.googleapis.com/devnet-artifacts/keystores/erigon-keystores/keystore1/UTC--2025-06-24T18-20-43.890647000Z--421657a89f467ac04c542e46645a7752e199b5e6" + +KUBE_POD = "erigon-snode-0" +RPC_HOST = "127.0.0.1" +RPC_PORT = 8545 +RPC_URL = f"http://{RPC_HOST}:{RPC_PORT}" +KUBECTL_BIN = "kubectl" + +CHAIN_ID = "141414" +PRIORITY_GAS_PRICE = "2000000000" +GAS_PRICE = "5000000000" +SOLC = "0.8.26" + +CORE_KEYS = [ + "BidderRegistry", "BlockTracker", "Oracle", + "PreconfManager", "ProviderRegistry", "ValidatorOptInRouter" +] + +# ---------- CLI ---------- +ap = argparse.ArgumentParser() +ap.add_argument("--password", "-p", required=True, help="Password for keystores") +ap.add_argument("--work-dir", default=f"/tmp/mev-commit-{datetime.now().strftime('%Y%m%d_%H%M%S')}") +args = ap.parse_args() + +WORK_DIR = Path(args.work_dir).resolve() +WORK_DIR.mkdir(parents=True, exist_ok=True) + +# ---------- Helpers ---------- +def run(cmd, cwd=None, log_file=None): + if log_file: + with open(log_file, "ab") as lf: + lf.write(("\n$ " + " ".join(cmd) + "\n").encode()) + return subprocess.run(cmd, cwd=cwd, stdout=lf, stderr=subprocess.STDOUT).returncode + return subprocess.run(cmd, cwd=cwd).returncode + +def wait_for_port(host, port, timeout=25): + deadline = time.time() + timeout + while time.time() < deadline: + try: + with socket.create_connection((host, port), timeout=1): + return True + except Exception: + time.sleep(0.5) + return False + +def download_keystore(url: str, dest_dir: Path) -> Path: + fname = url.split("/")[-1] + fpath = dest_dir / fname + if not fpath.exists(): + subprocess.check_call(["wget", "-q", "-O", str(fpath), url]) + return fpath + +def read_keystore_address(fpath: Path) -> str: + data = json.loads(fpath.read_text()) + return "0x" + data["address"] + +# ---------- Step 1. Keystores ---------- +ks_dir = WORK_DIR / "keystores" +ks_dir.mkdir(parents=True, exist_ok=True) + +oracle_keystore = download_keystore(ORACLE_KEYSTORE_URL, ks_dir) +oracle_addr = read_keystore_address(oracle_keystore) +os.environ["ORACLE_KEYSTORE_ADDRESS"] = oracle_addr + +sender_keystore = download_keystore(SENDER_KEYSTORE_URL, ks_dir) +sender_addr = read_keystore_address(sender_keystore) + +print(f"๐Ÿ”‘ Oracle address: {oracle_addr}") +print(f"๐Ÿ‘ค Sender address: {sender_addr}") + +# ---------- Step 2. Clone repo ---------- +repo_dir = WORK_DIR / "mev-commit" +print(f"๐Ÿ”น Cloning {REPO_URL}@{REPO_BRANCH} โ†’ {repo_dir}") +subprocess.check_call(["git", "clone", "--branch", REPO_BRANCH, "--single-branch", REPO_URL, str(repo_dir)]) +contracts_dir = repo_dir / "contracts" + +log_dir = contracts_dir / "deploy-logs" +out_dir = contracts_dir / "deploy-summaries" +for d in (log_dir, out_dir): d.mkdir(parents=True, exist_ok=True) + +ts = datetime.now().strftime("%Y%m%d_%H%M%S") +log_path = log_dir / f"deploy_core_{ts}.log" +full_json_path = out_dir / f"contracts_{ts}.json" +core_json_path = out_dir / "core-contracts.json" + +# ---------- Step 3. Clean/build (contracts/) ---------- +print("๐Ÿ”น forge clean && forge build โ€ฆ") +if run(["forge", "clean"], cwd=contracts_dir, log_file=log_path) != 0: sys.exit(1) +if run(["forge", "build"], cwd=contracts_dir, log_file=log_path) != 0: sys.exit(1) + +# ---------- Step 4. Port-forward + Deploy ---------- +print(f"๐Ÿ”น Port-forward {KUBE_POD} โ†’ localhost:{RPC_PORT}") +pf = subprocess.Popen([KUBECTL_BIN, "port-forward", f"pod/{KUBE_POD}", f"{RPC_PORT}:{RPC_PORT}"], + preexec_fn=os.setsid) +import atexit; atexit.register(lambda: os.killpg(os.getpgid(pf.pid), signal.SIGTERM)) +if not wait_for_port(RPC_HOST, RPC_PORT): sys.exit("RPC not up") + +print("๐Ÿ”น Deploying โ€ฆ") +deploy_cmd = [ + "forge", "script", "scripts/core/DeployCore.s.sol:DeployCore", + "--priority-gas-price", PRIORITY_GAS_PRICE, + "--with-gas-price", GAS_PRICE, + "--chain-id", CHAIN_ID, + "--rpc-url", RPC_URL, + "--keystores", str(sender_keystore), + "--password", args.password, + "--sender", sender_addr, + "--use", SOLC, + "--broadcast", "--json", "--via-ir", +] +rc = run(deploy_cmd, cwd=contracts_dir, log_file=log_path) + +# ---------- Parse logs ---------- +text = log_path.read_text(errors="ignore") +pairs = {} +for name in CORE_KEYS: + m = re.search(re.escape(name)+r"[^0-9a-fA-F]{0,200}(0x[0-9a-fA-F]{40})", text, re.I|re.S|re.M) + if m: pairs[name] = m.group(1) +for m in re.finditer(r"([A-Za-z][A-Za-z0-9:_ ]{1,64})[^0-9a-fA-F]{0,40}(0x[0-9a-fA-F]{40})", text): + name = re.sub(r"[:\s]+$", "", m.group(1).strip()) + if len(name) >= 2: pairs.setdefault(name, m.group(2)) + +with full_json_path.open("w") as jf: json.dump(pairs, jf, indent=2, sort_keys=True) +core_map = {k: pairs[k] for k in CORE_KEYS if k in pairs} +with NamedTemporaryFile("w", delete=False, dir=str(core_json_path.parent), prefix=".tmp_", suffix=".json") as tmp: + json.dump(core_map, tmp, indent=2); tmp.flush(); os.fsync(tmp.fileno()) + os.replace(tmp.name, core_json_path) + +# ---------- Result ---------- +if rc == 0: print("โœ… Success.") +else: sys.exit(f"โŒ Deploy failed (exit {rc}). See log: {log_path}") + +print(f"๐Ÿ“„ Log: {log_path}") +print(f"๐Ÿงพ Full JSON: {full_json_path}") +print(f"๐ŸŽฏ Core JSON: {core_json_path}") + diff --git a/infrastructure/charts/deploy.sh b/infrastructure/charts/deploy.sh new file mode 100644 index 000000000..dbb2e327e --- /dev/null +++ b/infrastructure/charts/deploy.sh @@ -0,0 +1,795 @@ +#!/bin/bash + +set -e + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +print_info() { echo -e "${BLUE}โ„น๏ธ $1${NC}"; } +print_success() { echo -e "${GREEN}โœ… $1${NC}"; } +print_warning() { echo -e "${YELLOW}โš ๏ธ $1${NC}"; } +print_error() { echo -e "${RED}โŒ $1${NC}"; } + +# Defaults +NAMESPACE="default" +TIMEOUT="600" +RPC_PORT="8545" +DEPLOY_SCRIPT="deploy-contracts.py" +DRY_RUN=false +CLEANUP=false +SKIP_CONTRACTS=false +PASSWORD="" +WORK_DIR="" +SELECTED_CHARTS="" +BRANCH="" +CONTRACTS_FILE="" + +# Image variables +DASHBOARD_IMAGE="" +P2P_IMAGE="" +RELAY_IMAGE="" +BIDDER_EMULATOR_IMAGE="" +PROVIDER_EMULATOR_IMAGE="" +ORACLE_IMAGE="" + +# Parse args +while [[ $# -gt 0 ]]; do + case $1 in + --dry-run) DRY_RUN=true; shift ;; + --cleanup) CLEANUP=true; shift ;; + --skip-contracts) SKIP_CONTRACTS=true; shift ;; + --namespace) NAMESPACE="$2"; shift 2 ;; + --password) PASSWORD="$2"; shift 2 ;; + --work-dir) WORK_DIR="$2"; shift 2 ;; + --charts) SELECTED_CHARTS="$2"; shift 2 ;; + --branch) BRANCH="$2"; shift 2 ;; + --contracts-file) CONTRACTS_FILE="$2"; shift 2 ;; + --help) + echo "Usage: $0 [--dry-run] [--cleanup] [--skip-contracts] [--password PASS] [--charts CHARTS] [--branch BRANCH] [--contracts-file FILE]" + echo "Charts: mock-l1,erigon-snode,relay-emulator,dashboard,oracle,bootnode,bidder,bidder-emulator,provider,provider-emulator" + echo "Branch: Filter Docker images by branch label (required for image discovery)" + echo "Contracts File: JSON file with contract addresses (used with --skip-contracts)" + exit 0 ;; + *) print_error "Unknown option: $1"; exit 1 ;; + esac +done + +# Cleanup function +cleanup_all() { + print_info "Cleaning up all releases..." + + for release in "mev-commit-dashboard" "mev-commit-relay-emulator" "erigon-snode" "erigon-mev-commit-mock-l1" "erigon-oracle" "erigon-mev-commit-bootnode" "erigon-mev-commit-bidder" "mev-commit-emulator-bt" "erigon-mev-commit-provider" "mev-commit-emulator"; do + if helm list -n $NAMESPACE | grep -q "^$release"; then + print_info "Deleting $release" + helm uninstall "$release" -n "$NAMESPACE" || true + fi + done + + sleep 5 + print_info "Deleting PVCs..." + kubectl get pvc -n "$NAMESPACE" --no-headers 2>/dev/null | grep -E "(erigon|geth|mock)" | awk '{print $1}' | xargs -r kubectl delete pvc -n "$NAMESPACE" || true + + print_success "Cleanup completed!" +} + +if [[ "$CLEANUP" == true ]]; then + cleanup_all + exit 0 +fi + +# Discover Docker images by branch and component +discover_images() { + if [[ -z "$BRANCH" ]]; then + print_warning "No branch specified, using hardcoded image values" + return 0 + fi + + print_info "Discovering Docker images for branch: $BRANCH" + + # Get all images filtered by branch + BRANCH_IMAGES=$(docker images --filter "label=branch=$BRANCH" --format "table {{.Repository}}:{{.Tag}}\t{{.ID}}" 2>/dev/null || true) + + if [[ -z "$BRANCH_IMAGES" ]]; then + print_warning "No Docker images found with branch label: $BRANCH" + print_warning "Using hardcoded image values from charts" + return 0 + fi + + print_info "Found images for branch $BRANCH:" + echo "$BRANCH_IMAGES" + echo + + # Extract images by component label + print_info "Matching images by component labels..." + + # Dashboard image (component=dashboard) + DASHBOARD_IMAGE=$(docker images --filter "label=branch=$BRANCH" --filter "label=component=dashboard" --format "{{.Repository}}:{{.Tag}}" 2>/dev/null | head -1 || true) + + # P2P image (component=p2p) - used by bootnode, bidder, provider + P2P_IMAGE=$(docker images --filter "label=branch=$BRANCH" --filter "label=component=p2p" --format "{{.Repository}}:{{.Tag}}" 2>/dev/null | head -1 || true) + + # Relay emulator image (component=relay-emulator) + RELAY_IMAGE=$(docker images --filter "label=branch=$BRANCH" --filter "label=component=relay-emulator" --format "{{.Repository}}:{{.Tag}}" 2>/dev/null | head -1 || true) + + # Bidder emulator image (component=bidder-emulator) + BIDDER_EMULATOR_IMAGE=$(docker images --filter "label=branch=$BRANCH" --filter "label=component=bidder-emulator" --format "{{.Repository}}:{{.Tag}}" 2>/dev/null | head -1 || true) + + # Provider emulator image (component=provider-emulator) + PROVIDER_EMULATOR_IMAGE=$(docker images --filter "label=branch=$BRANCH" --filter "label=component=provider-emulator" --format "{{.Repository}}:{{.Tag}}" 2>/dev/null | head -1 || true) + + # Oracle image (component=oracle) + ORACLE_IMAGE=$(docker images --filter "label=branch=$BRANCH" --filter "label=component=oracle" --format "{{.Repository}}:{{.Tag}}" 2>/dev/null | head -1 || true) + + print_info "=== Image Discovery Results ===" + [[ -n "$DASHBOARD_IMAGE" ]] && print_success "Dashboard: $DASHBOARD_IMAGE" || print_warning "Dashboard: Using chart default" + [[ -n "$P2P_IMAGE" ]] && print_success "P2P (bootnode/bidder/provider): $P2P_IMAGE" || print_warning "P2P: Using chart default" + [[ -n "$RELAY_IMAGE" ]] && print_success "Relay Emulator: $RELAY_IMAGE" || print_warning "Relay Emulator: Using chart default" + [[ -n "$BIDDER_EMULATOR_IMAGE" ]] && print_success "Bidder Emulator: $BIDDER_EMULATOR_IMAGE" || print_warning "Bidder Emulator: Using chart default" + [[ -n "$PROVIDER_EMULATOR_IMAGE" ]] && print_success "Provider Emulator: $PROVIDER_EMULATOR_IMAGE" || print_warning "Provider Emulator: Using chart default" + [[ -n "$ORACLE_IMAGE" ]] && print_success "Oracle: $ORACLE_IMAGE" || print_warning "Oracle: Using chart default" + echo +} + +# Parse discovered image into repository and tag +parse_image() { + local IMAGE="$1" + if [[ -n "$IMAGE" && "$IMAGE" == *":"* ]]; then + REPO="${IMAGE%:*}" + TAG="${IMAGE##*:}" + echo "$REPO" "$TAG" + else + echo "" "" + fi +} + +# Find pod by app label +find_pod() { + APP_NAME="$1" + + # Handle P2P nodes which all use mev-commit-p2p as app name + case "$APP_NAME" in + erigon-mev-commit-bootnode) + kubectl get pods -l "app.kubernetes.io/name=mev-commit-p2p,app.kubernetes.io/component=bootnode" -n "$NAMESPACE" --no-headers 2>/dev/null | awk 'NR==1{print $1}' || true + ;; + erigon-mev-commit-bidder) + kubectl get pods -l "app.kubernetes.io/name=mev-commit-p2p,app.kubernetes.io/component=bidder" -n "$NAMESPACE" --no-headers 2>/dev/null | awk 'NR==1{print $1}' || true + ;; + erigon-mev-commit-provider) + kubectl get pods -l "app.kubernetes.io/name=mev-commit-p2p,app.kubernetes.io/component=provider" -n "$NAMESPACE" --no-headers 2>/dev/null | awk 'NR==1{print $1}' || true + ;; + mev-commit-emulator-bt) + kubectl get pods -l "app.kubernetes.io/name=bidder-emulator" -n "$NAMESPACE" --no-headers 2>/dev/null | awk 'NR==1{print $1}' || true + ;; + *) + kubectl get pods -l "app.kubernetes.io/name=$APP_NAME" -n "$NAMESPACE" --no-headers 2>/dev/null | awk 'NR==1{print $1}' || true + ;; + esac +} + +# Find service by app label +find_service() { + kubectl get svc -l "app.kubernetes.io/name=$1" -n "$NAMESPACE" --no-headers 2>/dev/null | awk 'NR==1{print $1}' || true +} + +# Wait for pod ready +wait_pod_ready() { + APP_NAME="$1" + MAX_WAIT=30 + POD_NAME="" + + print_info "Waiting for $APP_NAME pod..." + for i in $(seq 1 $MAX_WAIT); do + POD_NAME=$(find_pod "$APP_NAME") + [[ -n "$POD_NAME" ]] && break + sleep 2 + done + + if [[ -z "$POD_NAME" ]]; then + print_error "Pod for $APP_NAME not found" + return 1 + fi + + print_info "Waiting for $POD_NAME to be ready..." + if kubectl wait pod/"$POD_NAME" --for=condition=Ready --timeout="${TIMEOUT}s" -n "$NAMESPACE"; then + print_success "$POD_NAME ready!" + else + print_error "$POD_NAME failed to be ready" + return 1 + fi +} + +# Deploy chart +deploy_chart() { + CHART_NAME="$1" + RELEASE_NAME="$2" + CHART_PATH="$3" + shift 3 + EXTRA_ARGS=("$@") + + print_info "Deploying $CHART_NAME..." + + HELM_CMD=(helm install "$RELEASE_NAME" "$CHART_PATH" -n "$NAMESPACE") + [[ "$NAMESPACE" != "default" ]] && HELM_CMD+=(--create-namespace) + [[ "$DRY_RUN" == true ]] && HELM_CMD+=(--dry-run) + [[ ${#EXTRA_ARGS[@]} -gt 0 ]] && HELM_CMD+=("${EXTRA_ARGS[@]}") + + if [[ "$DRY_RUN" == true ]]; then + if "${HELM_CMD[@]}" >/dev/null 2>&1; then + print_success "$CHART_NAME dry-run OK" + else + print_error "$CHART_NAME dry-run failed" + return 1 + fi + else + print_info "Running: ${HELM_CMD[*]}" + if "${HELM_CMD[@]}"; then + print_success "$CHART_NAME deployed" + wait_pod_ready "$CHART_NAME" + else + print_error "$CHART_NAME deployment failed" + return 1 + fi + fi +} + +# RPC check +check_rpc() { + print_info "Checking RPC..." + RESPONSE=$(curl -s -X POST -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \ + "http://127.0.0.1:$RPC_PORT" 2>/dev/null) + + BLOCK_HEX=$(echo "$RESPONSE" | grep -o '"result":"[^"]*"' | cut -d'"' -f4) + if [[ -n "$BLOCK_HEX" && "$BLOCK_HEX" != "null" ]]; then + BLOCK_NUMBER=$((16#${BLOCK_HEX#0x})) + print_success "RPC working! Block: $BLOCK_NUMBER" + return 0 + fi + return 1 +} + +# Find newest contracts JSON +find_contracts_json() { + find /tmp /private/tmp -name "core-contracts.json" -path "*/contracts/deploy-summaries/*" 2>/dev/null | xargs ls -t 2>/dev/null | head -1 +} + +# Extract contract addresses +extract_contracts() { + JSON_FILE="$1" + if [[ ! -f "$JSON_FILE" ]]; then + print_error "Contracts JSON not found: $JSON_FILE" + return 1 + fi + + if command -v jq >/dev/null 2>&1; then + BIDDER_REGISTRY=$(jq -r '.BidderRegistry // ""' "$JSON_FILE") + BLOCK_TRACKER=$(jq -r '.BlockTracker // ""' "$JSON_FILE") + ORACLE=$(jq -r '.Oracle // ""' "$JSON_FILE") + PRECONF_MANAGER=$(jq -r '.PreconfManager // ""' "$JSON_FILE") + PROVIDER_REGISTRY=$(jq -r '.ProviderRegistry // ""' "$JSON_FILE") + else + BIDDER_REGISTRY=$(python3 -c "import json; print(json.load(open('$JSON_FILE')).get('BidderRegistry', ''))") + BLOCK_TRACKER=$(python3 -c "import json; print(json.load(open('$JSON_FILE')).get('BlockTracker', ''))") + ORACLE=$(python3 -c "import json; print(json.load(open('$JSON_FILE')).get('Oracle', ''))") + PRECONF_MANAGER=$(python3 -c "import json; print(json.load(open('$JSON_FILE')).get('PreconfManager', ''))") + PROVIDER_REGISTRY=$(python3 -c "import json; print(json.load(open('$JSON_FILE')).get('ProviderRegistry', ''))") + fi + + if [[ -z "$BIDDER_REGISTRY" || -z "$ORACLE" ]]; then + print_error "Failed to extract contract addresses" + return 1 + fi + + print_success "Contracts extracted" +} + +# Get bootnode connection info +get_bootnode_connection() { + BOOTNODE_SERVICE="erigon-mev-commit-bootnode-mev-commit-p2p-bootnode" + MAX_ATTEMPTS=30 + + print_info "Getting bootnode connection info..." + + # Wait for LoadBalancer to get external IP + print_info "Waiting for LoadBalancer external IP..." + for i in $(seq 1 $MAX_ATTEMPTS); do + BOOTNODE_IP=$(kubectl get svc "$BOOTNODE_SERVICE" -n "$NAMESPACE" -o jsonpath='{.status.loadBalancer.ingress[0].ip}' 2>/dev/null) + if [[ -n "$BOOTNODE_IP" && "$BOOTNODE_IP" != "" ]]; then + break + fi + print_info "Attempt $i/$MAX_ATTEMPTS: Waiting for LoadBalancer IP..." + sleep 10 + done + + if [[ -z "$BOOTNODE_IP" || "$BOOTNODE_IP" == "" ]]; then + print_error "Failed to get bootnode external IP after $((MAX_ATTEMPTS * 10)) seconds" + kubectl get svc "$BOOTNODE_SERVICE" -n "$NAMESPACE" || true + return 1 + fi + + print_success "Bootnode IP: $BOOTNODE_IP" + + # Get peer ID from bootnode API + print_info "Getting peer ID from bootnode API..." + for i in $(seq 1 20); do + print_info "Attempt $i/20: Calling https://$BOOTNODE_IP:13723/v1/debug/topology" + + # Simple curl call matching the manual version + PEER_ID=$(curl -sk "https://$BOOTNODE_IP:13723/v1/debug/topology" | jq -r '.topology.self.Underlay' 2>/dev/null) + + if [[ -n "$PEER_ID" && "$PEER_ID" != "null" ]]; then + BOOTNODE_CONNECTION="/ip4/$BOOTNODE_IP/tcp/13522/p2p/$PEER_ID" + print_success "Bootnode connection: $BOOTNODE_CONNECTION" + return 0 + fi + + print_warning "No valid peer ID received, retrying in 10 seconds..." + + sleep 15 + done + + print_error "Failed to get bootnode peer ID after 20 attempts" + print_info "Final attempt to check bootnode service and pod status:" + kubectl get svc "$BOOTNODE_SERVICE" -n "$NAMESPACE" || true + kubectl get pods -l "app.kubernetes.io/name=mev-commit-p2p,app.kubernetes.io/component=bootnode" -n "$NAMESPACE" || true + return 1 +} + +# Load contracts from file (when --skip-contracts is used) +load_contracts_from_file() { + if [[ "$SKIP_CONTRACTS" == false ]]; then + return 0 # Not skipping contracts, will deploy them + fi + + print_info "Loading contracts from file (--skip-contracts enabled)..." + + # Determine contracts file to use + CONTRACTS_JSON="" + + if [[ -n "$CONTRACTS_FILE" ]]; then + # Use specified file + if [[ -f "$CONTRACTS_FILE" ]]; then + CONTRACTS_JSON="$CONTRACTS_FILE" + print_info "Using specified contracts file: $CONTRACTS_FILE" + else + print_error "Specified contracts file not found: $CONTRACTS_FILE" + return 1 + fi + else + # Auto-discover latest contracts file + CONTRACTS_JSON=$(find_contracts_json) + if [[ -n "$CONTRACTS_JSON" ]]; then + print_info "Auto-discovered contracts file: $CONTRACTS_JSON" + else + print_error "No contracts file found. Provide one with --contracts-file or deploy contracts first" + print_info "Expected JSON format:" + cat << 'EOF' +{ + "BidderRegistry": "0x1234567890123456789012345678901234567890", + "BlockTracker": "0x2345678901234567890123456789012345678901", + "Oracle": "0x3456789012345678901234567890123456789012", + "PreconfManager": "0x4567890123456789012345678901234567890123", + "ProviderRegistry": "0x5678901234567890123456789012345678901234" +} +EOF + return 1 + fi + fi + + # Load contracts from file + if extract_contracts "$CONTRACTS_JSON"; then + print_success "Contracts loaded from file!" + print_info "BidderRegistry: $BIDDER_REGISTRY" + print_info "BlockTracker: $BLOCK_TRACKER" + print_info "Oracle: $ORACLE" + print_info "PreconfManager: $PRECONF_MANAGER" + print_info "ProviderRegistry: $PROVIDER_REGISTRY" + return 0 + else + return 1 + fi +} + +# Port forward cleanup +cleanup_pf() { + [[ -n "$PF_PID" ]] && { kill $PF_PID 2>/dev/null || true; wait $PF_PID 2>/dev/null || true; } +} +trap cleanup_pf EXIT +# Determine charts to deploy +if [[ -n "$SELECTED_CHARTS" ]]; then + IFS=',' read -ra CHARTS <<< "$SELECTED_CHARTS" +else + CHARTS=("mock-l1" "erigon-snode" "dashboard" "relay-emulator" "oracle" "bootnode" "bidder" "bidder-emulator" "provider" "provider-emulator") +fi + +# Validation +for CHART in "${CHARTS[@]}"; do + case "$CHART" in + mock-l1) CHART_PATH="./mev-commit-geth-l1" ;; + erigon-snode) CHART_PATH="./mev-commit-erigon" ;; + relay-emulator) CHART_PATH="./mev-commit-relay-emulator" ;; + oracle) CHART_PATH="./mev-commit-oracle" ;; + dashboard) CHART_PATH="./mev-commit-dashboard" ;; + bootnode|bidder|provider) CHART_PATH="./mev-commit-p2p" ;; + bidder-emulator) CHART_PATH="./mev-commit-emulator-bt" ;; + provider-emulator) CHART_PATH="./mev-commit-emulator" ;; + *) print_error "Unknown chart: $CHART"; exit 1 ;; + esac + [[ ! -d "$CHART_PATH" ]] && { print_error "Chart not found: $CHART_PATH"; exit 1; } +done + +# Check password requirement (only when deploying contracts) +if [[ "$SKIP_CONTRACTS" == false && "$DRY_RUN" == false && -z "$PASSWORD" ]]; then + for CHART in "${CHARTS[@]}"; do + [[ "$CHART" == "erigon-snode" ]] && { print_error "Password required for contracts"; exit 1; } + done +fi + +# Discover images before deployment +discover_images + +# Load contracts from file if skipping deployment +load_contracts_from_file + +print_info "Deploying charts: ${CHARTS[*]}" + +# Initialize contract variables (will be set by load_contracts_from_file if skipping) +if [[ "$SKIP_CONTRACTS" == false ]]; then + BIDDER_REGISTRY="" + BLOCK_TRACKER="" + ORACLE="" + PRECONF_MANAGER="" + PROVIDER_REGISTRY="" +fi + +# Deploy charts +for CHART in "${CHARTS[@]}"; do + case "$CHART" in + mock-l1) + deploy_chart "mev-commit-mock-l1" "erigon-mev-commit-mock-l1" "./mev-commit-geth-l1" + ;; + erigon-snode) + deploy_chart "erigon-snode" "erigon-snode" "./mev-commit-erigon" + + # Deploy contracts if not dry-run and not skipping + if [[ "$SKIP_CONTRACTS" == false && "$DRY_RUN" == false ]]; then + print_info "Deploying contracts..." + + # Port forward + ERIGON_POD=$(find_pod "erigon-snode") + [[ -z "$ERIGON_POD" ]] && { print_error "Erigon pod not found"; exit 1; } + + kubectl port-forward pod/"$ERIGON_POD" "$RPC_PORT:$RPC_PORT" -n "$NAMESPACE" & + PF_PID=$! + sleep 5 + + # Check RPC + if ! check_rpc; then + print_error "RPC check failed" + exit 1 + fi + + # Deploy contracts + cleanup_pf # Stop our PF, Python script will create its own + + PYTHON_ARGS=("$DEPLOY_SCRIPT" "--password" "$PASSWORD") + [[ -n "$WORK_DIR" ]] && PYTHON_ARGS+=("--work-dir" "$WORK_DIR") + + if python3 "${PYTHON_ARGS[@]}"; then + print_success "Contracts deployed!" + + # Extract addresses + CONTRACTS_JSON=$(find_contracts_json) + if [[ -n "$CONTRACTS_JSON" ]] && extract_contracts "$CONTRACTS_JSON"; then + print_info "Contract addresses ready for dashboard" + else + print_error "Failed to extract contract addresses" + exit 1 + fi + else + print_error "Contract deployment failed" + exit 1 + fi + fi + ;; + relay-emulator) + # Get L1 service + L1_SERVICE=$(find_service "mev-commit-mock-l1") + L1_URL="http://${L1_SERVICE:-erigon-mev-commit-mock-l1}.${NAMESPACE}.svc.cluster.local:8545" + + # Prepare image args + RELAY_ARGS=(--set "job.l1RpcUrl=$L1_URL") + + if [[ -n "$RELAY_IMAGE" ]]; then + read RELAY_REPO RELAY_TAG <<< "$(parse_image "$RELAY_IMAGE")" + if [[ -n "$RELAY_REPO" && -n "$RELAY_TAG" ]]; then + RELAY_ARGS+=(--set "image.repository=$RELAY_REPO" --set "image.tag=$RELAY_TAG") + print_info "Using discovered relay image: $RELAY_IMAGE" + fi + fi + + deploy_chart "mev-commit-emulator" "mev-commit-relay-emulator" "./mev-commit-relay-emulator" "${RELAY_ARGS[@]}" + ;; + oracle) + # Oracle needs all services and contract addresses + L1_SERVICE=$(find_service "mev-commit-mock-l1") + L1_URL="http://${L1_SERVICE:-erigon-mev-commit-mock-l1}.${NAMESPACE}.svc.cluster.local:8545" + + ERIGON_SERVICE=$(find_service "erigon-snode") + ERIGON_HTTP="http://${ERIGON_SERVICE:-erigon-snode-erigon}.${NAMESPACE}.svc.cluster.local:8545" + ERIGON_WS="ws://${ERIGON_SERVICE:-erigon-snode-erigon}.${NAMESPACE}.svc.cluster.local:8546" + + RELAY_SERVICE=$(find_service "mev-commit-emulator") + RELAY_URL="http://${RELAY_SERVICE:-mev-commit-relay-emulator-mev-commit-emulator}.${NAMESPACE}.svc.cluster.local:8080" + + ORACLE_ARGS=( + --set "network.l1RpcUrls[0]=$L1_URL" + --set "network.settlementRpcHttp=$ERIGON_HTTP" + --set "network.settlementRpcWs=$ERIGON_WS" + --set "network.relayUrls[0]=$RELAY_URL" + ) + + # Add discovered oracle image + if [[ -n "$ORACLE_IMAGE" ]]; then + read ORACLE_REPO ORACLE_TAG <<< "$(parse_image "$ORACLE_IMAGE")" + if [[ -n "$ORACLE_REPO" && -n "$ORACLE_TAG" ]]; then + ORACLE_ARGS+=(--set "image.repository=$ORACLE_REPO" --set "image.tag=$ORACLE_TAG") + print_info "Using discovered oracle image: $ORACLE_IMAGE" + fi + fi + + # Add contract addresses if available + if [[ -n "$BIDDER_REGISTRY" ]]; then + ORACLE_ARGS+=( + --set "contracts.bidderRegistry=$BIDDER_REGISTRY" + --set "contracts.blockTracker=$BLOCK_TRACKER" + --set "contracts.oracle=$ORACLE" + --set "contracts.preconf=$PRECONF_MANAGER" + --set "contracts.providerRegistry=$PROVIDER_REGISTRY" + ) + fi + + deploy_chart "erigon-oracle" "erigon-oracle" "./mev-commit-oracle" "${ORACLE_ARGS[@]}" + ;; + bootnode) + # Bootnode needs contract addresses and service URLs + L1_SERVICE=$(find_service "mev-commit-mock-l1") + L1_URL="http://${L1_SERVICE:-erigon-mev-commit-mock-l1}.${NAMESPACE}.svc.cluster.local:8545" + + ERIGON_SERVICE=$(find_service "erigon-snode") + ERIGON_HTTP="http://${ERIGON_SERVICE:-erigon-snode-erigon}.${NAMESPACE}.svc.cluster.local:8545" + ERIGON_WS="ws://${ERIGON_SERVICE:-erigon-snode-erigon}.${NAMESPACE}.svc.cluster.local:8546" + + BOOTNODE_ARGS=( + --set "global.rpc.l1Endpoint=$L1_URL" + --set "global.rpc.settlementEndpoint=$ERIGON_HTTP" + --set "global.rpc.settlementWsEndpoint=$ERIGON_WS" + ) + + # Add discovered P2P image + if [[ -n "$P2P_IMAGE" ]]; then + read P2P_REPO P2P_TAG <<< "$(parse_image "$P2P_IMAGE")" + if [[ -n "$P2P_REPO" && -n "$P2P_TAG" ]]; then + BOOTNODE_ARGS+=(--set "global.image.repository=$P2P_REPO" --set "global.image.tag=$P2P_TAG") + print_info "Using discovered P2P image: $P2P_IMAGE" + fi + fi + + # Add contract addresses if available + if [[ -n "$BIDDER_REGISTRY" ]]; then + BOOTNODE_ARGS+=( + --set "global.contracts.bidderRegistry=$BIDDER_REGISTRY" + --set "global.contracts.blockTracker=$BLOCK_TRACKER" + --set "global.contracts.preconfStore=$PRECONF_MANAGER" + --set "global.contracts.providerRegistry=$PROVIDER_REGISTRY" + ) + fi + + deploy_chart "erigon-mev-commit-bootnode" "erigon-mev-commit-bootnode" "./mev-commit-p2p" "${BOOTNODE_ARGS[@]}" -f "./mev-commit-p2p/bootnode-values.yaml" + + # Get bootnode connection string for other P2P nodes + if [[ "$DRY_RUN" == false ]]; then + get_bootnode_connection + fi + ;; + bidder) + # Bidder needs bootnode connection string and RPC endpoints + if [[ -z "$BOOTNODE_CONNECTION" && "$DRY_RUN" == false ]]; then + print_error "Bootnode connection string not available for bidder" + exit 1 + fi + + # Get service URLs + L1_SERVICE=$(find_service "mev-commit-mock-l1") + L1_URL="http://${L1_SERVICE:-erigon-mev-commit-mock-l1}.${NAMESPACE}.svc.cluster.local:8545" + + ERIGON_SERVICE=$(find_service "erigon-snode") + ERIGON_HTTP="http://${ERIGON_SERVICE:-erigon-snode-erigon}.${NAMESPACE}.svc.cluster.local:8545" + ERIGON_WS="ws://${ERIGON_SERVICE:-erigon-snode-erigon}.${NAMESPACE}.svc.cluster.local:8546" + + BIDDER_ARGS=( + --set "global.rpc.l1Endpoint=$L1_URL" + --set "global.rpc.settlementEndpoint=$ERIGON_HTTP" + --set "global.rpc.settlementWsEndpoint=$ERIGON_WS" + ) + + # Add discovered P2P image + if [[ -n "$P2P_IMAGE" ]]; then + read P2P_REPO P2P_TAG <<< "$(parse_image "$P2P_IMAGE")" + if [[ -n "$P2P_REPO" && -n "$P2P_TAG" ]]; then + BIDDER_ARGS+=(--set "global.image.repository=$P2P_REPO" --set "global.image.tag=$P2P_TAG") + print_info "Using discovered P2P image: $P2P_IMAGE" + fi + fi + + [[ -n "$BOOTNODE_CONNECTION" ]] && BIDDER_ARGS+=(--set "node.bootnodeConnectionString=$BOOTNODE_CONNECTION") + + # Add contract addresses if available + if [[ -n "$BIDDER_REGISTRY" ]]; then + BIDDER_ARGS+=( + --set "global.contracts.bidderRegistry=$BIDDER_REGISTRY" + --set "global.contracts.blockTracker=$BLOCK_TRACKER" + --set "global.contracts.preconfStore=$PRECONF_MANAGER" + --set "global.contracts.providerRegistry=$PROVIDER_REGISTRY" + ) + fi + + deploy_chart "erigon-mev-commit-bidder" "erigon-mev-commit-bidder" "./mev-commit-p2p" "${BIDDER_ARGS[@]}" -f "./mev-commit-p2p/values-bidder.yaml" + ;; + bidder-emulator) + # Bidder emulator needs Mock L1 and Bidder service URLs + L1_SERVICE=$(find_service "mev-commit-mock-l1") + L1_URL="http://${L1_SERVICE:-erigon-mev-commit-mock-l1}.${NAMESPACE}.svc.cluster.local:8545" + + # For bidder service, we need to handle the P2P service naming + BIDDER_SERVICE="erigon-mev-commit-bidder-mev-commit-p2p-bidder" + BIDDER_RPC_URL="${BIDDER_SERVICE}.${NAMESPACE}.svc.cluster.local:13724" + + BIDDER_EMU_ARGS=( + --set "bidderEmulator.l1RpcUrl=$L1_URL" + --set "bidderEmulator.bidderRpcUrl=$BIDDER_RPC_URL" + ) + + # Add discovered bidder emulator image + if [[ -n "$BIDDER_EMULATOR_IMAGE" ]]; then + read BIDDER_EMU_REPO BIDDER_EMU_TAG <<< "$(parse_image "$BIDDER_EMULATOR_IMAGE")" + if [[ -n "$BIDDER_EMU_REPO" && -n "$BIDDER_EMU_TAG" ]]; then + BIDDER_EMU_ARGS+=(--set "image.repository=$BIDDER_EMU_REPO" --set "image.tag=$BIDDER_EMU_TAG") + print_info "Using discovered bidder emulator image: $BIDDER_EMULATOR_IMAGE" + fi + fi + + deploy_chart "mev-commit-emulator-bt" "mev-commit-emulator-bt" "./mev-commit-emulator-bt" "${BIDDER_EMU_ARGS[@]}" + ;; + provider) + # Provider needs bootnode connection string, RPC endpoints, and contract addresses + if [[ -z "$BOOTNODE_CONNECTION" && "$DRY_RUN" == false ]]; then + print_error "Bootnode connection string not available for provider" + exit 1 + fi + + # Get service URLs + L1_SERVICE=$(find_service "mev-commit-mock-l1") + L1_URL="http://${L1_SERVICE:-erigon-mev-commit-mock-l1}.${NAMESPACE}.svc.cluster.local:8545" + + ERIGON_SERVICE=$(find_service "erigon-snode") + ERIGON_HTTP="http://${ERIGON_SERVICE:-erigon-snode-erigon}.${NAMESPACE}.svc.cluster.local:8545" + ERIGON_WS="ws://${ERIGON_SERVICE:-erigon-snode-erigon}.${NAMESPACE}.svc.cluster.local:8546" + + PROVIDER_ARGS=( + --set "global.rpc.l1Endpoint=$L1_URL" + --set "global.rpc.settlementEndpoint=$ERIGON_HTTP" + --set "global.rpc.settlementWsEndpoint=$ERIGON_WS" + ) + + # Add discovered P2P image + if [[ -n "$P2P_IMAGE" ]]; then + read P2P_REPO P2P_TAG <<< "$(parse_image "$P2P_IMAGE")" + if [[ -n "$P2P_REPO" && -n "$P2P_TAG" ]]; then + PROVIDER_ARGS+=(--set "global.image.repository=$P2P_REPO" --set "global.image.tag=$P2P_TAG") + print_info "Using discovered P2P image: $P2P_IMAGE" + fi + fi + + [[ -n "$BOOTNODE_CONNECTION" ]] && PROVIDER_ARGS+=(--set "node.bootnodeConnectionString=$BOOTNODE_CONNECTION") + + # Add contract addresses if available + if [[ -n "$BIDDER_REGISTRY" ]]; then + PROVIDER_ARGS+=( + --set "global.contracts.bidderRegistry=$BIDDER_REGISTRY" + --set "global.contracts.blockTracker=$BLOCK_TRACKER" + --set "global.contracts.preconfStore=$PRECONF_MANAGER" + --set "global.contracts.providerRegistry=$PROVIDER_REGISTRY" + ) + fi + + deploy_chart "erigon-mev-commit-provider" "erigon-mev-commit-provider" "./mev-commit-p2p" "${PROVIDER_ARGS[@]}" -f "./mev-commit-p2p/values-provider.yaml" + ;; + provider-emulator) + # Provider emulator needs Provider and Relay service URLs + PROVIDER_SERVICE="erigon-mev-commit-provider-mev-commit-p2p-provider" + PROVIDER_URL="${PROVIDER_SERVICE}.${NAMESPACE}.svc.cluster.local:13624" + + RELAY_SERVICE=$(find_service "mev-commit-emulator") + RELAY_URL="http://${RELAY_SERVICE:-mev-commit-relay-emulator-mev-commit-emulator}.${NAMESPACE}.svc.cluster.local:8080" + + PROVIDER_EMU_ARGS=( + --set "job.serverAddr=$PROVIDER_URL" + --set "job.relay=$RELAY_URL" + ) + + # Add discovered provider emulator image + if [[ -n "$PROVIDER_EMULATOR_IMAGE" ]]; then + read PROVIDER_EMU_REPO PROVIDER_EMU_TAG <<< "$(parse_image "$PROVIDER_EMULATOR_IMAGE")" + if [[ -n "$PROVIDER_EMU_REPO" && -n "$PROVIDER_EMU_TAG" ]]; then + PROVIDER_EMU_ARGS+=(--set "image.repository=$PROVIDER_EMU_REPO" --set "image.tag=$PROVIDER_EMU_TAG") + print_info "Using discovered provider emulator image: $PROVIDER_EMULATOR_IMAGE" + fi + fi + + deploy_chart "mev-commit-emulator" "mev-commit-emulator" "./mev-commit-emulator" "${PROVIDER_EMU_ARGS[@]}" -f "./mev-commit-emulator/provider-emulator-values.yaml" + ;; + dashboard) + # Get erigon service and contract addresses + ERIGON_SERVICE=$(find_service "erigon-snode") + RPC_URL="ws://${ERIGON_SERVICE:-erigon-snode-erigon}.${NAMESPACE}.svc.cluster.local:8546" + + DASHBOARD_ARGS=( + --set "config.rpcUrl=$RPC_URL" + ) + + # Add discovered dashboard image + if [[ -n "$DASHBOARD_IMAGE" ]]; then + read DASHBOARD_REPO DASHBOARD_TAG <<< "$(parse_image "$DASHBOARD_IMAGE")" + if [[ -n "$DASHBOARD_REPO" && -n "$DASHBOARD_TAG" ]]; then + DASHBOARD_ARGS+=(--set "image.repository=$DASHBOARD_REPO" --set "image.tag=$DASHBOARD_TAG") + print_info "Using discovered dashboard image: $DASHBOARD_IMAGE" + fi + fi + + # Add contract addresses if available + if [[ -n "$BIDDER_REGISTRY" ]]; then + DASHBOARD_ARGS+=( + --set "config.bidderregistryContractAddr=$BIDDER_REGISTRY" + --set "config.blocktrackerContractAddr=$BLOCK_TRACKER" + --set "config.oracleContractAddr=$ORACLE" + --set "config.preconfContractAddr=$PRECONF_MANAGER" + --set "config.providerregistryContractAddr=$PROVIDER_REGISTRY" + ) + fi + + deploy_chart "mev-commit-dashboard" "mev-commit-dashboard" "./mev-commit-dashboard" "${DASHBOARD_ARGS[@]}" + ;; + esac +done + +[[ "$DRY_RUN" == true ]] && { print_success "Dry-run completed!"; exit 0; } + +print_success "All deployments completed!" + +# Summary +print_info "=== Summary ===" +for CHART in "${CHARTS[@]}"; do + case "$CHART" in + mock-l1) print_info "Mock L1: $(find_service "mev-commit-mock-l1").$NAMESPACE.svc.cluster.local:8545" ;; + erigon-snode) print_info "Erigon: $(find_service "erigon-snode").$NAMESPACE.svc.cluster.local:8545" ;; + relay-emulator) print_info "Relay: $(find_service "mev-commit-relay-emulator").$NAMESPACE.svc.cluster.local:8080" ;; + dashboard) print_info "Dashboard: $(find_service "mev-commit-dashboard").$NAMESPACE.svc.cluster.local:8080" ;; + esac +done +[[ -n "$BIDDER_REGISTRY" ]] && print_info "Contracts deployed โœ…" + +# Image summary +if [[ -n "$BRANCH" ]]; then + print_info "=== Images Used ===" + [[ -n "$DASHBOARD_IMAGE" ]] && print_info "Dashboard: $DASHBOARD_IMAGE" || print_info "Dashboard: Chart default" + [[ -n "$P2P_IMAGE" ]] && print_info "P2P (bootnode/bidder/provider): $P2P_IMAGE" || print_info "P2P: Chart default" + [[ -n "$RELAY_IMAGE" ]] && print_info "Relay Emulator: $RELAY_IMAGE" || print_info "Relay Emulator: Chart default" + [[ -n "$BIDDER_EMULATOR_IMAGE" ]] && print_info "Bidder Emulator: $BIDDER_EMULATOR_IMAGE" || print_info "Bidder Emulator: Chart default" + [[ -n "$PROVIDER_EMULATOR_IMAGE" ]] && print_info "Provider Emulator: $PROVIDER_EMULATOR_IMAGE" || print_info "Provider Emulator: Chart default" + [[ -n "$ORACLE_IMAGE" ]] && print_info "Oracle: $ORACLE_IMAGE" || print_info "Oracle: Chart default" +fi diff --git a/infrastructure/charts/mev-commit-dashboard/values.yaml b/infrastructure/charts/mev-commit-dashboard/values.yaml index f56fd40c3..cfc9d7192 100644 --- a/infrastructure/charts/mev-commit-dashboard/values.yaml +++ b/infrastructure/charts/mev-commit-dashboard/values.yaml @@ -1,15 +1,15 @@ replicaCount: 1 image: - repository: - tag: + repository: primev/primev + tag: pullPolicy: Always nameOverride: "" -fullnameOverride: "" +fullnameOverride: "erigon-mev-commit-dashboard-snode" # Namespace to deploy resources -namespace: mev-commit-observability +namespace: default # Common labels to apply to all resources commonLabels: {} diff --git a/infrastructure/charts/mev-commit-emulator-bt/Chart.yaml b/infrastructure/charts/mev-commit-emulator-bt/Chart.yaml new file mode 100644 index 000000000..993b601f3 --- /dev/null +++ b/infrastructure/charts/mev-commit-emulator-bt/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: bidder-emulator +description: A Helm chart for deploying bidder emulator and l1-transactor +type: application +version: 0.1.0 +appVersion: "1.0.0" diff --git a/infrastructure/charts/mev-commit-emulator-bt/templates/_helpers.tpl b/infrastructure/charts/mev-commit-emulator-bt/templates/_helpers.tpl new file mode 100644 index 000000000..475ca689c --- /dev/null +++ b/infrastructure/charts/mev-commit-emulator-bt/templates/_helpers.tpl @@ -0,0 +1,81 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "bidder-emulator.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "bidder-emulator.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "bidder-emulator.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "bidder-emulator.labels" -}} +helm.sh/chart: {{ include "bidder-emulator.chart" . }} +{{ include "bidder-emulator.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "bidder-emulator.selectorLabels" -}} +app.kubernetes.io/name: {{ include "bidder-emulator.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Generate log tags +*/}} +{{- define "bidder-emulator.logTags" -}} +{{- $tags := list }} +{{- if .Values.bidderEmulator.logTags }} +{{- $tags = append $tags .Values.bidderEmulator.logTags }} +{{- end }} +{{- $tags = append $tags (printf "service:%s" (include "bidder-emulator.name" .)) }} +{{- $tags = append $tags (printf "version:%s" .Values.version) }} +{{- $tags = append $tags (printf "namespace:%s" (.Values.namespace | default "default")) }} +{{- join "," $tags }} +{{- end }} + +{{/* +Validate required values +*/}} +{{- define "bidder-emulator.validateValues" -}} +{{- if not .Values.bidderEmulator.l1RpcUrl }} +{{- fail "bidderEmulator.l1RpcUrl is required" }} +{{- end }} +{{- if not .Values.bidderEmulator.bidderRpcUrl }} +{{- fail "bidderEmulator.bidderRpcUrl is required" }} +{{- end }} +{{- if not .Values.keystores.urls }} +{{- fail "keystores.urls is required - at least one keystore URL must be provided" }} +{{- end }} +{{- if not .Values.keystores.password }} +{{- fail "keystores.password is required" }} +{{- end }} +{{- end }} diff --git a/infrastructure/charts/mev-commit-emulator-bt/templates/configmap.yaml b/infrastructure/charts/mev-commit-emulator-bt/templates/configmap.yaml new file mode 100644 index 000000000..b4c8e04ae --- /dev/null +++ b/infrastructure/charts/mev-commit-emulator-bt/templates/configmap.yaml @@ -0,0 +1,338 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "bidder-emulator.fullname" . }}-scripts + namespace: {{ .Values.namespace | default "default" }} + labels: + {{- include "bidder-emulator.labels" . | nindent 4 }} +data: + setup.sh: | + #!/bin/sh + + set -e + + # Configuration from environment variables + KEYSTORE_PATH="${KEYSTORE_PATH:-/shared/keystores}" + KEYSTORE_URLS="${KEYSTORE_DOWNLOAD_URLS}" # Space/comma separated URLs + MAX_RETRIES="${KEYSTORE_RETRIES:-3}" + + echo "=== Bidder Emulator Keystore Initialization ===" + echo "Keystore path: $KEYSTORE_PATH" + + # Validate required environment variables + if [ -z "$KEYSTORE_URLS" ]; then + echo "Error: KEYSTORE_DOWNLOAD_URLS is required" + exit 1 + fi + + echo "Keystore URLs to process: $KEYSTORE_URLS" + + # Create keystore directory if it doesn't exist + mkdir -p "$KEYSTORE_PATH" + + # Function to extract address from filename using POSIX shell + extract_address_from_filename() { + local filename="$1" + + # Check if filename matches UTC----
format + case "$filename" in + UTC--????-??-??T??-??-??.??????*Z--*) + # Extract the address part after the last -- + address_part="${filename##*--}" + # Validate address is 40 hex characters + case "$address_part" in + *[!0-9a-fA-F]* | ????????????????????????????????????) + echo "Invalid address format: $address_part" >&2 + return 1 + ;; + ????????????????????????????????????????) + echo "$address_part" + return 0 + ;; + *) + echo "Invalid address length: $address_part" >&2 + return 1 + ;; + esac + ;; + *) + echo "Invalid keystore filename format: $filename" >&2 + return 1 + ;; + esac + } + + # Function to extract address from keystore JSON content + extract_address_from_content() { + local file_path="$1" + + if command -v jq >/dev/null 2>&1; then + jq -r '.address' "$file_path" 2>/dev/null || echo "" + else + # Extract address using grep and sed (fallback) + grep -o '"address"[[:space:]]*:[[:space:]]*"[^"]*"' "$file_path" 2>/dev/null | \ + sed 's/.*"address"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' || echo "" + fi + } + + # Function to normalize address (remove 0x prefix and convert to lowercase) + normalize_address() { + local addr="$1" + # Remove 0x prefix if present and convert to lowercase + echo "${addr#0x}" | tr '[:upper:]' '[:lower:]' + } + + # Function to validate keystore file + validate_keystore() { + local file_path="$1" + local filename="$2" + + echo "Validating keystore file: $file_path" + + if [ ! -f "$file_path" ]; then + echo "Keystore file does not exist" + return 1 + fi + + # Check if file is readable and not empty + if [ ! -s "$file_path" ]; then + echo "Keystore file is empty" + return 1 + fi + + # Extract address from filename + filename_address=$(extract_address_from_filename "$filename") + if [ $? -ne 0 ]; then + echo "โœ— Invalid filename format" + return 1 + fi + + # Normalize filename address + filename_address=$(normalize_address "$filename_address") + echo "Address from filename: $filename_address" + + # Check basic JSON structure first + if command -v jq >/dev/null 2>&1; then + echo "Using jq for JSON validation" + + # Validate JSON structure + if ! jq -e '.address and .crypto and .crypto.cipher and .crypto.ciphertext and .crypto.kdf' "$file_path" >/dev/null 2>&1; then + echo "โœ— Invalid keystore JSON structure" + return 1 + fi + + # Extract address from content + content_address=$(extract_address_from_content "$file_path") + if [ -z "$content_address" ]; then + echo "โœ— Could not extract address from keystore content" + return 1 + fi + + # Normalize content address + content_address=$(normalize_address "$content_address") + echo "Address from content: $content_address" + + # Compare addresses (case-insensitive) + if [ "$filename_address" = "$content_address" ]; then + echo "โœ“ Keystore validation successful - addresses match: $filename_address" + return 0 + else + echo "โœ— Address mismatch - filename: $filename_address, content: $content_address" + return 1 + fi + else + echo "Using basic validation (jq not available)" + + # Basic validation without jq + if ! grep -q '"crypto"' "$file_path" || \ + ! grep -q '"cipher"' "$file_path" || \ + ! grep -q '"ciphertext"' "$file_path" || \ + ! grep -q '"kdf"' "$file_path"; then + echo "โœ— Basic keystore structure validation failed" + return 1 + fi + + # Extract address from content using basic tools + content_address=$(extract_address_from_content "$file_path") + if [ -z "$content_address" ]; then + echo "โœ— Could not extract address from keystore content" + return 1 + fi + + # Normalize content address + content_address=$(normalize_address "$content_address") + echo "Address from content: $content_address" + + # Compare addresses (case-insensitive) + if [ "$filename_address" = "$content_address" ]; then + echo "โœ“ Basic keystore validation successful - addresses match: $filename_address" + return 0 + else + echo "โœ— Address mismatch - filename: $filename_address, content: $content_address" + return 1 + fi + fi + } + + # Function to download keystore with retries + download_keystore() { + local url="$1" + local output_path="$2" + local retries=0 + + echo "Downloading keystore from: $url" + + # Download with retries + while [ $retries -lt $MAX_RETRIES ]; do + echo "Download attempt $((retries + 1))/$MAX_RETRIES" + + if curl -fsSL --connect-timeout 30 --max-time 300 -o "$output_path" "$url"; then + echo "โœ“ Download successful" + return 0 + else + retries=$((retries + 1)) + if [ $retries -lt $MAX_RETRIES ]; then + echo "Download failed, retrying in 5 seconds..." + sleep 5 + else + echo "โœ— Download failed after $MAX_RETRIES attempts" + return 1 + fi + fi + done + } + + # Function to get filename from URL + get_filename_from_url() { + local url="$1" + basename "$url" + } + + # Function to process a single keystore + process_keystore() { + local url="$1" + local keystore_index="$2" + local temp_download="/tmp/keystore_temp_$(date +%s%N).json" + + echo "--- Processing keystore${keystore_index}: $url ---" + + # Get expected filename from URL + local expected_filename=$(get_filename_from_url "$url") + local keystore_dir="$KEYSTORE_PATH/keystore${keystore_index}" + local keystore_file_path="$keystore_dir/$expected_filename" + + echo "Expected filename: $expected_filename" + echo "Keystore directory: $keystore_dir" + + # Create keystore directory + mkdir -p "$keystore_dir" + + # Validate filename format before proceeding + if ! extract_address_from_filename "$expected_filename" >/dev/null 2>&1; then + echo "โœ— Invalid filename format in URL: $expected_filename" + echo "Expected format: UTC----
" + return 1 + fi + + # Check if keystore already exists and is valid + if [ -f "$keystore_file_path" ]; then + if validate_keystore "$keystore_file_path" "$expected_filename"; then + echo "โœ“ Valid keystore already exists - skipping download" + return 0 + else + echo "Removing invalid keystore file..." + rm -f "$keystore_file_path" + fi + fi + + # Download keystore to temporary location first + if ! download_keystore "$url" "$temp_download"; then + echo "โœ— Failed to download keystore from $url" + rm -f "$temp_download" + return 1 + fi + + # Validate downloaded keystore before moving it to final location + if ! validate_keystore "$temp_download" "$expected_filename"; then + echo "โœ— Downloaded keystore validation failed for $url" + rm -f "$temp_download" + return 1 + fi + + # Move validated keystore to final location + mv "$temp_download" "$keystore_file_path" + + # Set proper permissions + chmod 600 "$keystore_file_path" + + # Extract final address for logging + local final_address=$(extract_address_from_filename "$expected_filename") + echo "โœ“ Keystore processed successfully" + echo "โœ“ Keystore directory: $keystore_dir" + echo "โœ“ Keystore file: $keystore_file_path" + echo "โœ“ Address: $final_address" + + return 0 + } + + # Main processing logic + PROCESSED_COUNT=0 + FAILED_COUNT=0 + PROCESSED_ADDRESSES="" + + # Convert space/comma separated URLs to newline separated for processing + keystore_index=1 + echo "$KEYSTORE_URLS" | tr ' ,' '\n' | while IFS= read -r url; do + # Skip empty lines + [ -z "$url" ] && continue + + if process_keystore "$url" "$keystore_index"; then + PROCESSED_COUNT=$((PROCESSED_COUNT + 1)) + # Extract address for summary + filename=$(get_filename_from_url "$url") + address=$(extract_address_from_filename "$filename" 2>/dev/null || echo "unknown") + if [ -z "$PROCESSED_ADDRESSES" ]; then + PROCESSED_ADDRESSES="$address" + else + PROCESSED_ADDRESSES="$PROCESSED_ADDRESSES, $address" + fi + else + FAILED_COUNT=$((FAILED_COUNT + 1)) + echo "โœ— Failed to process keystore from URL: $url" + fi + + keystore_index=$((keystore_index + 1)) + echo "" + done + + # Create a consolidated checksum file for all keystores + if command -v sha256sum >/dev/null 2>&1; then + find "$KEYSTORE_PATH" -name "*.json" -type f -exec sha256sum {} \; > "$KEYSTORE_PATH/.keystores.checksum" 2>/dev/null || true + echo "โœ“ Consolidated checksum file created" + fi + + # Final summary + echo "=== Keystore Initialization Summary ===" + echo "โœ“ Keystore directory: $KEYSTORE_PATH" + echo "โœ“ Total keystores processed: $PROCESSED_COUNT" + if [ $FAILED_COUNT -gt 0 ]; then + echo "โœ— Failed downloads: $FAILED_COUNT" + exit 1 + else + echo "โœ“ All keystores initialized successfully" + fi + + # Clean up any loose keystore files in the root directory + echo "Cleaning up any loose keystore files in root directory..." + find "$KEYSTORE_PATH" -maxdepth 1 -name "UTC--*.json" -type f -delete 2>/dev/null || true + + # List all keystore directories for verification + echo "โœ“ Available keystore directories:" + for dir in "$KEYSTORE_PATH"/keystore*; do + if [ -d "$dir" ]; then + echo " $(basename "$dir"):" + ls -la "$dir"/*.json 2>/dev/null | sed 's/^/ /' || echo " No keystore files found" + fi + done + + echo "โœ“ Keystore initialization completed successfully" diff --git a/infrastructure/charts/mev-commit-emulator-bt/templates/deployment.yaml b/infrastructure/charts/mev-commit-emulator-bt/templates/deployment.yaml new file mode 100644 index 000000000..540aca912 --- /dev/null +++ b/infrastructure/charts/mev-commit-emulator-bt/templates/deployment.yaml @@ -0,0 +1,150 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "bidder-emulator.fullname" . }} + namespace: {{ .Values.namespace | default "default" }} + labels: + {{- include "bidder-emulator.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + strategy: + type: Recreate + selector: + matchLabels: + {{- include "bidder-emulator.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/scripts: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "bidder-emulator.selectorLabels" . | nindent 8 }} + service.name: {{ include "bidder-emulator.name" . }} + service.version: {{ .Values.version }} + spec: + {{- include "bidder-emulator.validateValues" . }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + initContainers: + - name: setup-keystores + image: "curlimages/curl:8.5.0" + imagePullPolicy: IfNotPresent + command: ["/bin/sh"] + args: ["/scripts/setup.sh"] + env: + {{- if .Values.keystores.urls }} + - name: KEYSTORE_DOWNLOAD_URLS + value: "{{ join " " .Values.keystores.urls }}" + {{- end }} + - name: KEYSTORE_PATH + value: "/shared/keystores" + - name: KEYSTORE_RETRIES + value: "{{ .Values.keystores.retries }}" + volumeMounts: + - name: shared-data + mountPath: /shared + - name: setup-scripts + mountPath: /scripts + readOnly: true + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 500m + memory: 512Mi + + containers: + - name: {{ include "bidder-emulator.name" . }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: ["/usr/local/bin/bidder-emulator"] + args: + - --l1-rpc-url={{ .Values.bidderEmulator.l1RpcUrl }} + - --bidder-rpc-url={{ .Values.bidderEmulator.bidderRpcUrl }} + - --deposit-amount={{ .Values.bidderEmulator.depositAmount }} + - --bid-workers={{ .Values.bidderEmulator.bidWorkers }} + {{- if .Values.bidderEmulator.logFormat }} + - --log-fmt={{ .Values.bidderEmulator.logFormat }} + {{- end }} + {{- if .Values.bidderEmulator.logLevel }} + - --log-level={{ .Values.bidderEmulator.logLevel }} + {{- end }} + {{- if include "bidder-emulator.logTags" . }} + - --log-tags={{ include "bidder-emulator.logTags" . }} + {{- end }} + env: + {{- $keystorePaths := list }} + {{- if .Values.keystores.urls }} + {{- range $index, $url := .Values.keystores.urls }} + {{- $filename := base $url }} + {{- $keystoreDir := printf "keystore%d" (add $index 1) }} + {{- $keystorePaths = append $keystorePaths (printf "/shared/keystores/%s/%s:%s" $keystoreDir $filename $.Values.keystores.password) }} + {{- end }} + {{- end }} + {{- if $keystorePaths }} + - name: TRANSACTOR_KEYSTORE_PATH_PASSWORD + value: "{{ join "," $keystorePaths }}" + {{- end }} + volumeMounts: + - name: shared-data + mountPath: /shared + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + exec: + command: + - /bin/sh + - -c + - "pgrep bidder-emulator" + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + exec: + command: + - /bin/sh + - -c + - "pgrep bidder-emulator" + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + {{- end }} + + volumes: + - name: shared-data + emptyDir: {} + - name: setup-scripts + configMap: + name: {{ include "bidder-emulator.fullname" . }}-scripts + defaultMode: 0755 + + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/infrastructure/charts/mev-commit-emulator-bt/values.yaml b/infrastructure/charts/mev-commit-emulator-bt/values.yaml new file mode 100644 index 000000000..0fe483d5f --- /dev/null +++ b/infrastructure/charts/mev-commit-emulator-bt/values.yaml @@ -0,0 +1,107 @@ +# Default values for bidder-emulator +replicaCount: 1 + +image: + repository: primev/primev + pullPolicy: Always + tag: "bidder-emulator-v5" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +# Namespace to deploy to +namespace: "default" + +# Pod annotations +podAnnotations: {} + +# Pod security context +podSecurityContext: {} + +# Container security context +securityContext: {} + +# Node selector +nodeSelector: {} + +# Tolerations +tolerations: [] + +# Affinity rules +affinity: {} + +# Resource limits and requests +resources: + limits: + cpu: 1000m + memory: 1Gi + requests: + cpu: 500m + memory: 512Mi + +# Bidder Emulator specific configuration +bidderEmulator: + # L1 RPC URL (required) + l1RpcUrl: "http://erigon-mev-commit-mock-l1.default.svc.cluster.local:8545" + + # Bidder RPC URL (required) + bidderRpcUrl: "erigon-mev-commit-bidder-mev-commit-p2p-bidder.default.svc.cluster.local:13724" + + # Deposit amount in wei (default: 1 ETH) + depositAmount: "1000000000000000000" + + # Number of bid workers to run concurrently + bidWorkers: 100 + + # Logging configuration + logLevel: "info" + logFormat: "text" + logTags: "" + +# Keystore configuration + +keystores: + urls: + - https://storage.googleapis.com/devnet-artifacts/keystores/mock-l1-keystores/keystore1/UTC--2025-07-21T14-22-29.999537000Z--0850f7e6d406c279a54798ebf49cd28a63b2ac66 + - https://storage.googleapis.com/devnet-artifacts/keystores/mock-l1-keystores/keystore2/UTC--2025-07-21T14-22-30.656419000Z--c2d5682a51108abda388a97c438ad7dcbbe1f19e + - https://storage.googleapis.com/devnet-artifacts/keystores/mock-l1-keystores/keystore3/UTC--2025-07-21T14-22-31.312232000Z--3d80a5769d9e9336c8b7cf8d8dc35e6372a17426 + - https://storage.googleapis.com/devnet-artifacts/keystores/mock-l1-keystores/keystore4/UTC--2025-07-21T14-22-31.970211000Z--54d45c914652c5013d10e65a7b8089aaac05acaf + - https://storage.googleapis.com/devnet-artifacts/keystores/mock-l1-keystores/keystore5/UTC--2025-07-21T14-22-32.630793000Z--a9db89c3c31e30b5b70db0196b7b95c209ec3cbc + - https://storage.googleapis.com/devnet-artifacts/keystores/mock-l1-keystores/keystore6/UTC--2025-07-21T14-22-33.285067000Z--9b6771c9142e5b7372bc4751ae8eb4a29770a5ad + - https://storage.googleapis.com/devnet-artifacts/keystores/mock-l1-keystores/keystore7/UTC--2025-07-21T14-22-33.945507000Z--2e0fc95244294adaec32d7a3f022bafffa40b8bc + - https://storage.googleapis.com/devnet-artifacts/keystores/mock-l1-keystores/keystore8/UTC--2025-07-21T14-22-34.606052000Z--ee8558e130ea52dc741e9ced909e831018e85b41 + - https://storage.googleapis.com/devnet-artifacts/keystores/mock-l1-keystores/keystore9/UTC--2025-07-21T14-22-35.259877000Z--11b06c0692b4a0c578fd060ce1085f78fc535cc7 + - https://storage.googleapis.com/devnet-artifacts/keystores/mock-l1-keystores/keystore10/UTC--2025-07-21T14-22-35.925315000Z--17d53e9eb9bfafb97f6a9e9b0d1a84b705efe107 + - https://storage.googleapis.com/devnet-artifacts/keystores/mock-l1-keystores/keystore11/UTC--2025-07-21T14-22-36.581191000Z--92ac5a7a965a7de2d3791dc2d1fbcb50afb460e1 + - https://storage.googleapis.com/devnet-artifacts/keystores/mock-l1-keystores/keystore12/UTC--2025-07-21T14-22-37.233621000Z--68177dd5a3316e39eb7f800751cc0679c5d407da + - https://storage.googleapis.com/devnet-artifacts/keystores/mock-l1-keystores/keystore13/UTC--2025-07-21T14-22-37.892482000Z--10b0a998f27837affe4962d9d84f81dd49bc29fb + - https://storage.googleapis.com/devnet-artifacts/keystores/mock-l1-keystores/keystore14/UTC--2025-07-21T14-22-38.548957000Z--bcf0e229b5c8e48066759f29bb34670ae57ffd48 + + + + + + # Keystore password + password: "CXBMf4xEDO0I" + + # Number of download retries + retries: 3 + +# Probes configuration +livenessProbe: + enabled: false + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + +readinessProbe: + enabled: false + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + +# Version for labeling +version: "latest" diff --git a/infrastructure/charts/mev-commit-emulator/Chart.yaml b/infrastructure/charts/mev-commit-emulator/Chart.yaml new file mode 100644 index 000000000..ee08e6d2d --- /dev/null +++ b/infrastructure/charts/mev-commit-emulator/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: mev-commit-emulator +description: A Helm chart for deploying mev-commit-emulator service +type: application +version: 0.1.0 +appVersion: "1.0.0" diff --git a/infrastructure/charts/mev-commit-emulator/bidder-emulator-values.yaml b/infrastructure/charts/mev-commit-emulator/bidder-emulator-values.yaml new file mode 100644 index 000000000..857b0bb1f --- /dev/null +++ b/infrastructure/charts/mev-commit-emulator/bidder-emulator-values.yaml @@ -0,0 +1,107 @@ +# Multi-Emulator Helm Chart Values (Bidder & Provider) + +# Global settings +namespace: default + +# Name overrides +nameOverride: "" +fullnameOverride: "" + +# Job configuration +job: + # Type of emulator: "bidder" or "provider" + type: "bidder" # Change to "provider" for provider-emulator + name: "bidder-emulator" # Change to "provider-emulator" for provider + replicas: 1 + + # Common flags for both emulators + #serverAddr: "mev-commit-provider-mev-commit-p2p-provider.default.svc.cluster.local:13624" # Provider endpoint + serverAddr: "erigon-mev-commit-bidder-mev-commit-p2p-bidder.default.svc.cluster.local:13724" # Bidder endpoint + httpPort: 8080 + env: + log-format: "json" # text or json + log-level: "debug" # debug, info, warn, error + log-tags: "" # Additional comma-separated log tags + otel-collector-endpoint-url: "" # OpenTelemetry collector URL + + # Bidder-specific flags (only used when type: "bidder") + rpcAddr: "http://erigon-mev-commit-mock-l1.default.svc.cluster.local:8545" # mock l1 endpoint + bidWorkers: 200 + + # Provider-specific flags (only used when type: "provider") + relay: "http://erigon-mev-commit-relay-emulator-mev-commit-emulator.default.svc.cluster.local:8080" # Relay address + errorProbability: 20 # Error probability (0-100) + +# Bidder/Provider emulator image +image: + repository: "primev/primev" + #tag: "provideremulator-amd" # provider --> tag: "provideremulator-amd" + tag: "" # bidderemulator-amd-v1 + pullPolicy: Always + +# Service configuration +service: + enabled: true + type: ClusterIP + port: 8080 + targetPort: 8080 + annotations: {} + +# Resource configuration +resources: + limits: + cpu: 15000m + memory: 50Gi + requests: + cpu: 250m + memory: 256Mi + +# Health checks +livenessProbe: + enabled: false + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + +readinessProbe: + enabled: false + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + +# Pod scheduling and configuration +#nodeSelector: +# workload-type: general + +tolerations: [] + +affinity: {} + +# Labels and annotations +annotations: {} + +podLabels: {} + +podAnnotations: {} + +# Example configurations: +# +# For Bidder Emulator: +# job: +# type: "bidder" +# name: "bidder-emulator" +# rpcAddr: "localhost:8545" +# serverAddr: "localhost:13524" +# bidWorkers: 2 +# httpPort: 8080 +# +# For Provider Emulator: +# job: +# type: "provider" +# name: "provider-emulator" +# serverAddr: "localhost:13524" +# httpPort: 8080 +# relay: "relay.example.com:8080" +# errorProbability: 20 diff --git a/infrastructure/charts/mev-commit-emulator/provider-emulator-values.yaml b/infrastructure/charts/mev-commit-emulator/provider-emulator-values.yaml new file mode 100644 index 000000000..f567ebbf6 --- /dev/null +++ b/infrastructure/charts/mev-commit-emulator/provider-emulator-values.yaml @@ -0,0 +1,107 @@ +# Multi-Emulator Helm Chart Values (Bidder & Provider) + +# Global settings +namespace: default + +# Name overrides +nameOverride: "" +fullnameOverride: "" + +# Job configuration +job: + # Type of emulator: "bidder" or "provider" + type: "provider" # Change to "provider" for provider-emulator + name: "provider-emulator" # Change to "provider-emulator" for provider + replicas: 1 + + # Common flags for both emulators + serverAddr: "erigon-mev-commit-provider-mev-commit-p2p-provider.default.svc.cluster.local:13624" # Provider endpoint + #serverAddr: "mev-commit-bidder-mev-commit-p2p-bidder.default.svc.cluster.local:13724" # Bidder endpoint + httpPort: 8080 + env: + log-format: "json" # text or json + log-level: "debug" # debug, info, warn, error + log-tags: "service.name:provider-emulator,service.version:1.2.0" # Additional comma-separated log tags + otel-collector-endpoint-url: "" # OpenTelemetry collector URL + + # Bidder-specific flags (only used when type: "bidder") + rpcAddr: "http://erigon-mev-commit-mock-l1.default.svc.cluster.local:8545" + bidWorkers: 2 + + # Provider-specific flags (only used when type: "provider") + relay: "http://erigon-mev-commit-relay-emulator-mev-commit-emulator.default.svc.cluster.local:8080" # Relay address + errorProbability: 0 # Error probability (0-100) + +# Bidder/Provider emulator image +image: + repository: "primev/primev" + tag: "" # provider --> tag: "provideremulator-amd" + #tag: "bidderemulator-amd-v1" # bidderemulator-amd-v1 + pullPolicy: Always + +# Service configuration +service: + enabled: true + type: ClusterIP + port: 8080 + targetPort: 8080 + annotations: {} + +# Resource configuration +resources: + limits: + cpu: 6000m + memory: 1Gi + requests: + cpu: 250m + memory: 256Mi + +# Health checks +livenessProbe: + enabled: false + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + +readinessProbe: + enabled: false + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + +# Pod scheduling and configuration +#nodeSelector: +# workload-type: general + +tolerations: [] + +affinity: {} + +# Labels and annotations +annotations: {} + +podLabels: {} + +podAnnotations: {} + +# Example configurations: +# +# For Bidder Emulator: +# job: +# type: "bidder" +# name: "bidder-emulator" +# rpcAddr: "localhost:8545" +# serverAddr: "localhost:13524" +# bidWorkers: 2 +# httpPort: 8080 +# +# For Provider Emulator: +# job: +# type: "provider" +# name: "provider-emulator" +# serverAddr: "localhost:13524" +# httpPort: 8080 +# relay: "relay.example.com:8080" +# errorProbability: 20 diff --git a/infrastructure/charts/mev-commit-emulator/templates/_helpers.tpl b/infrastructure/charts/mev-commit-emulator/templates/_helpers.tpl new file mode 100644 index 000000000..0c264b448 --- /dev/null +++ b/infrastructure/charts/mev-commit-emulator/templates/_helpers.tpl @@ -0,0 +1,68 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "emulator.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "emulator.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "emulator.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "emulator.labels" -}} +helm.sh/chart: {{ include "emulator.chart" . }} +{{ include "emulator.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/component: {{ .Values.job.type }}-emulator +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "emulator.selectorLabels" -}} +app.kubernetes.io/name: {{ include "emulator.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Validate emulator type and required values +*/}} +{{- define "emulator.validateValues" -}} +{{/* All validation removed */}} +{{- end }} + +{{/* +Get the appropriate command based on emulator type +*/}} +{{- define "emulator.command" -}} +{{- if eq .Values.job.type "bidder" -}} +/usr/local/bin/bidder-emulator +{{- else if eq .Values.job.type "provider" -}} +/usr/local/bin/provider-emulator +{{- end -}} +{{- end }} diff --git a/infrastructure/charts/mev-commit-emulator/templates/deployment.yaml b/infrastructure/charts/mev-commit-emulator/templates/deployment.yaml new file mode 100644 index 000000000..269aeaf01 --- /dev/null +++ b/infrastructure/charts/mev-commit-emulator/templates/deployment.yaml @@ -0,0 +1,104 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "emulator.fullname" . }} + namespace: {{ .Values.namespace | default "default" }} + labels: + {{- include "emulator.labels" . | nindent 4 }} + {{- with .Values.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.job.replicas }} + selector: + matchLabels: + {{- include "emulator.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "emulator.selectorLabels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- include "emulator.validateValues" . }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: {{ .Values.job.name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + - /usr/local/bin/{{ .Values.job.type }}-emulator + args: +{{- if and (eq .Values.job.type "bidder") .Values.job.rpcAddr }} + - "-rpc-addr={{ .Values.job.rpcAddr }}" +{{- end }} +{{- if .Values.job.serverAddr }} + - "-server-addr={{ .Values.job.serverAddr }}" +{{- end }} +{{- if and (eq .Values.job.type "bidder") .Values.job.bidWorkers }} + - "-bid-workers={{ .Values.job.bidWorkers }}" +{{- end }} +{{- if .Values.job.httpPort }} + - "-http-port={{ .Values.job.httpPort }}" +{{- end }} +{{- if and (eq .Values.job.type "provider") .Values.job.relay }} + - "-relay={{ .Values.job.relay }}" +{{- end }} +{{- if and (eq .Values.job.type "provider") (hasKey .Values.job "errorProbability") }} + - "-error-probability={{ .Values.job.errorProbability }}" +{{- end }} +{{- if index .Values.job.env "log-format" }} + - "-log-fmt={{ index .Values.job.env "log-format" }}" +{{- end }} +{{- if index .Values.job.env "log-level" }} + - "-log-level={{ index .Values.job.env "log-level" }}" +{{- end }} +{{- if index .Values.job.env "log-tags" }} + - "-log-tags={{ index .Values.job.env "log-tags" }}" +{{- end }} +{{- if index .Values.job.env "otel-collector-endpoint-url" }} + - "-otel-collector-endpoint-url={{ index .Values.job.env "otel-collector-endpoint-url" }}" +{{- end }} + ports: + - containerPort: {{ .Values.job.httpPort | default 8080 }} + name: metrics + protocol: TCP + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: /metrics + port: metrics + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: /metrics + port: metrics + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + {{- end }} diff --git a/infrastructure/charts/mev-commit-emulator/templates/service.yaml b/infrastructure/charts/mev-commit-emulator/templates/service.yaml new file mode 100644 index 000000000..7249ba6c8 --- /dev/null +++ b/infrastructure/charts/mev-commit-emulator/templates/service.yaml @@ -0,0 +1,22 @@ +{{- if .Values.service.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "emulator.fullname" . }} + namespace: {{ .Values.namespace | default "default" }} + labels: + {{- include "emulator.labels" . | nindent 4 }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: {{ .Values.service.targetPort }} + protocol: TCP + name: metrics + selector: + {{- include "emulator.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/infrastructure/charts/mev-commit-erigon/values.yaml b/infrastructure/charts/mev-commit-erigon/values.yaml index 01f073ee2..650e6860d 100644 --- a/infrastructure/charts/mev-commit-erigon/values.yaml +++ b/infrastructure/charts/mev-commit-erigon/values.yaml @@ -1,18 +1,7 @@ -# Genesis Configuration - Choose ONE method only -# Method 1: Generate accounts and create genesis automatically. Genesis template is stored in the configmap -# Method 2: Download genesis from existing URL -# NOTE: Both methods cannot be used simultaneously - validation will fail if both are configured genesis: - # Method 1: Account Generation (generates new accounts and creates genesis with them) accountGeneration: - enabled: false # Set to true to enable automatic account generation - count: 5 # Number of accounts to generate and fund in genesis. These accounts will be added under alloc{} - password: "randompassword" # Password for generated account keystores - timestamp: 0x198c3a13dc8 # Timestamp for genesis block in milliseconds - - # Method 2: URL Download (downloads existing genesis file) - url: "http://example.com/genesis.json" # Set this to download genesis from URL - + enabled: false + url: "https://storage.googleapis.com/devnet-artifacts/devnet-genesis/genesis.json" erigon: networkId: 10025 @@ -38,9 +27,9 @@ erigon: enabled: true addr: "0.0.0.0" zeroFeeTxList: - - "0x0000000000000000000000000000000000000000" # Contracts Deployer Keystore - - "0x0000000000000000000000000000000000000000" # Orcale Keystore - #- "0x0000000000000000000000000000000000000000" # Bridge Relayer Keystore + - "0x421657a89f467ac04c542e46645a7752e199b5e6" # Contracts Deployer Keystore + - "0x1c533735c11dd317bc816629f86e00f479d097a3" # Orcale Keystore + #- "0x929f3b326ea399cb953c18854733d6b894b9e819" # Bridge Relayer Keystore pruneMode: "archive" nodiscover: true # Additional arguments to pass to erigon @@ -52,7 +41,7 @@ erigon: snode: instanceId: "snode1" - priorityFeeRecipient: "0x0000000000000000000000000000000000000000" + priorityFeeRecipient: "0xcEc94aeB187321262BA8Ae5BB0A9D79EAd56da92" ports: api: 9090 health: 8080 @@ -65,22 +54,22 @@ snode: jwt: - token: "" + token: "32c2902cc88b19b1c775ffef7e67673f296beb2e610f93230b40e5ef822ae9d4" image: erigon: - repository: - tag: + repository: primev/primev + tag: erigon-snode-v3.0.7-ms-patch pullPolicy: IfNotPresent snode: - repository: - tag: + repository: primev/primev + tag: amd64-erigon-v3.0.7-simplify-cl pullPolicy: IfNotPresent storage: size: 100Gi - storageClassName: premium-rwo + storageClassName: premium-rwo #erigon-hyperdisk-extreme (for testnet) accessMode: ReadWriteOnce security: @@ -149,12 +138,62 @@ service: tolerations: [] - +# Example: +# tolerations: +# - key: "erigon-dedicated" +# operator: "Equal" +# value: "true" +# effect: "NoSchedule" +# - key: "node.kubernetes.io/disk-pressure" +# operator: "Exists" +# effect: "NoSchedule" affinity: {} - +# Example: +# affinity: +# nodeAffinity: +# requiredDuringSchedulingIgnoredDuringExecution: +# nodeSelectorTerms: +# - matchExpressions: +# - key: node-type +# operator: In +# values: +# - high-memory +# preferredDuringSchedulingIgnoredDuringExecution: +# - weight: 100 +# preference: +# matchExpressions: +# - key: zone +# operator: In +# values: +# - zone-1 +# podAntiAffinity: +# preferredDuringSchedulingIgnoredDuringExecution: +# - weight: 100 +# podAffinityTerm: +# labelSelector: +# matchExpressions: +# - key: app.kubernetes.io/name +# operator: In +# values: +# - erigon-snode +# topologyKey: kubernetes.io/hostname topologySpreadConstraints: [] +# Example: +# topologySpreadConstraints: +# - maxSkew: 1 +# topologyKey: topology.kubernetes.io/zone +# whenUnsatisfiable: DoNotSchedule +# labelSelector: +# matchLabels: +# app.kubernetes.io/name: erigon-snode + + +# podDisruptionBudget: +# enabled: false +# minAvailable: 1 +# maxUnavailable: 1 # Additional labels for StatefulSet additionalLabels: {} @@ -166,40 +205,5 @@ additionalLabels: {} ingress: enabled: false - className: "nginx" - - # RPC Configuration - rpc: - host: rpc.devnet.xyz - path: / - pathType: Prefix - annotations: - nginx.ingress.kubernetes.io/backend-protocol: "HTTP" - nginx.ingress.kubernetes.io/force-ssl-redirect: "true" - nginx.ingress.kubernetes.io/proxy-body-size: "0" - nginx.ingress.kubernetes.io/proxy-read-timeout: "1200" - nginx.ingress.kubernetes.io/proxy-send-timeout: "1200" - nginx.ingress.kubernetes.io/ssl-redirect: "true" - - # WebSocket Configuration - websocket: - host: ws.devnet.xyz - path: / - pathType: Prefix - annotations: - nginx.ingress.kubernetes.io/backend-protocol: "HTTP" - nginx.ingress.kubernetes.io/force-ssl-redirect: "true" - nginx.ingress.kubernetes.io/proxy-body-size: "0" - nginx.ingress.kubernetes.io/proxy-read-timeout: "36h00" - nginx.ingress.kubernetes.io/proxy-send-timeout: "3600" - nginx.ingress.kubernetes.io/ssl-redirect: "true" - # WebSocket specific annotations - nginx.ingress.kubernetes.io/proxy-http-version: "1.1" - nginx.ingress.kubernetes.io/proxy-set-headers: | - Connection "upgrade" - Upgrade $http_upgrade - - # TLS Configuration (shared) - tls: - secretName: devnet-tls # Should be pre-created if you using tls + diff --git a/infrastructure/charts/mev-commit-geth-l1/Chart.yaml b/infrastructure/charts/mev-commit-geth-l1/Chart.yaml new file mode 100644 index 000000000..f1c621b7b --- /dev/null +++ b/infrastructure/charts/mev-commit-geth-l1/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: mev-commit-mock-l1 +description: A Helm chart for deploying mev-commit-geth-l1 service +type: application +version: 0.1.0 +appVersion: "1.0.0" diff --git a/infrastructure/charts/mev-commit-geth-l1/templates/_helpers.tpl b/infrastructure/charts/mev-commit-geth-l1/templates/_helpers.tpl new file mode 100644 index 000000000..83948e88a --- /dev/null +++ b/infrastructure/charts/mev-commit-geth-l1/templates/_helpers.tpl @@ -0,0 +1,49 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "geth-l1.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "geth-l1.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "geth-l1.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "geth-l1.labels" -}} +helm.sh/chart: {{ include "geth-l1.chart" . }} +{{ include "geth-l1.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "geth-l1.selectorLabels" -}} +app.kubernetes.io/name: {{ include "geth-l1.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/infrastructure/charts/mev-commit-geth-l1/templates/deployment.yaml b/infrastructure/charts/mev-commit-geth-l1/templates/deployment.yaml new file mode 100644 index 000000000..1c8365e8a --- /dev/null +++ b/infrastructure/charts/mev-commit-geth-l1/templates/deployment.yaml @@ -0,0 +1,221 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "geth-l1.fullname" . }} + labels: + {{- include "geth-l1.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + strategy: + type: Recreate + selector: + matchLabels: + {{- include "geth-l1.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "geth-l1.selectorLabels" . | nindent 8 }} + spec: + containers: + - name: geth + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.geth.ports.http }} + protocol: TCP + - name: ws + containerPort: {{ .Values.geth.ports.ws }} + protocol: TCP + - name: p2p + containerPort: {{ .Values.geth.ports.p2p }} + protocol: TCP + command: ["geth"] + args: + {{- if .Values.geth.dev.enabled }} + - --dev + - --dev.period={{ .Values.geth.dev.period }} + {{- end }} + - --datadir={{ .Values.geth.dataDir }} + - --http + - --http.addr=0.0.0.0 + - --http.port={{ .Values.geth.ports.http }} + - --http.api={{ .Values.geth.rpc.apis }} + - --http.corsdomain={{ .Values.geth.rpc.cors }} + - --http.vhosts={{ .Values.geth.rpc.vhosts }} + - --ws + - --ws.addr=0.0.0.0 + - --ws.port={{ .Values.geth.ports.ws }} + - --ws.api={{ .Values.geth.rpc.apis }} + - --ws.origins={{ .Values.geth.rpc.wsOrigins }} + - --networkid={{ .Values.geth.networkId }} + - --verbosity={{ .Values.geth.logging.verbosity }} + {{- if .Values.geth.allowInsecureUnlock }} + - --allow-insecure-unlock + {{- end }} + {{- if .Values.geth.passwordFile.enabled }} + - --password=/etc/geth/password.txt + {{- end }} + {{- range .Values.geth.extraArgs }} + - {{ . }} + {{- end }} + volumeMounts: + - name: geth-data + mountPath: {{ .Values.geth.dataDir }} + {{- if .Values.geth.passwordFile.enabled }} + - name: password-file + mountPath: /etc/geth + readOnly: true + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + + {{- if .Values.funding.enabled }} + - name: funding-sidecar + image: "{{ .Values.funding.image.repository }}:{{ .Values.funding.image.tag }}" + imagePullPolicy: {{ .Values.funding.image.pullPolicy }} + securityContext: + runAsUser: 0 # Run as root to access keystore + runAsGroup: 0 + env: + - name: FOUNDRY_DISABLE_NIGHTLY_WARNING + value: "true" + command: ["/bin/sh"] + args: + - -c + - | + set -e + echo "Starting funding sidecar..." + + # Wait for Geth RPC to be ready using cast rpc (no curl needed) + echo "Waiting for Geth RPC to be available..." + ATTEMPTS=0 + until cast rpc eth_blockNumber --rpc-url http://localhost:{{ .Values.geth.ports.http }} >/dev/null 2>&1; do + ATTEMPTS=$((ATTEMPTS + 1)) + echo "Attempt $ATTEMPTS: Geth RPC not ready, waiting 1 second..." + + if [ $ATTEMPTS -gt 60 ]; then + echo "Giving up after 60 attempts (1 minute)" + exit 1 + fi + sleep 1 + done + echo "Geth RPC is ready!" + echo "Geth RPC is ready!" + + # Wait for keystore directory and files to be created + echo "Waiting for keystore to be generated..." + KEYSTORE_DIR="{{ .Values.geth.dataDir }}/keystore" + + # Wait up to 5 minutes for keystore to appear + TIMEOUT=300 + ELAPSED=0 + while [ ! -d "$KEYSTORE_DIR" ] || [ -z "$(ls -A $KEYSTORE_DIR 2>/dev/null)" ]; do + if [ $ELAPSED -ge $TIMEOUT ]; then + echo "Timeout waiting for keystore generation" + exit 1 + fi + echo "Keystore not found or empty, waiting 1 second... ($ELAPSED/$TIMEOUT)" + sleep 1 + ELAPSED=$((ELAPSED + 1)) + done + + # Find the keystore file (should be only one in dev mode) + KEYSTORE_FILE=$(ls $KEYSTORE_DIR | head -1) + if [ -z "$KEYSTORE_FILE" ]; then + echo "No keystore file found" + exit 1 + fi + + echo "Found keystore file: $KEYSTORE_FILE" + KEYSTORE_PATH="$KEYSTORE_DIR/$KEYSTORE_FILE" + + # Get the funding address from keystore + FUNDING_ADDRESS=$(cast wallet address --keystore "$KEYSTORE_PATH" --password="{{ .Values.funding.keystore.password }}") + echo "Funding from address: $FUNDING_ADDRESS" + + # Check if funding address has sufficient balance + BALANCE=$(cast balance $FUNDING_ADDRESS --rpc-url http://127.0.0.1:{{ .Values.geth.ports.http }}) + echo "Funding address balance: $BALANCE wei" + + # Get initial nonce + BASE_NONCE=$(cast nonce $FUNDING_ADDRESS --rpc-url http://localhost:{{ .Values.geth.ports.http }}) + echo "Starting nonce: $BASE_NONCE" + + # Fund each address + {{- range $index, $address := .Values.funding.addresses }} + echo "Funding address {{ $index }}: {{ $address }}" + + # Check current balance of target address + TARGET_BALANCE=$(cast balance {{ $address }} --rpc-url http://localhost:{{ $.Values.geth.ports.http }}) + echo "Current balance of {{ $address }}: $TARGET_BALANCE wei" + + # Send funding transaction + TX_HASH=$(cast send {{ $address }} \ + --value {{ $.Values.funding.amount }} \ + --gas-price {{ $.Values.funding.gasPrice }} \ + --nonce $((BASE_NONCE + {{ $index }})) \ + --keystore "$KEYSTORE_PATH" \ + --password="{{ $.Values.funding.keystore.password }}" \ + --rpc-url http://localhost:{{ $.Values.geth.ports.http }}) + + if [ $? -eq 0 ]; then + echo "โœ“ Successfully funded {{ $address }} - TX: $TX_HASH" + else + echo "โœ— Failed to fund {{ $address }}" + fi + + # Wait a bit between transactions + sleep 2 + {{- end }} + + echo "Funding process completed successfully!" + + # Keep the sidecar running (optional - you can remove this if you want it to exit) + echo "Funding sidecar will keep running..." + while true; do + sleep 3600 # Sleep for 1 hour + done + volumeMounts: + - name: geth-data + mountPath: {{ .Values.geth.dataDir }} + resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 50m + memory: 64Mi + {{- end }} + + volumes: + {{- if .Values.persistence.enabled }} + - name: geth-data + persistentVolumeClaim: + claimName: {{ include "geth-l1.fullname" . }} + {{- else }} + - name: geth-data + emptyDir: {} + {{- end }} + {{- if .Values.geth.passwordFile.enabled }} + - name: password-file + secret: + secretName: {{ include "geth-l1.fullname" . }}-password + {{- end }} + + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/infrastructure/charts/mev-commit-geth-l1/templates/pvc.yaml b/infrastructure/charts/mev-commit-geth-l1/templates/pvc.yaml new file mode 100644 index 000000000..b18652988 --- /dev/null +++ b/infrastructure/charts/mev-commit-geth-l1/templates/pvc.yaml @@ -0,0 +1,17 @@ +{{- if .Values.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "geth-l1.fullname" . }} + labels: + {{- include "geth-l1.labels" . | nindent 4 }} +spec: + accessModes: + - {{ .Values.persistence.accessMode }} + resources: + requests: + storage: {{ .Values.persistence.size }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass }} + {{- end }} +{{- end }} diff --git a/infrastructure/charts/mev-commit-geth-l1/templates/secret.yaml b/infrastructure/charts/mev-commit-geth-l1/templates/secret.yaml new file mode 100644 index 000000000..f796e0e83 --- /dev/null +++ b/infrastructure/charts/mev-commit-geth-l1/templates/secret.yaml @@ -0,0 +1,11 @@ +{{- if .Values.geth.passwordFile.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "geth-l1.fullname" . }}-password + labels: + {{- include "geth-l1.labels" . | nindent 4 }} +type: Opaque +data: + password.txt: {{ .Values.geth.passwordFile.password | b64enc | quote }} +{{- end }} diff --git a/infrastructure/charts/mev-commit-geth-l1/templates/service.yaml b/infrastructure/charts/mev-commit-geth-l1/templates/service.yaml new file mode 100644 index 000000000..f3221fcab --- /dev/null +++ b/infrastructure/charts/mev-commit-geth-l1/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "geth-l1.fullname" . }} + labels: + {{- include "geth-l1.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.http.port }} + targetPort: {{ .Values.service.http.targetPort }} + protocol: TCP + name: http + - port: {{ .Values.service.ws.port }} + targetPort: {{ .Values.service.ws.targetPort }} + protocol: TCP + name: ws + selector: + {{- include "geth-l1.selectorLabels" . | nindent 4 }} diff --git a/infrastructure/charts/mev-commit-geth-l1/values.yaml b/infrastructure/charts/mev-commit-geth-l1/values.yaml new file mode 100644 index 000000000..85beefa65 --- /dev/null +++ b/infrastructure/charts/mev-commit-geth-l1/values.yaml @@ -0,0 +1,107 @@ +# values.yaml +replicaCount: 1 + +nameOverride: "" +fullnameOverride: "erigon-mev-commit-mock-l1" + +image: + repository: ethereum/client-go + tag: "v1.15.11" + pullPolicy: IfNotPresent + +# Geth configuration +geth: + + dataDir: "/data/geth" + networkId: 133787 + dev: + enabled: true + period: 12 # Block time in seconds + ports: + http: 8545 + ws: 8546 + p2p: 30303 + rpc: + apis: "eth,net,web3,debug,admin,personal,txpool" + cors: "*" + vhosts: "*" + wsOrigins: "*" + logging: + verbosity: 3 + allowInsecureUnlock: true + # Extra arguments to pass to geth command + extraArgs: [] + # - "--mine" + # - "--miner.threads=1" + # - "--gcmode=archive" + passwordFile: + enabled: true + password: "password" + +# Funding configuration +funding: + enabled: true + image: + repository: ghcr.io/foundry-rs/foundry # Image with cast tool + tag: "latest" + pullPolicy: IfNotPresent + + # Keystore configuration - uses the same password as geth + keystore: + password: "password" # Same as geth.passwordFile.password + + # Funding parameters + amount: "10000ether" + gasPrice: "5000000000" # 5 gwei + + # List of addresses to fund + addresses: + - "0x0850f7e6d406c279a54798ebf49cd28a63b2ac66" + - "0xc2d5682a51108abda388a97c438ad7dcbbe1f19e" + - "0x3d80a5769d9e9336c8b7cf8d8dc35e6372a17426" + - "0x54d45c914652c5013d10e65a7b8089aaac05acaf" + - "0xa9db89c3c31e30b5b70db0196b7b95c209ec3cbc" + - "0x9b6771c9142e5b7372bc4751ae8eb4a29770a5ad" + - "0x2e0fc95244294adaec32d7a3f022bafffa40b8bc" + - "0xee8558e130ea52dc741e9ced909e831018e85b41" + - "0x11b06c0692b4a0c578fd060ce1085f78fc535cc7" + - "0x17d53e9eb9bfafb97f6a9e9b0d1a84b705efe107" + - "0x92ac5a7a965a7de2d3791dc2d1fbcb50afb460e1" + - "0x68177dd5a3316e39eb7f800751cc0679c5d407da" + - "0x10b0a998f27837affe4962d9d84f81dd49bc29fb" + - "0xbcf0e229b5c8e48066759f29bb34670ae57ffd48" + +# Storage +persistence: + enabled: true + storageClass: "premium-rwo" + accessMode: ReadWriteOnce + size: 10Gi + +# Service configuration +service: + type: ClusterIP + http: + port: 8545 + targetPort: 8545 + ws: + port: 8546 + targetPort: 8546 + +# Resource limits +resources: + limits: + cpu: 2000m + memory: 4Gi + requests: + cpu: 500m + memory: 1Gi + +# Node selector and tolerations +#nodeSelector: +# node.kubernetes.io/instance-type: c2-standard-8 +tolerations: [] +affinity: {} + +# Pod annotations +podAnnotations: {} diff --git a/infrastructure/charts/mev-commit-oracle/Chart.yaml b/infrastructure/charts/mev-commit-oracle/Chart.yaml new file mode 100644 index 000000000..fc58ed393 --- /dev/null +++ b/infrastructure/charts/mev-commit-oracle/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: mev-commit-oracle +description: A Helm chart for deploying mev-commit-oracle service +type: application +version: 0.1.0 +appVersion: "1.0.0" diff --git a/infrastructure/charts/mev-commit-oracle/scripts/keystore-init.sh b/infrastructure/charts/mev-commit-oracle/scripts/keystore-init.sh new file mode 100644 index 000000000..ee9d4d6d9 --- /dev/null +++ b/infrastructure/charts/mev-commit-oracle/scripts/keystore-init.sh @@ -0,0 +1,349 @@ +#!/bin/sh + +set -e + +# Configuration from environment variables +KEYSTORE_PATH="${KEYSTORE_PATH:-/keystore}" +KEYSTORE_URL="${KEYSTORE_DOWNLOAD_URL}" +MAX_RETRIES="${KEYSTORE_RETRIES:-3}" +KEYSTORE_SOURCE="${KEYSTORE_SOURCE:-url}" # 'url' or 'aws' + +echo "=== MEV Oracle Keystore Initialization ===" +echo "Keystore path: $KEYSTORE_PATH" +echo "Keystore source: $KEYSTORE_SOURCE" + +# Create keystore directory if it doesn't exist +mkdir -p "$KEYSTORE_PATH" + +# Function to extract address from filename using POSIX shell +extract_address_from_filename() { + local filename="$1" + + # Check if filename matches UTC----
format + case "$filename" in + UTC--????-??-??T??-??-??.??????*Z--*) + # Extract the address part after the last -- + address_part="${filename##*--}" + # Validate address is 40 hex characters + case "$address_part" in + *[!0-9a-fA-F]* | ????????????????????????????????????) + echo "Invalid address format: $address_part" >&2 + return 1 + ;; + ????????????????????????????????????????) + echo "$address_part" + return 0 + ;; + *) + echo "Invalid address length: $address_part" >&2 + return 1 + ;; + esac + ;; + *) + echo "Invalid keystore filename format: $filename" >&2 + return 1 + ;; + esac +} + +# Function to extract address from keystore JSON content +extract_address_from_content() { + local file_path="$1" + + if command -v jq >/dev/null 2>&1; then + jq -r '.address' "$file_path" 2>/dev/null || echo "" + else + # Extract address using grep and sed (fallback) + grep -o '"address"[[:space:]]*:[[:space:]]*"[^"]*"' "$file_path" 2>/dev/null | \ + sed 's/.*"address"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' || echo "" + fi +} + +# Function to normalize address (remove 0x prefix and convert to lowercase) +normalize_address() { + local addr="$1" + # Remove 0x prefix if present and convert to lowercase + echo "${addr#0x}" | tr '[:upper:]' '[:lower:]' +} + +# Function to validate keystore file +validate_keystore() { + local file_path="$1" + local filename="$2" + + echo "Validating keystore file: $file_path" + + if [ ! -f "$file_path" ]; then + echo "Keystore file does not exist" + return 1 + fi + + # Check if file is readable and not empty + if [ ! -s "$file_path" ]; then + echo "Keystore file is empty" + return 1 + fi + + # Extract address from filename + filename_address=$(extract_address_from_filename "$filename") + if [ $? -ne 0 ]; then + echo "โœ— Invalid filename format" + return 1 + fi + + # Normalize filename address + filename_address=$(normalize_address "$filename_address") + echo "Address from filename: $filename_address" + + # Check basic JSON structure first + if command -v jq >/dev/null 2>&1; then + echo "Using jq for JSON validation" + + # Validate JSON structure + if ! jq -e '.address and .crypto and .crypto.cipher and .crypto.ciphertext and .crypto.kdf' "$file_path" >/dev/null 2>&1; then + echo "โœ— Invalid keystore JSON structure" + return 1 + fi + + # Extract address from content + content_address=$(extract_address_from_content "$file_path") + if [ -z "$content_address" ]; then + echo "โœ— Could not extract address from keystore content" + return 1 + fi + + # Normalize content address + content_address=$(normalize_address "$content_address") + echo "Address from content: $content_address" + + # Compare addresses (case-insensitive) + if [ "$filename_address" = "$content_address" ]; then + echo "โœ“ Keystore validation successful - addresses match: $filename_address" + return 0 + else + echo "โœ— Address mismatch - filename: $filename_address, content: $content_address" + return 1 + fi + else + echo "Using basic validation (jq not available)" + + # Basic validation without jq + if ! grep -q '"crypto"' "$file_path" || \ + ! grep -q '"cipher"' "$file_path" || \ + ! grep -q '"ciphertext"' "$file_path" || \ + ! grep -q '"kdf"' "$file_path"; then + echo "โœ— Basic keystore structure validation failed" + return 1 + fi + + # Extract address from content using basic tools + content_address=$(extract_address_from_content "$file_path") + if [ -z "$content_address" ]; then + echo "โœ— Could not extract address from keystore content" + return 1 + fi + + # Normalize content address + content_address=$(normalize_address "$content_address") + echo "Address from content: $content_address" + + # Compare addresses (case-insensitive) + if [ "$filename_address" = "$content_address" ]; then + echo "โœ“ Basic keystore validation successful - addresses match: $filename_address" + return 0 + else + echo "โœ— Address mismatch - filename: $filename_address, content: $content_address" + return 1 + fi + fi +} + +# Function to download keystore with retries (URL mode) +download_keystore() { + local url="$1" + local output_path="$2" + local retries=0 + + echo "Downloading keystore from: $url" + + # Download with retries + while [ $retries -lt $MAX_RETRIES ]; do + echo "Download attempt $((retries + 1))/$MAX_RETRIES" + + if curl -fsSL --connect-timeout 30 --max-time 300 -o "$output_path" "$url"; then + echo "โœ“ Download successful" + return 0 + else + retries=$((retries + 1)) + if [ $retries -lt $MAX_RETRIES ]; then + echo "Download failed, retrying in 5 seconds..." + sleep 5 + else + echo "โœ— Download failed after $MAX_RETRIES attempts" + return 1 + fi + fi + done +} + +# Function to get filename from URL +get_filename_from_url() { + local url="$1" + basename "$url" +} + +# Function to handle AWS Secrets Manager source +handle_aws_source() { + echo "Using AWS Secrets Manager source..." + + # Read keystore content and filename from External Secret + local keystore_content_file="/secrets/temp_keystore.json" + local filename_file="/secrets/filename.txt" + + # Validate that secret files exist + if [ ! -f "$keystore_content_file" ]; then + echo "โœ— Keystore content file not found: $keystore_content_file" + exit 1 + fi + + if [ ! -f "$filename_file" ]; then + echo "โœ— Filename file not found: $filename_file" + exit 1 + fi + + # Read the expected filename + local expected_filename=$(cat "$filename_file") + local keystore_file_path="$KEYSTORE_PATH/$expected_filename" + + echo "Expected filename: $expected_filename" + + # Validate filename format before proceeding + if ! extract_address_from_filename "$expected_filename" >/dev/null 2>&1; then + echo "โœ— Invalid filename format: $expected_filename" + echo "Expected format: UTC----
" + exit 1 + fi + + # Check if keystore already exists and is valid + if [ -f "$keystore_file_path" ]; then + if validate_keystore "$keystore_file_path" "$expected_filename"; then + echo "โœ“ Valid keystore already exists - initialization complete" + return 0 + else + echo "Removing invalid keystore file..." + rm -f "$keystore_file_path" + fi + fi + + # Copy keystore content to final location + echo "Setting up keystore from External Secret..." + cp "$keystore_content_file" "$keystore_file_path" + + # Validate the keystore + if ! validate_keystore "$keystore_file_path" "$expected_filename"; then + echo "โœ— Keystore validation failed" + rm -f "$keystore_file_path" + exit 1 + fi + + # Set proper permissions + chmod 600 "$keystore_file_path" + + # Create a checksum file for integrity checking + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$keystore_file_path" > "$KEYSTORE_PATH/.keystore.checksum" + echo "โœ“ Checksum file created" + fi + + # Extract final address for logging + local final_address=$(extract_address_from_filename "$expected_filename") + echo "โœ“ AWS keystore initialization completed successfully" + echo "โœ“ Keystore file: $keystore_file_path" + echo "โœ“ Address: $final_address" +} + +# Function to handle URL download source +handle_url_source() { + echo "Using URL download source..." + + # Validate required environment variables + if [ -z "$KEYSTORE_URL" ]; then + echo "Error: KEYSTORE_DOWNLOAD_URL is required for URL source" + exit 1 + fi + + echo "Download URL: $KEYSTORE_URL" + + local temp_download="/tmp/keystore_temp.json" + + # Get expected filename from URL + local expected_filename=$(get_filename_from_url "$KEYSTORE_URL") + local keystore_file_path="$KEYSTORE_PATH/$expected_filename" + + echo "Expected filename: $expected_filename" + + # Validate filename format before proceeding + if ! extract_address_from_filename "$expected_filename" >/dev/null 2>&1; then + echo "โœ— Invalid filename format in URL: $expected_filename" + echo "Expected format: UTC----
" + exit 1 + fi + + # Check if keystore already exists and is valid + if [ -f "$keystore_file_path" ]; then + if validate_keystore "$keystore_file_path" "$expected_filename"; then + echo "โœ“ Valid keystore already exists - initialization complete" + return 0 + else + echo "Removing invalid keystore file..." + rm -f "$keystore_file_path" + fi + fi + + # Download keystore to temporary location first + if ! download_keystore "$KEYSTORE_URL" "$temp_download"; then + echo "โœ— Failed to download keystore" + exit 1 + fi + + # Validate downloaded keystore before moving it to final location + if ! validate_keystore "$temp_download" "$expected_filename"; then + echo "โœ— Downloaded keystore validation failed" + rm -f "$temp_download" + exit 1 + fi + + # Move validated keystore to final location + mv "$temp_download" "$keystore_file_path" + + # Set proper permissions + chmod 600 "$keystore_file_path" + + # Create a checksum file for integrity checking + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$keystore_file_path" > "$KEYSTORE_PATH/.keystore.checksum" + echo "โœ“ Checksum file created" + fi + + # Extract final address for logging + local final_address=$(extract_address_from_filename "$expected_filename") + echo "โœ“ URL keystore initialization completed successfully" + echo "โœ“ Keystore file: $keystore_file_path" + echo "โœ“ Address: $final_address" +} + +# Main logic - route to appropriate handler based on source +case "$KEYSTORE_SOURCE" in + "aws") + handle_aws_source + ;; + "url") + handle_url_source + ;; + *) + echo "โœ— Invalid KEYSTORE_SOURCE: $KEYSTORE_SOURCE" + echo "Valid values: 'aws' or 'url'" + exit 1 + ;; +esac diff --git a/infrastructure/charts/mev-commit-oracle/templates/_helpers.tpl b/infrastructure/charts/mev-commit-oracle/templates/_helpers.tpl new file mode 100644 index 000000000..6cd2a613c --- /dev/null +++ b/infrastructure/charts/mev-commit-oracle/templates/_helpers.tpl @@ -0,0 +1,87 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "mev-oracle.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "mev-oracle.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "mev-oracle.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "mev-oracle.labels" -}} +helm.sh/chart: {{ include "mev-oracle.chart" . }} +{{ include "mev-oracle.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "mev-oracle.selectorLabels" -}} +app.kubernetes.io/name: {{ include "mev-oracle.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the keystore secret name +*/}} +{{- define "mev-oracle.keystoreSecretName" -}} +{{- if .Values.keystore.existingSecret }} +{{- .Values.keystore.existingSecret }} +{{- else }} +{{- include "mev-oracle.fullname" . }}-keystore +{{- end }} +{{- end }} + +{{/* +Create the auth token secret name +*/}} +{{- define "mev-oracle.authTokenSecretName" -}} +{{- include "mev-oracle.fullname" . }}-auth-token +{{- end }} + +{{/* +Validate required configuration +*/}} +{{- define "mev-oracle.validateConfig" -}} +{{- if not .Values.oracle.authToken }} +{{- fail "oracle.authToken is required" }} +{{- end }} +{{- if not .Values.keystore.existingSecret }} +{{- if not .Values.keystore.password }} +{{- fail "keystore.password is required when not using existingSecret" }} +{{- end }} +{{- end }} +{{- if not .Values.postgresql.host }} +{{- fail "postgresql.host is required" }} +{{- end }} +{{- if not .Values.postgresql.password }} +{{- fail "postgresql.password is required" }} +{{- end }} +{{- end }} diff --git a/infrastructure/charts/mev-commit-oracle/templates/configmap-init.yaml b/infrastructure/charts/mev-commit-oracle/templates/configmap-init.yaml new file mode 100644 index 000000000..aaf2a946a --- /dev/null +++ b/infrastructure/charts/mev-commit-oracle/templates/configmap-init.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "mev-oracle.fullname" . }}-init + labels: + app.kubernetes.io/name: {{ include "mev-oracle.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: init + {{- include "mev-oracle.labels" . | nindent 4 }} +data: + keystore-init.sh: |- + {{- .Files.Get "scripts/keystore-init.sh" | nindent 4 }} diff --git a/infrastructure/charts/mev-commit-oracle/templates/configmap.yaml b/infrastructure/charts/mev-commit-oracle/templates/configmap.yaml new file mode 100644 index 000000000..cbfbfdfff --- /dev/null +++ b/infrastructure/charts/mev-commit-oracle/templates/configmap.yaml @@ -0,0 +1,66 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "mev-oracle.fullname" . }} + labels: + {{- include "mev-oracle.labels" . | nindent 4 }} +data: + # Logging configuration + MEV_ORACLE_LOG_FMT: {{ .Values.oracle.logFormat | quote }} + MEV_ORACLE_LOG_LEVEL: {{ .Values.oracle.logLevel | quote }} + {{- if .Values.oracle.logTags }} + MEV_ORACLE_LOG_TAGS: {{ .Values.oracle.logTags | quote }} + {{- end }} + + # HTTP port + MEV_ORACLE_HTTP_PORT: {{ .Values.oracle.httpPort | quote }} + + # L1 RPC URLs (comma-separated) + MEV_ORACLE_L1_RPC_URLS: {{ join "," .Values.network.l1RpcUrls | quote }} + + # Settlement RPC URLs + MEV_ORACLE_SETTLEMENT_RPC_URL_HTTP: {{ .Values.network.settlementRpcHttp | quote }} + MEV_ORACLE_SETTLEMENT_RPC_URL_WS: {{ .Values.network.settlementRpcWs | quote }} + + # Contract addresses + {{- if .Values.contracts.oracle }} + MEV_ORACLE_ORACLE_CONTRACT_ADDR: {{ .Values.contracts.oracle | quote }} + {{- end }} + {{- if .Values.contracts.preconf }} + MEV_ORACLE_PRECONF_CONTRACT_ADDR: {{ .Values.contracts.preconf | quote }} + {{- end }} + {{- if .Values.contracts.blockTracker }} + MEV_ORACLE_BLOCKTRACKER_CONTRACT_ADDR: {{ .Values.contracts.blockTracker | quote }} + {{- end }} + {{- if .Values.contracts.bidderRegistry }} + MEV_ORACLE_BIDDERREGISTRY_CONTRACT_ADDR: {{ .Values.contracts.bidderRegistry | quote }} + {{- end }} + {{- if .Values.contracts.providerRegistry }} + MEV_ORACLE_PROVIDERREGISTRY_CONTRACT_ADDR: {{ .Values.contracts.providerRegistry | quote }} + {{- end }} + + # PostgreSQL configuration + MEV_ORACLE_PG_HOST: {{ .Values.postgresql.host | quote }} + MEV_ORACLE_PG_PORT: {{ .Values.postgresql.port | quote }} + MEV_ORACLE_PG_USER: {{ .Values.postgresql.username | quote }} + MEV_ORACLE_PG_DBNAME: {{ .Values.postgresql.database | quote }} + + # Other oracle configuration + MEV_ORACLE_LAGGERD_MODE: {{ .Values.oracle.laggerdMode | quote }} + {{- if .Values.oracle.gasLimit }} + MEV_COMMIT_GAS_LIMIT: {{ .Values.oracle.gasLimit | quote }} + {{- end }} + {{- if .Values.oracle.gasTipCap }} + MEV_COMMIT_GAS_TIP_CAP: {{ .Values.oracle.gasTipCap | quote }} + {{- end }} + {{- if .Values.oracle.gasFeeCap }} + MEV_COMMIT_GAS_FEE_CAP: {{ .Values.oracle.gasFeeCap | quote }} + {{- end }} + + # Relay URLs (comma-separated) + {{- if .Values.network.relayUrls }} + MEV_ORACLE_RELAY_URLS: {{ join "," .Values.network.relayUrls | quote }} + {{- end }} + + # Keystore path + MEV_ORACLE_KEYSTORE_PATH: {{ .Values.keystore.path | quote }} diff --git a/infrastructure/charts/mev-commit-oracle/templates/deployment.yaml b/infrastructure/charts/mev-commit-oracle/templates/deployment.yaml new file mode 100644 index 000000000..c2c940b4a --- /dev/null +++ b/infrastructure/charts/mev-commit-oracle/templates/deployment.yaml @@ -0,0 +1,143 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "mev-oracle.fullname" . }} + labels: + {{- include "mev-oracle.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.replicaCount }} + strategy: + type: Recreate + selector: + matchLabels: + {{- include "mev-oracle.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + checksum/scripts: {{ .Files.Get "scripts/keystore-init.sh" | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "mev-oracle.selectorLabels" . | nindent 8 }} + spec: + initContainers: + - name: keystore-init + {{- if .Values.keystore.awsEnabled }} + image: curlimages/curl:8.5.0 + {{- else }} + image: "alpine/curl:8.5.0" + {{- end }} + imagePullPolicy: IfNotPresent + env: + - name: KEYSTORE_PATH + value: "{{ .Values.keystore.path }}" + - name: KEYSTORE_RETRIES + value: "{{ .Values.keystore.retries | default "3" }}" + {{- if .Values.keystore.awsEnabled }} + - name: KEYSTORE_SOURCE + value: "aws" + {{- else }} + - name: KEYSTORE_SOURCE + value: "url" + - name: KEYSTORE_DOWNLOAD_URL + value: "{{ .Values.keystore.downloadUrl }}" + {{- end }} + command: ["/bin/sh"] + args: ["/scripts/keystore-init.sh"] + volumeMounts: + - name: keystore-shared + mountPath: {{ .Values.keystore.path }} + - name: init-scripts + mountPath: /scripts + readOnly: true + {{- if .Values.keystore.awsEnabled }} + - name: keystore-secret + mountPath: /secrets + readOnly: true + {{- end }} + securityContext: + runAsUser: 0 + runAsGroup: 0 + runAsNonRoot: false + containers: + - name: oracle + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: http + containerPort: {{ .Values.oracle.httpPort }} + protocol: TCP + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + env: + - name: MEV_ORACLE_PG_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "mev-oracle.fullname" . }}-postgresql + key: MEV_ORACLE_PG_PASSWORD + - name: MEV_ORACLE_REGISTER_PROVIDER_API_AUTH_TOKEN + valueFrom: + secretKeyRef: + name: {{ include "mev-oracle.authTokenSecretName" . }} + key: MEV_ORACLE_REGISTER_PROVIDER_API_AUTH_TOKEN + - name: MEV_ORACLE_KEYSTORE_PASSWORD + valueFrom: + secretKeyRef: + {{- if .Values.keystore.awsEnabled }} + name: {{ include "mev-oracle.fullname" . }}-keystore + key: password.txt + {{- else }} + name: {{ include "mev-oracle.keystoreSecretName" . }} + key: MEV_ORACLE_KEYSTORE_PASSWORD + {{- end }} + envFrom: + - configMapRef: + name: {{ include "mev-oracle.fullname" . }} + volumeMounts: + - name: keystore-shared + mountPath: {{ .Values.keystore.path }} + readOnly: false + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumes: + - name: keystore-shared + persistentVolumeClaim: + claimName: {{ .Values.keystore.persistentVolumeClaim }} + - name: init-scripts + configMap: + name: {{ include "mev-oracle.fullname" . }}-init + defaultMode: 0755 + {{- if .Values.keystore.awsEnabled }} + - name: keystore-secret + secret: + secretName: {{ include "mev-oracle.fullname" . }}-keystore + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/infrastructure/charts/mev-commit-oracle/templates/externalsecret.yaml b/infrastructure/charts/mev-commit-oracle/templates/externalsecret.yaml new file mode 100644 index 000000000..ba1e40dea --- /dev/null +++ b/infrastructure/charts/mev-commit-oracle/templates/externalsecret.yaml @@ -0,0 +1,32 @@ +{{- if and .Values.keystore.awsEnabled .Values.global.externalSecrets.enabled }} +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ include "mev-oracle.fullname" . }}-keystore + labels: + {{- include "mev-oracle.labels" . | nindent 4 }} + annotations: + helm.sh/hook: pre-install,pre-upgrade + helm.sh/hook-weight: "-2" +spec: + refreshInterval: {{ .Values.keystore.refreshInterval | default "12h" }} + secretStoreRef: + name: {{ .Values.global.externalSecrets.secretStore }} + kind: {{ .Values.global.externalSecrets.secretStoreKind | default "ClusterSecretStore" }} + target: + name: {{ include "mev-oracle.fullname" . }}-keystore + creationPolicy: Owner + data: + - secretKey: temp_keystore.json + remoteRef: + key: {{ .Values.keystore.awsSecretKey | default (printf "%s-keystore" (include "mev-oracle.fullname" .)) }} + property: {{ .Values.keystore.properties.keystore | default "mev_oracle_keystore" }} + - secretKey: filename.txt + remoteRef: + key: {{ .Values.keystore.awsSecretKey | default (printf "%s-keystore" (include "mev-oracle.fullname" .)) }} + property: {{ .Values.keystore.properties.keystoreFilename | default "mev_oracle_keystore_filename" }} + - secretKey: password.txt + remoteRef: + key: {{ .Values.keystore.awsSecretKey | default (printf "%s-keystore" (include "mev-oracle.fullname" .)) }} + property: {{ .Values.keystore.properties.keystorePassword | default "mev_oracle_keystore_password" }} +{{- end }} diff --git a/infrastructure/charts/mev-commit-oracle/templates/pvc.yaml b/infrastructure/charts/mev-commit-oracle/templates/pvc.yaml new file mode 100644 index 000000000..6f79854aa --- /dev/null +++ b/infrastructure/charts/mev-commit-oracle/templates/pvc.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ .Values.keystore.persistentVolumeClaim }} + labels: + {{- include "mev-oracle.labels" . | nindent 4 }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.keystore.size | default "1Gi" }} + storageClassName: {{ .Values.keystore.storageClassName | quote }} diff --git a/infrastructure/charts/mev-commit-oracle/templates/secret.yaml b/infrastructure/charts/mev-commit-oracle/templates/secret.yaml new file mode 100644 index 000000000..3defe544a --- /dev/null +++ b/infrastructure/charts/mev-commit-oracle/templates/secret.yaml @@ -0,0 +1,36 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "mev-oracle.fullname" . }}-postgresql + labels: + {{- include "mev-oracle.labels" . | nindent 4 }} +type: Opaque +data: + # PostgreSQL password + MEV_ORACLE_PG_PASSWORD: {{ .Values.postgresql.password | b64enc | quote }} + +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "mev-oracle.authTokenSecretName" . }} + labels: + {{- include "mev-oracle.labels" . | nindent 4 }} +type: Opaque +data: + # Oracle API authentication token + MEV_ORACLE_REGISTER_PROVIDER_API_AUTH_TOKEN: {{ .Values.oracle.authToken | b64enc | quote }} + +{{- if and (not .Values.keystore.awsEnabled) (not .Values.keystore.existingSecret) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "mev-oracle.keystoreSecretName" . }} + labels: + {{- include "mev-oracle.labels" . | nindent 4 }} +type: Opaque +data: + # Keystore password (only created when AWS is disabled) + MEV_ORACLE_KEYSTORE_PASSWORD: {{ .Values.keystore.defaultPassword | default "changeme" | b64enc | quote }} +{{- end }} diff --git a/infrastructure/charts/mev-commit-oracle/templates/service.yaml b/infrastructure/charts/mev-commit-oracle/templates/service.yaml new file mode 100644 index 000000000..a1937c210 --- /dev/null +++ b/infrastructure/charts/mev-commit-oracle/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "mev-oracle.fullname" . }} + labels: + {{- include "mev-oracle.labels" . | nindent 4 }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + name: http + selector: + {{- include "mev-oracle.selectorLabels" . | nindent 4 }} diff --git a/infrastructure/charts/mev-commit-oracle/values.yaml b/infrastructure/charts/mev-commit-oracle/values.yaml new file mode 100644 index 000000000..b52a13e0a --- /dev/null +++ b/infrastructure/charts/mev-commit-oracle/values.yaml @@ -0,0 +1,98 @@ +nameOverride: "erigon-oracle" +fullnameOverride: "erigon-oracle" + +# Deployment settings +replicaCount: 1 + +image: + repository: primev/primev + pullPolicy: Always + tag: "" + +# Pod annotations for monitoring +podAnnotations: + prometheus.io/scrape: "true" + prometheus.io/port: "8080" + prometheus.io/path: "/metrics" + +# Service configuration +service: + type: ClusterIP + port: 8080 + annotations: {} + +# Resource allocation +resources: + limits: + cpu: 4000m + memory: 12Gi + requests: + cpu: 2000m + memory: 4Gi + +# Oracle configuration - matching your existing setup +oracle: + logLevel: "info" + logFormat: "json" + logTags: "env:production,service:mev-oracle" + httpPort: 8080 + gasLimit: "3000000" + gasTipCap: "100000000" # 0.1 gwei + gasFeeCap: "120000000" # 0.12 gwei + laggerdMode: 12 + authToken: "4af6db58d4c783d76f5af8e8ad806f9c4960c5323793b48632e042a9693770a8" + +# Network configuration +network: + l1RpcUrls: + - "http://erigon-mev-commit-mock-l1.default.svc.cluster.local:8545" + settlementRpcHttp: "http://35.227.95.89:8545" + settlementRpcWs: "ws://35.227.95.89:8546" + relayUrls: + - "http://erigon-mev-commit-relay-emulator-mev-commit-emulator.default.svc.cluster.local:8080" + +# Contract addresses - matching your existing deployment +contracts: + bidderRegistry: "0xD91baDBc6017FC9307c120F75594de02d91197b1" + blockTracker: "0x7B1037E6d4104Ff4586F17468A5BBaE399ff3eC1" + oracle: "0x71F4F1bbE4F5c6b992D9B88dE75E55516bB8CEaf" + preconf: "0xA3fC4897E0a8C1762dcE1Da0d218590354b7dCbd" + providerRegistry: "0x9c281A0f040DeA51037f88693340Cd5F5354dEB5" + +# KEYSTORE CONFIGURATION - URL DOWNLOAD MODE (like your existing setup) +keystore: + path: "/keystore" + + # URL download mode (awsEnabled: false) + awsEnabled: false # Using URL download like your existing setup + downloadUrl: "https://storage.googleapis.com/devnet-artifacts/keystores/erigon-keystores/keystore2/UTC--2025-06-24T18-20-44.554617000Z--1c533735c11dd317bc816629f86e00f479d097a3" + retries: 3 + defaultPassword: "CXBMf4xEDO0I" # Your existing keystore password + + # Storage configuration + persistentVolumeClaim: erigon-mev-commit-oracle + size: 10Mi + storageClassName: "standard" + + # AWS configuration (not used when awsEnabled: false) + refreshInterval: "12h" + awsSecretKey: "testenv/testnet/mev-commit" + properties: + keystore: "oracle_keystore" + keystoreFilename: "oracle_keystore_filename" + keystorePassword: "oracle_keystore_password" + +# External PostgreSQL configuration - matching your existing setup +postgresql: + database: mev_oracle + host: oracle-devnet-postgresql.default.svc.cluster.local + password: mev_oracle_password + port: 5432 + username: mev_oracle + +# Global configuration - DISABLED for URL download mode +global: + externalSecrets: + enabled: false # Disabled since we're using URL download + secretStore: "aws-cluster-secret-store" + secretStoreKind: "ClusterSecretStore" diff --git a/infrastructure/charts/mev-commit-p2p/Chart.yaml b/infrastructure/charts/mev-commit-p2p/Chart.yaml new file mode 100644 index 000000000..307127f9b --- /dev/null +++ b/infrastructure/charts/mev-commit-p2p/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: mev-commit-p2p +description: A Helm chart for deploying mev-commit-p2p service +type: application +version: 0.1.0 +appVersion: "1.0.0" diff --git a/infrastructure/charts/mev-commit-p2p/bootnode-values.yaml b/infrastructure/charts/mev-commit-p2p/bootnode-values.yaml new file mode 100644 index 000000000..535495ea6 --- /dev/null +++ b/infrastructure/charts/mev-commit-p2p/bootnode-values.yaml @@ -0,0 +1,89 @@ +# Override values specific to bidder deployment + +# Global overrides for bidder +global: + # TLS configuration - disabled for bidder + tls: + disabled: false + selfSigned: true + + contracts: + bidderRegistry: "0x00000000000000000000000000000000000000" + blockTracker: "0x00000000000000000000000000000000000000" + # oracle: "0x00000000000000000000000000000000000000" + preconfStore: "0x00000000000000000000000000000000000000" + providerRegistry: "0x00000000000000000000000000000000000000" + + + # RPC endpoints + rpc: + settlementEndpoint: "" + settlementWsEndpoint: "" + l1Endpoint: "" # mock L1 + + +node: + type: "bootnode" + replicas: 1 + + ports: + http: 13723 + p2p: 13522 + rpc: 13724 + + service: + type: LoadBalancer + annotations: {} + + # Use the actual passwords as default values + defaultKeystorePassword: "CXBMf4xEDO0I" + defaultP2PSecret: "mev-commit-bidder-secret" + + resources: + limits: + cpu: 6000m + memory: 12Gi + requests: + cpu: 280m + memory: 1Gi + + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + runAsNonRoot: false + runAsUser: 0 + + podSecurityContext: + fsGroup: 0 + + nodeSelector: {} + tolerations: [] + affinity: {} + podAnnotations: {} + imagePullSecrets: [] + +# Bidder-specific configuration +bidder: + bidTimeout: "60s" + autodeposit: + enabled: false + amount: "" + providerWhitelist: "" + +# Keystore configuration - KEEP AWS SM for keystore files +keystore: + path: "/keystore" + downloadUrl: "https://storage.googleapis.com/devnet-artifacts/keystores/erigon-keystores/keystore3/UTC--2025-06-24T18-20-45.217640000Z--b3de169055dc89aa8c5f03fdf07621bd371888e3" + + # AWS Secrets Manager keystore (still enabled for keystore files) + awsEnabled: false # Keep AWS SM for keystore files only + awsSecretKey: "testenv/testnet/mev-commit" + refreshInterval: "12h" + properties: + keystore: "bootnode1_keystore" + keystoreFilename: "bootnode1_keystore_filename" + +#nodeSelector: +# workload-type: general diff --git a/infrastructure/charts/mev-commit-p2p/scripts/keystore-init.sh b/infrastructure/charts/mev-commit-p2p/scripts/keystore-init.sh new file mode 100644 index 000000000..5004db680 --- /dev/null +++ b/infrastructure/charts/mev-commit-p2p/scripts/keystore-init.sh @@ -0,0 +1,257 @@ +#!/bin/sh + +set -e + +# Configuration from environment variables +KEYSTORE_PATH="${KEYSTORE_PATH:-/keystore}" +KEYSTORE_URL="${KEYSTORE_DOWNLOAD_URL}" +MAX_RETRIES="${KEYSTORE_RETRIES:-3}" + +echo "=== MEV-Commit Keystore Initialization ===" +echo "Keystore path: $KEYSTORE_PATH" +echo "Download URL: $KEYSTORE_URL" + +# Validate required environment variables +if [ -z "$KEYSTORE_URL" ]; then + echo "Error: KEYSTORE_DOWNLOAD_URL is required" + exit 1 +fi + +# Create keystore directory if it doesn't exist +mkdir -p "$KEYSTORE_PATH" + +# Function to extract address from filename using POSIX shell +extract_address_from_filename() { + local filename="$1" + + # Check if filename matches UTC----
format + case "$filename" in + UTC--????-??-??T??-??-??.??????*Z--*) + # Extract the address part after the last -- + address_part="${filename##*--}" + # Validate address is 40 hex characters + case "$address_part" in + *[!0-9a-fA-F]* | ????????????????????????????????????) + echo "Invalid address format: $address_part" >&2 + return 1 + ;; + ????????????????????????????????????????) + echo "$address_part" + return 0 + ;; + *) + echo "Invalid address length: $address_part" >&2 + return 1 + ;; + esac + ;; + *) + echo "Invalid keystore filename format: $filename" >&2 + return 1 + ;; + esac +} + +# Function to extract address from keystore JSON content +extract_address_from_content() { + local file_path="$1" + + if command -v jq >/dev/null 2>&1; then + jq -r '.address' "$file_path" 2>/dev/null || echo "" + else + # Extract address using grep and sed (fallback) + grep -o '"address"[[:space:]]*:[[:space:]]*"[^"]*"' "$file_path" 2>/dev/null | \ + sed 's/.*"address"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/' || echo "" + fi +} + +# Function to normalize address (remove 0x prefix and convert to lowercase) +normalize_address() { + local addr="$1" + # Remove 0x prefix if present and convert to lowercase + echo "${addr#0x}" | tr '[:upper:]' '[:lower:]' +} + +# Function to validate keystore file +validate_keystore() { + local file_path="$1" + local filename="$2" + + echo "Validating keystore file: $file_path" + + if [ ! -f "$file_path" ]; then + echo "Keystore file does not exist" + return 1 + fi + + # Check if file is readable and not empty + if [ ! -s "$file_path" ]; then + echo "Keystore file is empty" + return 1 + fi + + # Extract address from filename + filename_address=$(extract_address_from_filename "$filename") + if [ $? -ne 0 ]; then + echo "โœ— Invalid filename format" + return 1 + fi + + # Normalize filename address + filename_address=$(normalize_address "$filename_address") + echo "Address from filename: $filename_address" + + # Check basic JSON structure first + if command -v jq >/dev/null 2>&1; then + echo "Using jq for JSON validation" + + # Validate JSON structure + if ! jq -e '.address and .crypto and .crypto.cipher and .crypto.ciphertext and .crypto.kdf' "$file_path" >/dev/null 2>&1; then + echo "โœ— Invalid keystore JSON structure" + return 1 + fi + + # Extract address from content + content_address=$(extract_address_from_content "$file_path") + if [ -z "$content_address" ]; then + echo "โœ— Could not extract address from keystore content" + return 1 + fi + + # Normalize content address + content_address=$(normalize_address "$content_address") + echo "Address from content: $content_address" + + # Compare addresses (case-insensitive) + if [ "$filename_address" = "$content_address" ]; then + echo "โœ“ Keystore validation successful - addresses match: $filename_address" + return 0 + else + echo "โœ— Address mismatch - filename: $filename_address, content: $content_address" + return 1 + fi + else + echo "Using basic validation (jq not available)" + + # Basic validation without jq + if ! grep -q '"crypto"' "$file_path" || \ + ! grep -q '"cipher"' "$file_path" || \ + ! grep -q '"ciphertext"' "$file_path" || \ + ! grep -q '"kdf"' "$file_path"; then + echo "โœ— Basic keystore structure validation failed" + return 1 + fi + + # Extract address from content using basic tools + content_address=$(extract_address_from_content "$file_path") + if [ -z "$content_address" ]; then + echo "โœ— Could not extract address from keystore content" + return 1 + fi + + # Normalize content address + content_address=$(normalize_address "$content_address") + echo "Address from content: $content_address" + + # Compare addresses (case-insensitive) + if [ "$filename_address" = "$content_address" ]; then + echo "โœ“ Basic keystore validation successful - addresses match: $filename_address" + return 0 + else + echo "โœ— Address mismatch - filename: $filename_address, content: $content_address" + return 1 + fi + fi +} + +# Function to download keystore with retries +download_keystore() { + local url="$1" + local output_path="$2" + local retries=0 + + echo "Downloading keystore from: $url" + + # Download with retries + while [ $retries -lt $MAX_RETRIES ]; do + echo "Download attempt $((retries + 1))/$MAX_RETRIES" + + if curl -fsSL --connect-timeout 30 --max-time 300 -o "$output_path" "$url"; then + echo "โœ“ Download successful" + return 0 + else + retries=$((retries + 1)) + if [ $retries -lt $MAX_RETRIES ]; then + echo "Download failed, retrying in 5 seconds..." + sleep 5 + else + echo "โœ— Download failed after $MAX_RETRIES attempts" + return 1 + fi + fi + done +} + +# Function to get filename from URL +get_filename_from_url() { + local url="$1" + basename "$url" +} + +# Main logic +TEMP_DOWNLOAD="/tmp/keystore_temp.json" + +# Get expected filename from URL +EXPECTED_FILENAME=$(get_filename_from_url "$KEYSTORE_URL") +KEYSTORE_FILE_PATH="$KEYSTORE_PATH/$EXPECTED_FILENAME" + +echo "Expected filename: $EXPECTED_FILENAME" + +# Validate filename format before proceeding +if ! extract_address_from_filename "$EXPECTED_FILENAME" >/dev/null 2>&1; then + echo "โœ— Invalid filename format in URL: $EXPECTED_FILENAME" + echo "Expected format: UTC----
" + exit 1 +fi + +# Check if keystore already exists and is valid +if [ -f "$KEYSTORE_FILE_PATH" ]; then + if validate_keystore "$KEYSTORE_FILE_PATH" "$EXPECTED_FILENAME"; then + echo "โœ“ Valid keystore already exists - initialization complete" + exit 0 + else + echo "Removing invalid keystore file..." + rm -f "$KEYSTORE_FILE_PATH" + fi +fi + +# Download keystore to temporary location first +if ! download_keystore "$KEYSTORE_URL" "$TEMP_DOWNLOAD"; then + echo "โœ— Failed to download keystore" + exit 1 +fi + +# Validate downloaded keystore before moving it to final location +if ! validate_keystore "$TEMP_DOWNLOAD" "$EXPECTED_FILENAME"; then + echo "โœ— Downloaded keystore validation failed" + rm -f "$TEMP_DOWNLOAD" + exit 1 +fi + +# Move validated keystore to final location +mv "$TEMP_DOWNLOAD" "$KEYSTORE_FILE_PATH" + +# Set proper permissions +chmod 600 "$KEYSTORE_FILE_PATH" + +# Create a checksum file for integrity checking +if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$KEYSTORE_FILE_PATH" > "$KEYSTORE_PATH/.keystore.checksum" + echo "โœ“ Checksum file created" +fi + +# Extract final address for logging +FINAL_ADDRESS=$(extract_address_from_filename "$EXPECTED_FILENAME") +echo "โœ“ Keystore initialization completed successfully" +echo "โœ“ Keystore file: $KEYSTORE_FILE_PATH" +echo "โœ“ Address: $FINAL_ADDRESS" diff --git a/infrastructure/charts/mev-commit-p2p/templates/_helpers.tpl b/infrastructure/charts/mev-commit-p2p/templates/_helpers.tpl new file mode 100644 index 000000000..499eaa5a3 --- /dev/null +++ b/infrastructure/charts/mev-commit-p2p/templates/_helpers.tpl @@ -0,0 +1,75 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "mev-commit-p2p.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "mev-commit-p2p.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "mev-commit-p2p.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "mev-commit-p2p.labels" -}} +helm.sh/chart: {{ include "mev-commit-p2p.chart" . }} +{{ include "mev-commit-p2p.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "mev-commit-p2p.selectorLabels" -}} +app.kubernetes.io/name: {{ include "mev-commit-p2p.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Define TLS secret name based on settings +*/}} +{{- define "mev-commit-p2p.tlsSecretName" -}} +{{- if .Values.global.tls.selfSigned -}} +{{ include "mev-commit-p2p.fullname" . }}-tls +{{- else if .Values.global.tls.existingSecret -}} +{{ .Values.global.tls.existingSecret }} +{{- else -}} +{{ include "mev-commit-p2p.fullname" . }}-tls +{{- end -}} +{{- end }} + +{{/* +Generate self-signed certificate if enabled +*/}} +{{- define "mev-commit-p2p.generateCertificate" -}} +{{- $altNames := list (printf "%s-%s" (include "mev-commit-p2p.fullname" .) .Values.node.type) (printf "%s-%s.%s" (include "mev-commit-p2p.fullname" .) .Values.node.type .Release.Namespace) (printf "%s-%s.%s.svc" (include "mev-commit-p2p.fullname" .) .Values.node.type .Release.Namespace) -}} +{{- $ca := genCA "mev-commit-ca" 365 -}} +{{- $cert := genSignedCert (include "mev-commit-p2p.fullname" .) nil $altNames 365 $ca -}} +tls.crt: {{ $cert.Cert | b64enc }} +tls.key: {{ $cert.Key | b64enc }} +{{- end -}} diff --git a/infrastructure/charts/mev-commit-p2p/templates/configmap-init.yaml b/infrastructure/charts/mev-commit-p2p/templates/configmap-init.yaml new file mode 100644 index 000000000..6d92f7680 --- /dev/null +++ b/infrastructure/charts/mev-commit-p2p/templates/configmap-init.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "mev-commit-p2p.fullname" . }}-{{ .Values.node.type }}-init + labels: + app.kubernetes.io/name: {{ include "mev-commit-p2p.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: {{ .Values.node.type }}-init + {{- include "mev-commit-p2p.labels" . | nindent 4 }} +data: + keystore-init.sh: |- + {{- .Files.Get "scripts/keystore-init.sh" | nindent 4 }} diff --git a/infrastructure/charts/mev-commit-p2p/templates/configmap.yaml b/infrastructure/charts/mev-commit-p2p/templates/configmap.yaml new file mode 100644 index 000000000..0c57b0bd8 --- /dev/null +++ b/infrastructure/charts/mev-commit-p2p/templates/configmap.yaml @@ -0,0 +1,67 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "mev-commit-p2p.fullname" . }}-{{ .Values.node.type }}-config + labels: + app.kubernetes.io/name: {{ include "mev-commit-p2p.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: {{ .Values.node.type }} + {{- include "mev-commit-p2p.labels" . | nindent 4 }} +data: + MEV_COMMIT_HTTP_ADDR: "0.0.0.0" + MEV_COMMIT_RPC_ADDR: "0.0.0.0" + MEV_COMMIT_P2P_ADDR: "0.0.0.0" + MEV_COMMIT_KEYSTORE_PATH: "{{ .Values.keystore.path | default "/keystore" }}" + MEV_COMMIT_HTTP_PORT: "{{ .Values.node.ports.http }}" + MEV_COMMIT_P2P_PORT: "{{ .Values.node.ports.p2p }}" + MEV_COMMIT_RPC_PORT: "{{ .Values.node.ports.rpc }}" + MEV_COMMIT_PEER_TYPE: "{{ .Values.node.type }}" + {{- if not .Values.global.tls.disabled }} + MEV_COMMIT_SERVER_TLS_CERTIFICATE: "/certs/tls.crt" + MEV_COMMIT_SERVER_TLS_PRIVATE_KEY: "/certs/tls.key" + {{- else }} + # TLS disabled - no certificate paths + {{- end }} + MEV_COMMIT_NAT_ADDR: "$(POD_IP)" + MEV_COMMIT_NAT_PORT: "{{ .Values.node.ports.p2p }}" + MEV_COMMIT_LOG_FMT: "{{ .Values.global.logging.format | default "json" }}" + MEV_COMMIT_LOG_LEVEL: "{{ .Values.global.logging.level | default "info" }}" + MEV_COMMIT_LOG_TAGS: "{{ .Values.global.logging.logTags }}" + + + # Contract addresses - non-sensitive public information + MEV_COMMIT_BIDDER_REGISTRY_ADDR: "{{ .Values.global.contracts.bidderRegistry }}" + MEV_COMMIT_PROVIDER_REGISTRY_ADDR: "{{ .Values.global.contracts.providerRegistry }}" + MEV_COMMIT_PRECONF_ADDR: "{{ .Values.global.contracts.preconfStore }}" + MEV_COMMIT_BLOCK_TRACKER_ADDR: "{{ .Values.global.contracts.blockTracker }}" + MEV_COMMIT_VALIDATOR_ROUTER_ADDR: "{{ .Values.global.contracts.validatorRouter }}" + + # Gas parameters + MEV_COMMIT_GAS_LIMIT: "{{ .Values.global.gas.limit }}" + MEV_COMMIT_GAS_TIP_CAP: "{{ .Values.global.gas.tipCap }}" + MEV_COMMIT_GAS_FEE_CAP: "{{ .Values.global.gas.feeCap }}" + MEV_COMMIT_NOTIFICATIONS_BUFFER: "{{ .Values.global.notifications.bufferCapacity | default "100" }}" + {{- if eq .Values.node.type "bidder" }} + MEV_COMMIT_BIDDER_BID_TIMEOUT: "{{ .Values.bidder.bidTimeout | default "30s" }}" + MEV_COMMIT_AUTODEPOSIT_ENABLED: "{{ .Values.bidder.autodeposit.enabled | default "false" }}" + {{- if .Values.bidder.autodeposit.enabled }} + MEV_COMMIT_AUTODEPOSIT_AMOUNT: "{{ .Values.bidder.autodeposit.amount }}" + {{- end }} + {{- end }} + {{- if eq .Values.node.type "provider" }} + MEV_COMMIT_PROVIDER_DECISION_TIMEOUT: "{{ .Values.provider.decisionTimeout | default "30s" }}" + {{- end }} + MEV_COMMIT_SETTLEMENT_RPC_ENDPOINT: "{{ .Values.global.rpc.settlementEndpoint }}" + MEV_COMMIT_SETTLEMENT_WS_RPC_ENDPOINT: "{{ .Values.global.rpc.settlementWsEndpoint }}" + MEV_COMMIT_L1_RPC_URL: "{{ .Values.global.rpc.l1Endpoint }}" + MEV_COMMIT_LAGGARD_MODE: "{{ .Values.global.laggardMode | default "10" }}" + {{- if eq .Values.node.type "bidder" }} + {{- if .Values.bidder.providerWhitelist }} + MEV_COMMIT_PROVIDER_WHITELIST: "{{ .Values.bidder.providerWhitelist }}" + {{- end }} + {{- end }} + {{- if ne .Values.node.type "bootnode" }} + {{- if .Values.node.bootnodeConnectionString }} + MEV_COMMIT_BOOTNODES: "{{ .Values.node.bootnodeConnectionString }}" + {{- end }} + {{- end }} diff --git a/infrastructure/charts/mev-commit-p2p/templates/externalsecret.yaml b/infrastructure/charts/mev-commit-p2p/templates/externalsecret.yaml new file mode 100644 index 000000000..2c9c4b46b --- /dev/null +++ b/infrastructure/charts/mev-commit-p2p/templates/externalsecret.yaml @@ -0,0 +1,86 @@ +{{- if .Values.global.externalSecrets.enabled }} +--- +{{- if and (not .Values.global.tls.selfSigned) (not .Values.global.tls.disabled) }} +# External Secret for TLS certificates (only if not using self-signed certs and TLS is not disabled) +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ include "mev-commit-p2p.fullname" . }}-tls-secret + labels: + {{- include "mev-commit-p2p.labels" . | nindent 4 }} +spec: + refreshInterval: "24h" + secretStoreRef: + name: {{ .Values.global.externalSecrets.secretStore }} + kind: {{ .Values.global.externalSecrets.secretStoreKind | default "ClusterSecretStore" }} + target: + name: {{ include "mev-commit-p2p.fullname" . }}-tls + creationPolicy: Owner + data: + - secretKey: tls.crt + remoteRef: + key: {{ .Values.global.externalSecrets.tlsSecretKey | default (printf "%s-tls" (include "mev-commit-p2p.fullname" .)) }} + property: tls.crt + - secretKey: tls.key + remoteRef: + key: {{ .Values.global.externalSecrets.tlsSecretKey | default (printf "%s-tls" (include "mev-commit-p2p.fullname" .)) }} + property: tls.key +{{- end }} + +--- +# External Secret for node secrets (passwords, P2P secrets) - only when ESO is enabled +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ include "mev-commit-p2p.fullname" . }}-node-secrets + labels: + {{- include "mev-commit-p2p.labels" . | nindent 4 }} +spec: + refreshInterval: "12h" + secretStoreRef: + name: {{ .Values.global.externalSecrets.secretStore }} + kind: {{ .Values.global.externalSecrets.secretStoreKind | default "ClusterSecretStore" }} + target: + name: {{ include "mev-commit-p2p.fullname" . }}-{{ .Values.node.type }}-secrets + creationPolicy: Owner + data: + - secretKey: MEV_COMMIT_KEYSTORE_PASSWORD + remoteRef: + key: {{ .Values.global.externalSecrets.nodeSecretKey | default (printf "%s-node-secrets" (include "mev-commit-p2p.fullname" .)) }} + property: {{ .Values.global.externalSecrets.nodeSecretProperties.keystorePassword | default "keystore-password" }} + - secretKey: MEV_COMMIT_SECRET + remoteRef: + key: {{ .Values.global.externalSecrets.nodeSecretKey | default (printf "%s-node-secrets" (include "mev-commit-p2p.fullname" .)) }} + property: {{ .Values.global.externalSecrets.nodeSecretProperties.p2pSecret | default "p2p-secret" }} +{{- end }} + +{{- if .Values.keystore.awsEnabled }} +--- +# External Secret for keystore files (independent of global ESO setting) +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: {{ include "mev-commit-p2p.fullname" . }}-{{ .Values.node.type }}-keystore + labels: + {{- include "mev-commit-p2p.labels" . | nindent 4 }} + annotations: + helm.sh/hook: pre-install,pre-upgrade + helm.sh/hook-weight: "-2" +spec: + refreshInterval: {{ .Values.keystore.refreshInterval | default "12h" }} + secretStoreRef: + name: {{ .Values.global.externalSecrets.secretStore }} + kind: {{ .Values.global.externalSecrets.secretStoreKind | default "ClusterSecretStore" }} + target: + name: {{ include "mev-commit-p2p.fullname" . }}-{{ .Values.node.type }}-keystore + creationPolicy: Owner + data: + - secretKey: temp_keystore.json + remoteRef: + key: {{ .Values.keystore.awsSecretKey | default (printf "%s-%s-keystore" (include "mev-commit-p2p.fullname" .) .Values.node.type) }} + property: {{ .Values.keystore.properties.keystore | default "keystore-json" }} + - secretKey: filename.txt + remoteRef: + key: {{ .Values.keystore.awsSecretKey | default (printf "%s-%s-keystore" (include "mev-commit-p2p.fullname" .) .Values.node.type) }} + property: {{ .Values.keystore.properties.keystoreFilename | default "keystore-filename" }} +{{- end }} diff --git a/infrastructure/charts/mev-commit-p2p/templates/secret.yaml b/infrastructure/charts/mev-commit-p2p/templates/secret.yaml new file mode 100644 index 000000000..2715d8a80 --- /dev/null +++ b/infrastructure/charts/mev-commit-p2p/templates/secret.yaml @@ -0,0 +1,93 @@ +{{- if and .Values.global.tls.selfSigned (not .Values.global.tls.disabled) }} +{{- $secretName := printf "%s-tls" (include "mev-commit-p2p.fullname" .) }} +{{- $existingSecret := lookup "v1" "Secret" .Release.Namespace $secretName }} +{{- $altNames := list (printf "%s-%s" (include "mev-commit-p2p.fullname" .) .Values.node.type) (printf "%s-%s.%s" (include "mev-commit-p2p.fullname" .) .Values.node.type .Release.Namespace) (printf "%s-%s.%s.svc" (include "mev-commit-p2p.fullname" .) .Values.node.type .Release.Namespace) }} +{{- $forceRegenerate := .Values.global.tls.forceRegenerate | default false }} + +{{- if and $existingSecret (not $forceRegenerate) }} +# Preserve existing TLS certificate +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + labels: + app.kubernetes.io/name: {{ include "mev-commit-p2p.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- include "mev-commit-p2p.labels" . | nindent 4 }} + annotations: + # Track certificate generation + {{- if $existingSecret.metadata.annotations }} + {{- if index $existingSecret.metadata.annotations "mev-commit.io/cert-created" }} + mev-commit.io/cert-created: {{ index $existingSecret.metadata.annotations "mev-commit.io/cert-created" | quote }} + {{- else }} + mev-commit.io/cert-created: {{ now | quote }} + {{- end }} + {{- else }} + mev-commit.io/cert-created: {{ now | quote }} + {{- end }} + mev-commit.io/cert-preserved: {{ now | quote }} +type: kubernetes.io/tls +data: + tls.crt: {{ index $existingSecret.data "tls.crt" }} + tls.key: {{ index $existingSecret.data "tls.key" }} +{{- else }} +# Generate new self-signed certificate +{{- $validityDays := .Values.global.tls.validityDays | default 365 | int }} +{{- $ca := genCA "mev-commit-ca" $validityDays }} +{{- $cert := genSignedCert (include "mev-commit-p2p.fullname" .) nil $altNames $validityDays $ca }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }} + labels: + app.kubernetes.io/name: {{ include "mev-commit-p2p.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- include "mev-commit-p2p.labels" . | nindent 4 }} + annotations: + # Track certificate generation + mev-commit.io/cert-created: {{ now | quote }} + mev-commit.io/cert-validity-days: {{ $validityDays | quote }} + {{- if $forceRegenerate }} + mev-commit.io/cert-force-regenerated: {{ now | quote }} + {{- end }} +type: kubernetes.io/tls +data: + tls.crt: {{ $cert.Cert | b64enc }} + tls.key: {{ $cert.Key | b64enc }} +{{- end }} +{{- else if .Values.global.tls.disabled }} +--- +# Create empty TLS secret when TLS is disabled (for compatibility) +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "mev-commit-p2p.fullname" . }}-tls + labels: + app.kubernetes.io/name: {{ include "mev-commit-p2p.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + {{- include "mev-commit-p2p.labels" . | nindent 4 }} + annotations: + mev-commit.io/tls-disabled: "true" +type: Opaque +data: + tls.crt: "" + tls.key: "" +{{- end }} + +{{- if and (not .Values.global.externalSecrets.enabled) (not .Values.node.existingSecret) }} +--- +# Create default secrets if neither ESO nor existing secrets are specified +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "mev-commit-p2p.fullname" . }}-{{ .Values.node.type }}-secrets + labels: + app.kubernetes.io/name: {{ include "mev-commit-p2p.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: {{ .Values.node.type }} + {{- include "mev-commit-p2p.labels" . | nindent 4 }} +type: Opaque +data: + MEV_COMMIT_KEYSTORE_PASSWORD: {{ .Values.node.defaultKeystorePassword | default "changeme" | b64enc }} + MEV_COMMIT_SECRET: {{ .Values.node.defaultP2PSecret | default (printf "mev-commit-%s-secret" .Values.node.type) | b64enc }} +{{- end }} diff --git a/infrastructure/charts/mev-commit-p2p/templates/service.yaml b/infrastructure/charts/mev-commit-p2p/templates/service.yaml new file mode 100644 index 000000000..d7272a1ee --- /dev/null +++ b/infrastructure/charts/mev-commit-p2p/templates/service.yaml @@ -0,0 +1,41 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "mev-commit-p2p.fullname" . }}-{{ .Values.node.type }} + labels: + app.kubernetes.io/name: {{ include "mev-commit-p2p.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: {{ .Values.node.type }} + {{- include "mev-commit-p2p.labels" . | nindent 4 }} + {{- with .Values.node.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.node.service.type }} + ports: + - port: {{ .Values.node.ports.http }} + targetPort: {{ .Values.node.ports.http }} + protocol: TCP + name: http + {{- if and (eq .Values.node.service.type "NodePort") .Values.node.service.nodePorts.http }} + nodePort: {{ .Values.node.service.nodePorts.http }} + {{- end }} + - port: {{ .Values.node.ports.p2p }} + targetPort: {{ .Values.node.ports.p2p }} + protocol: TCP + name: p2p + {{- if and (eq .Values.node.service.type "NodePort") .Values.node.service.nodePorts.p2p }} + nodePort: {{ .Values.node.service.nodePorts.p2p }} + {{- end }} + - port: {{ .Values.node.ports.rpc }} + targetPort: {{ .Values.node.ports.rpc }} + protocol: TCP + name: rpc + {{- if and (eq .Values.node.service.type "NodePort") .Values.node.service.nodePorts.rpc }} + nodePort: {{ .Values.node.service.nodePorts.rpc }} + {{- end }} + selector: + app.kubernetes.io/name: {{ include "mev-commit-p2p.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: {{ .Values.node.type }} diff --git a/infrastructure/charts/mev-commit-p2p/templates/statefulset.yaml b/infrastructure/charts/mev-commit-p2p/templates/statefulset.yaml new file mode 100644 index 000000000..77f5b2564 --- /dev/null +++ b/infrastructure/charts/mev-commit-p2p/templates/statefulset.yaml @@ -0,0 +1,165 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "mev-commit-p2p.fullname" . }}-{{ .Values.node.type }} + labels: + app.kubernetes.io/name: {{ include "mev-commit-p2p.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: {{ .Values.node.type }} + {{- include "mev-commit-p2p.labels" . | nindent 4 }} +spec: + serviceName: {{ include "mev-commit-p2p.fullname" . }}-{{ .Values.node.type }} + replicas: {{ .Values.node.replicas }} + selector: + matchLabels: + app.kubernetes.io/name: {{ include "mev-commit-p2p.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: {{ .Values.node.type }} + template: + metadata: + labels: + app.kubernetes.io/name: {{ include "mev-commit-p2p.name" . }} + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: {{ .Values.node.type }} + spec: + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.node.podSecurityContext | nindent 8 }} + + initContainers: + {{- if .Values.keystore.awsEnabled }} + # AWS SM Keystore Init Container (when keystore.awsEnabled is true) + - name: keystore-aws-init + image: "busybox:latest" + imagePullPolicy: IfNotPresent + command: + - sh + - -c + - | + echo "=== AWS SM Keystore Initialization ===" + FILENAME=$(cat /temp/filename.txt) + echo "Keystore filename: $FILENAME" + echo "Copying keystore to: {{ .Values.keystore.path }}/$FILENAME" + + # Create keystore directory + mkdir -p "{{ .Values.keystore.path }}" + + # Copy keystore with proper filename + cp /temp/temp_keystore.json "{{ .Values.keystore.path }}/$FILENAME" + + # Set proper permissions + chmod 600 "{{ .Values.keystore.path }}/$FILENAME" + + echo "โœ“ AWS SM keystore initialization completed" + echo "โœ“ Keystore file: {{ .Values.keystore.path }}/$FILENAME" + volumeMounts: + - name: keystore + mountPath: {{ .Values.keystore.path }} + - name: temp-keystore + mountPath: /temp + securityContext: + runAsUser: 0 + runAsGroup: 0 + runAsNonRoot: false + {{- else }} + # URL-based Keystore Init Container (when keystore.awsEnabled is false) + - name: keystore-url-init + image: "alpine/curl:8.5.0" + imagePullPolicy: IfNotPresent + command: ["/bin/sh", "/scripts/keystore-init.sh"] + env: + - name: KEYSTORE_PATH + value: "{{ .Values.keystore.path }}" + - name: KEYSTORE_DOWNLOAD_URL + value: "{{ .Values.keystore.downloadUrl }}" + - name: KEYSTORE_RETRIES + value: "{{ .Values.keystore.retries | default "3" }}" + volumeMounts: + - name: keystore + mountPath: {{ .Values.keystore.path }} + - name: init-scripts + mountPath: /scripts + securityContext: + runAsUser: 0 + runAsGroup: 0 + runAsNonRoot: false + {{- end }} + + containers: + - name: {{ .Values.node.type }} + image: "{{ .Values.global.image.repository }}:{{ .Values.global.image.tag }}" + imagePullPolicy: {{ .Values.global.image.pullPolicy }} + command: ["mev-commit"] + ports: + - name: http + containerPort: {{ .Values.node.ports.http }} + protocol: TCP + - name: p2p + containerPort: {{ .Values.node.ports.p2p }} + protocol: TCP + - name: rpc + containerPort: {{ .Values.node.ports.rpc }} + protocol: TCP + env: + - name: MEV_COMMIT_NAT_ADDR + valueFrom: + fieldRef: + fieldPath: status.podIP + envFrom: + - configMapRef: + name: {{ include "mev-commit-p2p.fullname" . }}-{{ .Values.node.type }}-config + - secretRef: + name: {{ include "mev-commit-p2p.fullname" . }}-{{ .Values.node.type }}-secrets + resources: + {{- toYaml .Values.node.resources | nindent 12 }} + securityContext: + {{- toYaml .Values.node.securityContext | nindent 12 }} + volumeMounts: + - name: data + mountPath: /data + - name: keystore + mountPath: {{ .Values.keystore.path }} + {{- if not .Values.global.tls.disabled }} + - name: tls-certs + mountPath: /certs + {{- end }} + + volumes: + {{- if not .Values.global.tls.disabled }} + - name: tls-certs + secret: + secretName: {{ include "mev-commit-p2p.fullname" . }}-tls + {{- end }} + {{- if .Values.keystore.awsEnabled }} + # AWS SM volumes + - name: temp-keystore + secret: + secretName: {{ include "mev-commit-p2p.fullname" . }}-{{ .Values.node.type }}-keystore + {{- else }} + # URL-based volumes + - name: init-scripts + configMap: + name: {{ include "mev-commit-p2p.fullname" . }}-{{ .Values.node.type }}-init + defaultMode: 0755 + {{- end }} + + volumeClaimTemplates: + - metadata: + name: data + spec: + accessModes: ["ReadWriteOnce"] + storageClassName: {{ .Values.global.persistence.storageClass | default "premium-rwo" }} + resources: + requests: + storage: {{ .Values.global.persistence.dataSize | default "10Gi" }} + - metadata: + name: keystore + spec: + accessModes: ["ReadWriteOnce"] + storageClassName: {{ .Values.global.persistence.storageClass | default "premium-rwo" }} + resources: + requests: + storage: {{ .Values.global.persistence.keystoreSize | default "5Mi" }} diff --git a/infrastructure/charts/mev-commit-p2p/values-bidder.yaml b/infrastructure/charts/mev-commit-p2p/values-bidder.yaml new file mode 100644 index 000000000..f64f34073 --- /dev/null +++ b/infrastructure/charts/mev-commit-p2p/values-bidder.yaml @@ -0,0 +1,82 @@ +# Override values specific to bidder deployment + +global: + # TLS configuration - disabled for bidder + tls: + disabled: false + selfSigned: true + + contracts: + bidderRegistry: "0x00000000000000000000000000000000000000" + blockTracker: "0x00000000000000000000000000000000000000" + # oracle: "0x00000000000000000000000000000000000000" + preconfStore: "0x00000000000000000000000000000000000000" + providerRegistry: "0x00000000000000000000000000000000000000" + + + # RPC endpoints + rpc: + settlementEndpoint: "" + settlementWsEndpoint: "" + l1Endpoint: "" # mock L1 + +node: + type: "bidder" + replicas: 1 + + ports: + http: 13723 + p2p: 13722 + rpc: 13724 + + service: + type: ClusterIP + annotations: {} + + defaultKeystorePassword: "CXBMf4xEDO0I" + defaultP2PSecret: "" + + bootnodeConnectionString: "/ip4/109.94.98.75/tcp/13522/p2p/16Uiu2HAmBEYHm37swRU5aphvX7AXxtpFE6kGCkWvgBoRML4FtmyW" + resources: + limits: + cpu: 6000m + memory: 12Gi + requests: + cpu: 280m + memory: 1Gi + + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + runAsNonRoot: false + runAsUser: 0 + + podSecurityContext: + fsGroup: 0 + + nodeSelector: {} + tolerations: [] + affinity: {} + + podAnnotations: {} + imagePullSecrets: [] + +# Bidder-specific configuration +bidder: + bidTimeout: "60s" + + autodeposit: + enabled: false + amount: "" # leave empty or set to a value like "0.01" + + providerWhitelist: "" # comma-separated addresses, e.g. "0xabc...,0xdef..." + +keystore: + path: "/keystore" + downloadUrl: "https://storage.googleapis.com/devnet-artifacts/keystores/erigon-keystores/keystore4/UTC--2025-06-24T18-20-45.876172000Z--cede115ffaa36313bde4e3dcf1fc50869b308791" + retries: 3 + +#nodeSelector: +# workload-type: general diff --git a/infrastructure/charts/mev-commit-p2p/values-provider.yaml b/infrastructure/charts/mev-commit-p2p/values-provider.yaml new file mode 100644 index 000000000..a563a2066 --- /dev/null +++ b/infrastructure/charts/mev-commit-p2p/values-provider.yaml @@ -0,0 +1,81 @@ +# Override values specific to provider deployment + +global: + # TLS configuration - disabled for bidder + tls: + disabled: false + selfSigned: true + + contracts: + bidderRegistry: "0x00000000000000000000000000000000000000" + blockTracker: "0x00000000000000000000000000000000000000" + # oracle: "0x00000000000000000000000000000000000000" + preconfStore: "0x00000000000000000000000000000000000000" + providerRegistry: "0x00000000000000000000000000000000000000" + + + # RPC endpoints + rpc: + settlementEndpoint: "" + settlementWsEndpoint: "" + l1Endpoint: "" # mock L1 + + +node: + type: "provider" + replicas: 1 + + ports: + http: 13623 + p2p: 13622 + rpc: 13624 + + service: + type: ClusterIP + annotations: {} + + defaultKeystorePassword: "CXBMf4xEDO0I" + defaultP2PSecret: "mev-commit-provider-secret" + + bootnodeConnectionString: "/ip4/35.196.27.69/tcp/13522/p2p/16Uiu2HAmUeBCkYDwrxn4wKmbFd55JpgbW2WuF3fpLxQvwNbWP4SY" + + resources: + limits: + cpu: 16000m + memory: 32Gi + requests: + cpu: 700m + memory: 1Gi + + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + runAsNonRoot: false + runAsUser: 0 + + podSecurityContext: + fsGroup: 0 + + nodeSelector: {} + tolerations: [] + affinity: {} + + podAnnotations: {} + imagePullSecrets: [] + +# Provider-specific configuration +provider: + decisionTimeout: "30s" + +#nodeSelector: +# workload-type: general + +keystore: + path: "/keystore" + downloadUrl: "https://storage.googleapis.com/devnet-artifacts/keystores/erigon-keystores/keystore5/UTC--2025-06-24T18-20-46.524919000Z--44b5a4b0c879844033e11b88b507816492ff98b0" + + + # AWS Secrets Manager keystore (still enabled for keystore files) + awsEnabled: false # Keep AWS SM for keystore files only diff --git a/infrastructure/charts/mev-commit-p2p/values.yaml b/infrastructure/charts/mev-commit-p2p/values.yaml new file mode 100644 index 000000000..2cfb7af02 --- /dev/null +++ b/infrastructure/charts/mev-commit-p2p/values.yaml @@ -0,0 +1,122 @@ +# Default values for mev-commit-p2p. + +nameOverride: "" +fullnameOverride: "" + +global: + # Docker image configuration + image: + repository: primev/primev + tag: + pullPolicy: Always + + # TLS certificate configuration + tls: + selfSigned: true + existingSecret: "" + forceRegenerate: false + validityDays: 365 + + # External Secret Operator configuration + externalSecrets: + enabled: false + secretStore: "aws-cluster-secret-store" + secretStoreKind: "ClusterSecretStore" + nodeSecretKey: "mev-commit-node-secrets" + tlsSecretKey: "mev-commit-tls" + + # Persistence configuration + persistence: + enabled: true + storageClass: "premium-rwo" + dataSize: "10Gi" + keystoreSize: "2Mi" + + # Logging configuration + logging: + format: "json" + level: "info" + logTags: "service.name:mev-commit-bootnode,service.version:1.2.0,environment:production,team:protocol" + + # Contract addresses + contracts: + bidderRegistry: "0x3B6fE9ea4421c303FD83795D294b8222ff2d9EB3" + blockTracker: "0xFF7cF3A4B4b2acdf426D813e76aa11946bb5329b" + # oracle: "0xB241520A1a049e16Dd9193B1FCE23cB458f1f676" + preconfStore: "0x16A76F15335e61a04f1aD13858c2383Cb9d14057" + providerRegistry: "0x2B8ff90eAD0069fd6286ca389457bf58107971fe" + #validatorRouter: "0x6c7A44A9A1836EF14129261D23166F7BD0196fb8" + + # Gas settings for transactions + gas: + limit: "2000000" + tipCap: "50000000" + feeCap: "60000000" + + # RPC endpoints + rpc: + settlementEndpoint: "http://erigon-snode-erigon.hoodie-testnet.svc.cluster.local:8545" + settlementWsEndpoint: "ws://erigon-snode-erigon.hoodie-testnet.svc.cluster.local:8546" + l1Endpoint: "https://ethereum-hoodi-rpc.publicnode.com" + + # Notifications + notifications: + bufferCapacity: "100" + + # Ethereum chain configuration + laggardMode: "10" + +# Node configuration template (bootnode, provider, or bidder) +node: + type: "bootnode" + replicas: 1 + + service: + type: ClusterIP + annotations: {} + nodePorts: + http: null + p2p: null + rpc: null + + ports: + http: 13523 + p2p: 13522 + rpc: 13524 + + securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: false + runAsNonRoot: false + runAsUser: 0 + + podSecurityContext: + fsGroup: 0 + + resources: + limits: + cpu: 1000m + memory: 2048Mi + requests: + cpu: 500m + memory: 1024Mi + + bootnodeConnectionString: "" + + defaultKeystorePassword: "CXBMf4xEDO0I" + defaultP2PSecret: "" + existingSecret: "" + + nodeSelector: {} + tolerations: [] + affinity: {} + + podAnnotations: {} + imagePullSecrets: [] + +# Explicit config for use in ConfigMap +keystore: + path: "/p2p-keystore" + diff --git a/infrastructure/charts/mev-commit-relay-emulator/Chart.yaml b/infrastructure/charts/mev-commit-relay-emulator/Chart.yaml new file mode 100644 index 000000000..4edcd96f7 --- /dev/null +++ b/infrastructure/charts/mev-commit-relay-emulator/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +name: mev-commit-emulator +description: A Helm chart for deploying mev-commit-relay-emulator service +type: application +version: 0.1.0 +appVersion: "1.0.0" diff --git a/infrastructure/charts/mev-commit-relay-emulator/templates/_helpers.tpl b/infrastructure/charts/mev-commit-relay-emulator/templates/_helpers.tpl new file mode 100644 index 000000000..e5d6c1245 --- /dev/null +++ b/infrastructure/charts/mev-commit-relay-emulator/templates/_helpers.tpl @@ -0,0 +1,74 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "relay-emulator.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "relay-emulator.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "relay-emulator.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "relay-emulator.labels" -}} +helm.sh/chart: {{ include "relay-emulator.chart" . }} +{{ include "relay-emulator.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/component: relay-emulator +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "relay-emulator.selectorLabels" -}} +app.kubernetes.io/name: {{ include "relay-emulator.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create log tags with service info +*/}} +{{- define "relay-emulator.logTags" -}} +{{- $baseTags := printf "service.name:%s,service.version:%s" .Values.job.name .Chart.AppVersion -}} +{{- if index .Values.job.env "log-tags" -}} +{{- printf "%s,%s" $baseTags (index .Values.job.env "log-tags") -}} +{{- else -}} +{{- $baseTags -}} +{{- end -}} +{{- end }} + +{{/* +Validate required values +*/}} +{{- define "relay-emulator.validateValues" -}} +{{- if not .Values.job.l1RpcUrl }} +{{- fail "job.l1RpcUrl is required" }} +{{- end }} +{{- if not .Values.job.httpPort }} +{{- fail "job.httpPort is required" }} +{{- end }} +{{- end }} diff --git a/infrastructure/charts/mev-commit-relay-emulator/templates/deployment.yaml b/infrastructure/charts/mev-commit-relay-emulator/templates/deployment.yaml new file mode 100644 index 000000000..8070103d4 --- /dev/null +++ b/infrastructure/charts/mev-commit-relay-emulator/templates/deployment.yaml @@ -0,0 +1,96 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "relay-emulator.fullname" . }} + namespace: {{ .Values.namespace | default "default" }} + labels: + {{- include "relay-emulator.labels" . | nindent 4 }} + {{- with .Values.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.job.replicas }} + selector: + matchLabels: + {{- include "relay-emulator.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "relay-emulator.selectorLabels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- include "relay-emulator.validateValues" . }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: {{ .Values.job.name }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: + - /usr/local/bin/relay-emulator + args: + - "--l1-rpc-url={{ .Values.job.l1RpcUrl }}" + - "--http-port={{ .Values.job.httpPort }}" + {{- if index .Values.job.env "log-format" }} + - "--log-fmt={{ index .Values.job.env "log-format" }}" + {{- end }} + {{- if index .Values.job.env "log-level" }} + - "--log-level={{ index .Values.job.env "log-level" }}" + {{- end }} + {{- if include "relay-emulator.logTags" . }} + - "--log-tags={{ include "relay-emulator.logTags" . }}" + {{- end }} + env: + - name: MOCK_RELAY_L1_RPC_URL + value: {{ .Values.job.l1RpcUrl | quote }} + - name: MOCK_RELAY_HTTP_PORT + value: {{ .Values.job.httpPort | quote }} + - name: MOCK_RELAY_LOG_FMT + value: {{ index .Values.job.env "log-format" | default "json" | quote }} + - name: MOCK_RELAY_LOG_LEVEL + value: {{ index .Values.job.env "log-level" | default "info" | quote }} + - name: MOCK_RELAY_LOG_TAGS + value: {{ include "relay-emulator.logTags" . | quote }} + ports: + - containerPort: {{ .Values.job.httpPort }} + name: http + protocol: TCP + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + {{- end }} diff --git a/infrastructure/charts/mev-commit-relay-emulator/templates/service.yaml b/infrastructure/charts/mev-commit-relay-emulator/templates/service.yaml new file mode 100644 index 000000000..61c47df4e --- /dev/null +++ b/infrastructure/charts/mev-commit-relay-emulator/templates/service.yaml @@ -0,0 +1,22 @@ +{{- if .Values.service.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "relay-emulator.fullname" . }} + namespace: {{ .Values.namespace | default "default" }} + labels: + {{- include "relay-emulator.labels" . | nindent 4 }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: {{ .Values.service.targetPort }} + protocol: TCP + name: http + selector: + {{- include "relay-emulator.selectorLabels" . | nindent 4 }} +{{- end }} diff --git a/infrastructure/charts/mev-commit-relay-emulator/values.yaml b/infrastructure/charts/mev-commit-relay-emulator/values.yaml new file mode 100644 index 000000000..b636393ad --- /dev/null +++ b/infrastructure/charts/mev-commit-relay-emulator/values.yaml @@ -0,0 +1,72 @@ +# Relay Emulator Helm Chart Values + +# Global settings +namespace: default + +# Name overrides +nameOverride: "" +fullnameOverride: "" + +# Job configuration +job: + name: "relay-emulator" + replicas: 1 + + # Relay emulator specific configuration + l1RpcUrl: "http://erigon-mev-commit-mock-l1.default.svc.cluster.local:8545" # L1 RPC URL + httpPort: 8080 # HTTP port for relay APIs + + # Logging configuration + env: + log-format: "json" # text or json + log-level: "info" # debug, info, warn, error + log-tags: "" # Additional comma-separated log tags + +# Application image +image: + repository: "primev/primev" + tag: "" + pullPolicy: Always + +# Service configuration +service: + enabled: true + type: ClusterIP + port: 8080 + targetPort: 8080 + annotations: {} + +# Resource configuration +resources: + limits: + cpu: 1000m + memory: 2Gi + requests: + cpu: 250m + memory: 256Mi + +# Health checks +livenessProbe: + enabled: false + initialDelaySeconds: 30 + periodSeconds: 30 + timeoutSeconds: 10 + failureThreshold: 3 + +readinessProbe: + enabled: false + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 3 + +# Pod scheduling and configuration +#nodeSelector: +# workload-type: general +tolerations: [] +affinity: {} + +# Labels and annotations +annotations: {} +podLabels: {} +podAnnotations: {} diff --git a/infrastructure/charts/postgres/Chart.yaml b/infrastructure/charts/postgres/Chart.yaml new file mode 100644 index 000000000..452236c24 --- /dev/null +++ b/infrastructure/charts/postgres/Chart.yaml @@ -0,0 +1,7 @@ +# Chart.yaml +apiVersion: v2 +name: postgresql +description: A simple Helm chart for PostgreSQL +type: application +version: 0.1.0 +appVersion: "15" diff --git a/infrastructure/charts/postgres/templates/_helpers.tpl b/infrastructure/charts/postgres/templates/_helpers.tpl new file mode 100644 index 000000000..85a732154 --- /dev/null +++ b/infrastructure/charts/postgres/templates/_helpers.tpl @@ -0,0 +1,50 @@ +# templates/_helpers.tpl +{{/* +Expand the name of the chart. +*/}} +{{- define "postgresql.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "postgresql.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "postgresql.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "postgresql.labels" -}} +helm.sh/chart: {{ include "postgresql.chart" . }} +{{ include "postgresql.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "postgresql.selectorLabels" -}} +app.kubernetes.io/name: {{ include "postgresql.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/infrastructure/charts/postgres/templates/configmap.yaml b/infrastructure/charts/postgres/templates/configmap.yaml new file mode 100644 index 000000000..26b13cfe5 --- /dev/null +++ b/infrastructure/charts/postgres/templates/configmap.yaml @@ -0,0 +1,10 @@ +# templates/configmap.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "postgresql.fullname" . }}-config + labels: + {{- include "postgresql.labels" . | nindent 4 }} +data: + POSTGRES_DB: {{ .Values.postgresql.database | quote }} + POSTGRES_USER: {{ .Values.postgresql.username | quote }} diff --git a/infrastructure/charts/postgres/templates/deployment.yaml b/infrastructure/charts/postgres/templates/deployment.yaml new file mode 100644 index 000000000..ec6cb520c --- /dev/null +++ b/infrastructure/charts/postgres/templates/deployment.yaml @@ -0,0 +1,113 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "postgresql.fullname" . }} + labels: + {{- include "postgresql.labels" . | nindent 4 }} + {{- with .Values.additionalLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.replicaCount }} + strategy: + type: {{ .Values.strategy.type }} + selector: + matchLabels: + {{- include "postgresql.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "postgresql.selectorLabels" . | nindent 8 }} + {{- with .Values.additionalLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + securityContext: + {{- toYaml .Values.securityContext | nindent 8 }} + {{- if .Values.persistence.enabled }} + initContainers: + - name: init-permissions + image: busybox:1.36 + command: + - sh + - -c + - | + mkdir -p /var/lib/postgresql/data/pgdata + chown -R 999:999 /var/lib/postgresql/data + chmod -R 750 /var/lib/postgresql/data + volumeMounts: + - name: postgresql-data + mountPath: /var/lib/postgresql/data + securityContext: + runAsUser: 0 + {{- end }} + containers: + - name: postgresql + image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + ports: + - name: postgresql + containerPort: 5432 + protocol: TCP + env: + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "postgresql.fullname" . }}-secret + key: postgres-password + - name: PGDATA + value: /var/lib/postgresql/data/{{ .Values.persistence.subPath }} + envFrom: + - configMapRef: + name: {{ include "postgresql.fullname" . }}-config + volumeMounts: + {{- if .Values.persistence.enabled }} + - name: postgresql-data + mountPath: /var/lib/postgresql/data + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- if .Values.probes.liveness.enabled }} + livenessProbe: + exec: + command: + - /bin/sh + - -c + - pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB} + initialDelaySeconds: {{ .Values.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.liveness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.liveness.failureThreshold }} + successThreshold: {{ .Values.probes.liveness.successThreshold }} + {{- end }} + {{- if .Values.probes.readiness.enabled }} + readinessProbe: + exec: + command: + - /bin/sh + - -c + - pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB} + initialDelaySeconds: {{ .Values.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.probes.readiness.timeoutSeconds }} + failureThreshold: {{ .Values.probes.readiness.failureThreshold }} + successThreshold: {{ .Values.probes.readiness.successThreshold }} + {{- end }} + volumes: + {{- if .Values.persistence.enabled }} + - name: postgresql-data + persistentVolumeClaim: + claimName: {{ include "postgresql.fullname" . }}-pvc + {{- end }} diff --git a/infrastructure/charts/postgres/templates/pvc.yaml b/infrastructure/charts/postgres/templates/pvc.yaml new file mode 100644 index 000000000..d61622f7c --- /dev/null +++ b/infrastructure/charts/postgres/templates/pvc.yaml @@ -0,0 +1,18 @@ +# templates/pvc.yaml +{{- if .Values.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "postgresql.fullname" . }}-pvc + labels: + {{- include "postgresql.labels" . | nindent 4 }} +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: {{ .Values.persistence.size }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass }} + {{- end }} +{{- end }} diff --git a/infrastructure/charts/postgres/templates/secret.yaml b/infrastructure/charts/postgres/templates/secret.yaml new file mode 100644 index 000000000..5f27a35cc --- /dev/null +++ b/infrastructure/charts/postgres/templates/secret.yaml @@ -0,0 +1,10 @@ +# templates/secret.yaml +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "postgresql.fullname" . }}-secret + labels: + {{- include "postgresql.labels" . | nindent 4 }} +type: Opaque +data: + postgres-password: {{ .Values.postgresql.password | default (randAlphaNum 16) | b64enc | quote }} diff --git a/infrastructure/charts/postgres/templates/service.yaml b/infrastructure/charts/postgres/templates/service.yaml new file mode 100644 index 000000000..af3c093c6 --- /dev/null +++ b/infrastructure/charts/postgres/templates/service.yaml @@ -0,0 +1,20 @@ +# templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: {{ include "postgresql.fullname" . }} + labels: + {{- include "postgresql.labels" . | nindent 4 }} +spec: + type: {{ .Values.service.type }} + ports: + - port: {{ .Values.service.port }} + targetPort: postgresql + protocol: TCP + name: postgresql + selector: + {{- if .Values.service.customSelector }} + {{- toYaml .Values.service.customSelector | nindent 4 }} + {{- else }} + {{- include "postgresql.selectorLabels" . | nindent 4 }} + {{- end }} diff --git a/infrastructure/charts/postgres/values.yaml b/infrastructure/charts/postgres/values.yaml new file mode 100644 index 000000000..593b61c46 --- /dev/null +++ b/infrastructure/charts/postgres/values.yaml @@ -0,0 +1,75 @@ +# values.yaml +# Default values for postgresql +replicaCount: 1 + +image: + repository: postgres + tag: "15" + pullPolicy: IfNotPresent + +# Deployment strategy +strategy: + type: Recreate + +# Additional labels for deployment +additionalLabels: + app: oracle + chain: testnet + +# PostgreSQL configuration +postgresql: + database: mev_oracle + username: mev_oracle + # Password will be generated if not set + password: "mev_oracle_password" + +# Persistence +persistence: + enabled: true + storageClass: "premium-rwo" + size: 8Gi + subPath: "pgdata" + +# Service configuration +service: + type: ClusterIP + port: 5432 + # Custom selector for service (optional) + # If not specified, will use standard selector labels + customSelector: {} + # app: custom-postgres + # version: v1 + +# Resource limits +resources: + limits: + cpu: 1000m + memory: 4Gi + requests: + cpu: 100m + memory: 1Gi + +# Security context +securityContext: + runAsUser: 999 + runAsGroup: 999 + fsGroup: 999 + +probes: + liveness: + enabled: false + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + readiness: + enabled: false + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +#nodeSelector: +# workload-type: general