diff --git a/formulas/run-red-team.toml b/formulas/run-red-team.toml index 875035a..c99b81e 100644 --- a/formulas/run-red-team.toml +++ b/formulas/run-red-team.toml @@ -55,9 +55,9 @@ description = "Timeout in seconds for the adversarial agent run (maps to CLAUDE_ # → promote-attacks (if floor broken) → deliver → teardown. # # CANDIDATE_NAME and OPTIMIZER_PROFILE label the evidence record and attack -# filenames; they do not select which optimizer is deployed — bootstrap-light -# always deploys via DeployLocal.sol. Per-candidate Push3 injection is planned -# but not yet wired (see notes.candidate_injection). +# filenames. To deploy a specific Push3 candidate, set the CANDIDATE env var +# (path to a .push3 file) — bootstrap-light.sh will transpile, recompile, and +# upgrade the Optimizer proxy to OptimizerV3 (see notes.candidate_injection). [execution] script = "scripts/harb-evaluator/red-team.sh" @@ -81,9 +81,9 @@ scripts/harb-evaluator/bootstrap-light.sh: liquidity into positions — establishing a realistic baseline. - Verifies Anvil responds and all contract addresses are present in onchain/deployments-local.json before proceeding. -Note: the deployed optimizer is always the default from DeployLocal.sol. -Per-candidate Push3 transpilation is not yet implemented here; see -notes.candidate_injection. +When the CANDIDATE env var is set (path to a .push3 file), bootstrap-light.sh +transpiles the candidate and upgrades the Optimizer proxy to OptimizerV3. +See notes.candidate_injection for details. """ [[steps]] @@ -242,13 +242,14 @@ rediscoveries are silently dropped and the step exits 0. """ candidate_injection = """ -Push3 candidate injection is not yet implemented: bootstrap-light.sh always -deploys the default optimizer via DeployLocal.sol and does not read the -CANDIDATE env var. The candidate_name and optimizer_profile inputs are used -only for labelling (evidence records, attack filenames, PR titles); they do not -affect which optimizer is deployed. -Wiring CANDIDATE → push3-transpiler → forge compile → bootstrap-light is -tracked as a follow-up issue. +Push3 candidate injection is supported via the CANDIDATE env var in +bootstrap-light.sh. When CANDIDATE points to a .push3 file the script: + 1. Invokes push3-transpiler to regenerate OptimizerV3Push3.sol. + 2. Extracts the function body into OptimizerV3Push3Lib.sol (shared library). + 3. Deploys contracts normally via DeployLocal.sol (Optimizer v1 behind UUPS proxy). + 4. Deploys a fresh OptimizerV3 implementation and upgrades the proxy via upgradeTo(). +The candidate_name and optimizer_profile inputs remain metadata-only (evidence +records, attack filenames, PR titles). """ run_attack_suite_gap = """ diff --git a/scripts/harb-evaluator/bootstrap-light.sh b/scripts/harb-evaluator/bootstrap-light.sh index 9e48f69..93fb3bc 100755 --- a/scripts/harb-evaluator/bootstrap-light.sh +++ b/scripts/harb-evaluator/bootstrap-light.sh @@ -1,13 +1,21 @@ #!/usr/bin/env bash # Lightweight bootstrap for red-team / evaluator use. # Starts only Anvil + deploys contracts. No ponder, no webapp, no txnbot. +# +# Environment overrides: +# CANDIDATE Path to a .push3 file. When set, the push3-transpiler is +# invoked to regenerate OptimizerV3Push3Lib.sol, then the +# deployed Optimizer UUPS proxy is upgraded to OptimizerV3 +# (which delegates to the regenerated lib). +# RPC_URL Anvil RPC endpoint (default: http://localhost:8545) set -euo pipefail REPO_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" ONCHAIN_DIR="$REPO_ROOT/onchain" -RPC_URL="http://localhost:8545" +RPC_URL="${RPC_URL:-http://localhost:8545}" CAST="$HOME/.foundry/bin/cast" FORGE="$HOME/.foundry/bin/forge" +CANDIDATE="${CANDIDATE:-}" log() { echo "[bootstrap-light] $*"; } die() { log "ERROR: $*" >&2; exit 1; } @@ -30,7 +38,85 @@ log "Clearing code from deployer ($DEPLOYER) + feeDest" $CAST rpc --rpc-url "$RPC_URL" anvil_setCode "$DEPLOYER" "0x" 2>/dev/null || true $CAST rpc --rpc-url "$RPC_URL" anvil_setCode "0xf6a3eef9088A255c32b6aD2025f83E57291D9011" "0x" 2>/dev/null || true -# 3. Deploy contracts — capture output for addresses +# 3. Push3 candidate injection (optional) +# When CANDIDATE is set, transpile the .push3 file into OptimizerV3Push3.sol, +# then regenerate OptimizerV3Push3Lib.sol so that OptimizerV3 (which delegates +# to the lib) will use the candidate logic after the proxy upgrade in step 6. +if [[ -n "$CANDIDATE" ]]; then + [[ -f "$CANDIDATE" ]] || die "CANDIDATE file not found: $CANDIDATE" + log "Transpiling candidate: $CANDIDATE" + + TRANSPILER_DIR="$REPO_ROOT/tools/push3-transpiler" + TRANSPILER_OUT="$ONCHAIN_DIR/src/OptimizerV3Push3.sol" + + # Ensure transpiler deps are installed + if [[ ! -d "$TRANSPILER_DIR/node_modules" ]]; then + (cd "$TRANSPILER_DIR" && npm install --silent) || die "transpiler npm install failed" + fi + + # Transpile Push3 → standalone OptimizerV3Push3.sol + (cd "$TRANSPILER_DIR" && npx tsx src/index.ts "$CANDIDATE" "$TRANSPILER_OUT") \ + || die "push3-transpiler failed" + + # Regenerate OptimizerV3Push3Lib.sol from the transpiler output. + # Extracts the calculateParams function body and wraps it in a library. + python3 - "$TRANSPILER_OUT" "$ONCHAIN_DIR/src/OptimizerV3Push3Lib.sol" <<'PYEOF' || die "lib generation failed" +import sys + +with open(sys.argv[1]) as f: + push3 = f.read() + +# Extract function body (everything between the first { after calculateParams and its closing }) +fn_start = push3.find("function calculateParams") +if fn_start == -1: + sys.exit("calculateParams not found in transpiler output") +brace_start = push3.find("{", fn_start) +sig_end = push3.index("\n", brace_start) + 1 + +lines = push3[sig_end:].split("\n") +body_lines = [] +depth = 1 +for line in lines: + depth += line.count("{") - line.count("}") + if depth <= 0: + break + body_lines.append(line) +else: + sys.exit("closing brace not found") + +body = "\n".join(body_lines) + +lib = f"""// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.19; + +import {{OptimizerInput}} from "./IOptimizer.sol"; + +/** + * @title OptimizerV3Push3Lib + * @notice Shared library containing the canonical Push3 transpiler output for + * OptimizerV3 parameter calculation. Used by both OptimizerV3Push3 + * (standalone) and OptimizerV3 (UUPS-upgradeable Optimizer) so that + * future transpiler changes require only one edit. + * @dev Auto-regenerated by bootstrap-light.sh from CANDIDATE env var. + */ +library OptimizerV3Push3Lib {{ + function calculateParams(OptimizerInput[8] memory inputs) + internal + pure + returns (uint256 ci, uint256 anchorShare, uint24 anchorWidth, uint256 discoveryDepth) + {{ +{body} + }} +}} +""" +with open(sys.argv[2], "w") as f: + f.write(lib) +PYEOF + + log "OptimizerV3Push3Lib.sol regenerated from candidate" +fi + +# 4. Deploy contracts — capture output for addresses log "Deploying contracts..." cd "$ONCHAIN_DIR" # Fix ownership of forge artifacts (docker creates root-owned files) @@ -39,7 +125,7 @@ rm -f deployments-local.json # force fresh DEPLOY_OUT=$($FORGE script script/DeployLocal.sol --rpc-url "$RPC_URL" --broadcast 2>&1) echo "$DEPLOY_OUT" | grep -E "^\[|deployed|complete|Summary" || true -# 4. Extract addresses from output and write deployments-local.json +# 5. Extract addresses from output and write deployments-local.json KRK=$(echo "$DEPLOY_OUT" | grep -oP 'Kraiken deployed: \K0x[a-fA-F0-9]+') [[ -n "$KRK" ]] || die "Could not extract Kraiken address from deploy output" STAKE=$(echo "$DEPLOY_OUT" | grep -oP 'Stake deployed: \K0x[a-fA-F0-9]+') @@ -74,7 +160,37 @@ cat > "$ONCHAIN_DIR/deployments-local.json" << EOF } EOF -# 5. Verify +# 6. Upgrade Optimizer proxy to OptimizerV3 (candidate injection) +# DeployLocal deploys Optimizer (v1) behind a UUPS proxy. When a Push3 +# candidate was transpiled in step 3, we deploy a fresh OptimizerV3 +# implementation (which delegates to the regenerated OptimizerV3Push3Lib) +# and upgrade the proxy so the candidate logic is live on-chain. +if [[ -n "$CANDIDATE" ]]; then + log "Upgrading Optimizer proxy to OptimizerV3 (candidate: $(basename "$CANDIDATE" .push3))..." + # Deployer PK — Anvil account 0 (same key DeployLocal.sol uses via .secret.local) + DEPLOYER_PK=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + + # Deploy OptimizerV3 implementation + V3_DEPLOY_OUT=$($FORGE create --rpc-url "$RPC_URL" --private-key "$DEPLOYER_PK" \ + src/OptimizerV3.sol:OptimizerV3 2>&1) \ + || die "OptimizerV3 deployment failed: $V3_DEPLOY_OUT" + V3_IMPL=$(echo "$V3_DEPLOY_OUT" | grep -oP 'Deployed to: \K0x[a-fA-F0-9]+') + [[ -n "$V3_IMPL" ]] || die "Could not extract OptimizerV3 address from forge create output" + log "OptimizerV3 implementation: $V3_IMPL" + + # Upgrade the UUPS proxy + $CAST send --rpc-url "$RPC_URL" --private-key "$DEPLOYER_PK" \ + "$OPT" "upgradeTo(address)" "$V3_IMPL" >/dev/null 2>&1 \ + || die "upgradeTo failed" + log "Proxy upgraded to OptimizerV3" + + # Verify the upgrade by calling getLiquidityParams through the proxy + VERIFY_OUT=$($CAST call --rpc-url "$RPC_URL" "$OPT" \ + "getLiquidityParams()(uint256,uint256,uint24,uint256)" 2>/dev/null) || true + log "Post-upgrade getLiquidityParams: $VERIFY_OUT" +fi + +# 7. Verify VWAP=$($CAST call --rpc-url "$RPC_URL" "$LM" "cumulativeVolume()(uint256)" 2>/dev/null || echo "0") log "LiquidityManager: $LM" log "cumulativeVolume: $VWAP"