Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/workflows/check-no-fee.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: check-no-fee

on:
pull_request:
push:
branches: [main]

jobs:
check-no-fee:
name: Verify no fee/admin surface
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run check-no-fee.sh
run: bash scripts/check-no-fee.sh
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ resolver = "2"
members = ["program", "no-padding", "assertions"]
exclude = ["tests-e2e"]

[workspace.metadata.cli]
solana = "3.0.4"

[workspace.dependencies]
pinocchio = { version = "0.9", features = ["std"] }
pinocchio-pubkey = { version = "0.3" }
Expand Down
74 changes: 74 additions & 0 deletions scripts/check-no-fee.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env bash
# Verify that program-v2 contains no fee/admin surface.
#
# Reads scripts/fee-paths.txt and fails if:
# - any PATH rule matches a file or directory that exists
# - any SYMBOL rule (ERE regex) matches inside program/src/**/*.rs
#
# Designed to run in CI (Linux, GNU grep) and locally (macOS, BSD grep).
# Exit codes:
# 0 = clean
# 1 = violation found
# 2 = invalid invocation / missing fee-paths.txt

set -euo pipefail

REPO_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
RULES_FILE="$REPO_ROOT/scripts/fee-paths.txt"
SOURCE_ROOT="$REPO_ROOT/program/src"

if [[ ! -f "$RULES_FILE" ]]; then
echo "error: $RULES_FILE not found" >&2
exit 2
fi

violations=0

# ---- PATH rules: file/dir must not exist ---------------------------------
while IFS= read -r line; do
# Strip comments and trim
line="${line%%#*}"
line="${line#"${line%%[![:space:]]*}"}"
line="${line%"${line##*[![:space:]]}"}"
[[ -z "$line" ]] && continue

if [[ "$line" == PATH\ * ]]; then
target="${line#PATH }"
if [[ -e "$REPO_ROOT/$target" ]]; then
echo "✗ forbidden path exists: $target" >&2
violations=$((violations + 1))
fi
fi
done < "$RULES_FILE"

# ---- SYMBOL rules: regex must not match in program/src/**.rs -------------
if [[ -d "$SOURCE_ROOT" ]]; then
while IFS= read -r line; do
line="${line%%#*}"
line="${line#"${line%%[![:space:]]*}"}"
line="${line%"${line##*[![:space:]]}"}"
[[ -z "$line" ]] && continue

if [[ "$line" == SYMBOL\ * ]]; then
pattern="${line#SYMBOL }"
# -E extended regex; -r recursive; --include filters; -l list files;
# -n line numbers; -H show filename. Use -lE first to detect quickly.
if matches=$(grep -rEn --include='*.rs' -- "$pattern" "$SOURCE_ROOT" 2>/dev/null); then
echo "✗ forbidden symbol /$pattern/ matched:" >&2
# Indent each match line for readability
printf '%s\n' "$matches" | sed 's/^/ /' >&2
violations=$((violations + 1))
fi
fi
done < "$RULES_FILE"
fi

if (( violations > 0 )); then
echo "" >&2
echo "FAIL: $violations fee-surface violation(s) found." >&2
echo "Either remove the offending file/symbol, or update scripts/fee-paths.txt" >&2
echo "if the rule itself is wrong." >&2
exit 1
fi

echo "✓ program-v2 source is fee-surface clean."
51 changes: 51 additions & 0 deletions scripts/fee-paths.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# ============================================================================
# Fee surface declaration — must NEVER exist in program-v2.
#
# Read by:
# scripts/check-no-fee.sh — fails CI if any rule below matches
# scripts/strip-fee.sh — auto-deletes PATH entries after cherry-pick
#
# Format (one rule per line, prefix-tagged):
# PATH <repo-relative path or directory>
# Fails if the file or directory exists.
# strip-fee.sh removes it via `git rm -rf`.
# SYMBOL <ERE regex>
# Fails if the regex matches in any tracked .rs file under program/src/.
# strip-fee.sh DOES NOT auto-edit source — the user must hand-resolve.
#
# Lines starting with `#` and blank lines are ignored.
#
# Why this file exists:
# program-v2 is the "no-profit" foundation clone of lazorkit-protocol.
# When cherry-picking feature commits from lazorkit-protocol, the fee/admin
# paths must be excluded. This file is the single source of truth for what
# counts as "fee surface".
# ============================================================================

# --- State accounts (fee/admin storage) -------------------------------------
PATH program/src/state/protocol_config.rs
PATH program/src/state/treasury_shard.rs
PATH program/src/state/integrator_record.rs

# --- Processor module (admin-only instructions) ----------------------------
PATH program/src/processor/protocol

# --- Type names (fee state structs) ----------------------------------------
SYMBOL \bProtocolConfig\b
SYMBOL \bTreasuryShard\b
SYMBOL \bFeeRecord\b
SYMBOL \bIntegratorRecord\b

# --- Function names (fee collection internals) -----------------------------
SYMBOL \btry_collect_fee\b

# --- Field names (fee config) ----------------------------------------------
SYMBOL \bcreation_fee\b
SYMBOL \bexecution_fee\b

# --- Instruction handler module paths (admin-only) -------------------------
SYMBOL \binitialize_protocol\b
SYMBOL \bupdate_protocol\b
SYMBOL \bregister_integrator\b
SYMBOL \bwithdraw_treasury\b
SYMBOL \binitialize_treasury_shard\b
106 changes: 106 additions & 0 deletions scripts/strip-fee.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#!/usr/bin/env bash
# Post-cherry-pick cleanup: remove fee/admin surface that may have been
# introduced by an upstream patch from lazorkit-protocol.
#
# Workflow:
# 1. Apply the upstream patch: git am --3way path/to/feature.mbox
# or: git apply path/to/feature.patch && git add -A
# 2. Run this script: ./scripts/strip-fee.sh
# 3. Hand-resolve any remaining symbol leaks reported below.
# 4. Verify: ./scripts/check-no-fee.sh
# 5. Commit the cleaned result.
#
# Behavior:
# - PATH rules are auto-removed via `git rm -rf` (when present).
# - SYMBOL rules are NOT auto-edited — stripping a struct/function reference
# from the middle of a file requires understanding context. The script
# prints each offending file:line and exits non-zero so the user notices.
#
# Use --dry-run to see what would happen without modifying anything.

set -euo pipefail

REPO_ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
RULES_FILE="$REPO_ROOT/scripts/fee-paths.txt"
SOURCE_ROOT="$REPO_ROOT/program/src"

DRY_RUN=0
if [[ "${1:-}" == "--dry-run" ]]; then
DRY_RUN=1
fi

if [[ ! -f "$RULES_FILE" ]]; then
echo "error: $RULES_FILE not found" >&2
exit 2
fi

cd "$REPO_ROOT"

removed=0
symbol_leaks=0

# ---- PATH rules: auto-remove via git rm ----------------------------------
echo "→ Removing forbidden paths..."
while IFS= read -r line; do
line="${line%%#*}"
line="${line#"${line%%[![:space:]]*}"}"
line="${line%"${line##*[![:space:]]}"}"
[[ -z "$line" ]] && continue

if [[ "$line" == PATH\ * ]]; then
target="${line#PATH }"
if [[ -e "$target" ]]; then
if (( DRY_RUN )); then
echo " [dry-run] would remove: $target"
else
# Use git rm if tracked, otherwise plain rm. -f ignores missing.
if git ls-files --error-unmatch -- "$target" >/dev/null 2>&1; then
git rm -rf --quiet -- "$target"
else
rm -rf -- "$target"
fi
echo " removed: $target"
fi
removed=$((removed + 1))
fi
fi
done < "$RULES_FILE"

if (( removed == 0 )); then
echo " (no forbidden paths present)"
fi

# ---- SYMBOL rules: report only, don't edit -------------------------------
echo ""
echo "→ Scanning for forbidden symbols in program/src/**.rs..."
if [[ -d "$SOURCE_ROOT" ]]; then
while IFS= read -r line; do
line="${line%%#*}"
line="${line#"${line%%[![:space:]]*}"}"
line="${line%"${line##*[![:space:]]}"}"
[[ -z "$line" ]] && continue

if [[ "$line" == SYMBOL\ * ]]; then
pattern="${line#SYMBOL }"
if matches=$(grep -rEn --include='*.rs' -- "$pattern" "$SOURCE_ROOT" 2>/dev/null); then
echo " ✗ /$pattern/ leaked into source:"
printf '%s\n' "$matches" | sed 's/^/ /'
symbol_leaks=$((symbol_leaks + 1))
fi
fi
done < "$RULES_FILE"
fi

echo ""
if (( symbol_leaks > 0 )); then
echo "FAIL: $symbol_leaks symbol leak(s) require manual cleanup." >&2
echo "Edit the files above to remove fee references, then re-run check-no-fee.sh." >&2
exit 1
fi

if (( DRY_RUN )); then
echo "✓ dry-run complete. $removed path(s) would be removed."
else
echo "✓ strip complete. $removed path(s) removed, no symbol leaks."
echo " Run ./scripts/check-no-fee.sh to verify."
fi
Loading