harb/tools/push3-transpiler/test_transpiler_clamping.sh
openhands 4a24217030 fix: inject.sh uses ts-node which is broken on Node >= 22 ESM (#1008)
Replace ts-node with tsx across all push3-transpiler scripts:
- inject.sh: npx ts-node → npx tsx
- test_inject_extraction.sh: node --loader ts-node/esm → npx tsx
- test_transpiler_clamping.sh: node --loader ts-node/esm → npx tsx
- package.json: ts-node devDep → tsx, transpile script updated
- tsconfig.json: removed ts-node config block
- src/index.ts: updated usage comments

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 21:44:29 +00:00

268 lines
8.1 KiB
Bash
Executable file

#!/usr/bin/env bash
# test_transpiler_clamping.sh — Tests transpiler validation of output register values.
# Verifies that:
# - negative literal outputs for ci, anchorShare, anchorWidth, discoveryDepth produce
# a transpiler-level error with a clear message (not a silent Solidity compile failure).
# - an anchorWidth literal outside [0, 16777215] produces a transpiler-level error.
# - a valid anchorWidth literal within range is accepted and still uses % (2**24).
# Exit: 0 if all tests pass, 1 if any fail.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PASS=0
FAIL=0
assert_contains() {
local name="$1"
local output="$2"
local expected="$3"
if echo "$output" | grep -qF "$expected"; then
echo " PASS: $name"
PASS=$((PASS + 1))
else
echo " FAIL: $name"
echo " expected to contain: $expected"
echo " actual output:"
echo "$output" | sed 's/^/ /'
FAIL=$((FAIL + 1))
fi
}
assert_not_contains() {
local name="$1"
local output="$2"
local pattern="$3"
if echo "$output" | grep -qF "$pattern"; then
echo " FAIL: $name"
echo " expected NOT to contain: $pattern"
echo " actual output:"
echo "$output" | sed 's/^/ /'
FAIL=$((FAIL + 1))
else
echo " PASS: $name"
PASS=$((PASS + 1))
fi
}
assert_exits_nonzero() {
local name="$1"
local stderr_output="$2"
local expected_fragment="$3"
# stderr_output is non-empty only if the command failed (captured separately)
if echo "$stderr_output" | grep -qF "$expected_fragment"; then
echo " PASS: $name"
PASS=$((PASS + 1))
else
echo " FAIL: $name"
echo " expected stderr to contain: $expected_fragment"
echo " actual stderr:"
echo "$stderr_output" | sed 's/^/ /'
FAIL=$((FAIL + 1))
fi
}
# ── Ensure node_modules exist ────────────────────────────────────────────────
if [ ! -d "$SCRIPT_DIR/node_modules" ]; then
(cd "$SCRIPT_DIR" && npm install --silent)
fi
TMPDIR_T=$(mktemp -d)
trap 'rm -rf "$TMPDIR_T"' EXIT
run_transpiler() {
local push3_content="$1"
local input_file="$TMPDIR_T/test_input.push3"
local output_file="$TMPDIR_T/test_output.sol"
printf '%s\n' "$push3_content" > "$input_file"
rm -f "$output_file"
local stderr_out
# Capture stderr; allow non-zero exit without aborting
if stderr_out=$(cd "$SCRIPT_DIR" && npx tsx src/index.ts "$input_file" "$output_file" 2>&1 >/dev/null); then
echo "OK"
else
printf '%s' "$stderr_out"
fi
}
# ── Test 5: anchorWidth literal > uint24 max → transpiler error ──────────────
echo "Test 5: anchorWidth = 1e18 (literal) → transpiler error, not silent Solidity failure"
# Push3 program: push 4 values (discoveryDepth, anchorWidth=1e18, anchorShare, ci).
# The transpiler pops top-4 from DYADIC stack; these literals sit on top of the
# primed input slots, so they become the 4 output vars.
STDERR=$(run_transpiler "(
300000000000000000
1000000000000000000
300000000000000000
0
)")
assert_contains \
"anchorWidth 1e18: transpiler emits range error message" \
"$STDERR" \
"anchorWidth literal (1000000000000000000) is outside uint24 range [0, 16777215]"
# ── Test 6: valid anchorWidth literal (in range) → accepted, wraps with % (2**24) ─
echo "Test 6: anchorWidth = 100 (valid literal) → accepted, output uses % (2**24)"
INPUT_6="$TMPDIR_T/valid_aw.push3"
OUTPUT_6="$TMPDIR_T/valid_aw.sol"
cat > "$INPUT_6" <<'PUSH3EOF'
(
300000000000000000
100
300000000000000000
0
)
PUSH3EOF
(cd "$SCRIPT_DIR" && npx tsx src/index.ts "$INPUT_6" "$OUTPUT_6" 2>/dev/null)
SOL_6=$(cat "$OUTPUT_6")
assert_contains \
"anchorWidth 100: output uses % (2**24)" \
"$SOL_6" \
"uint24(100 % (2**24))"
# ── Test 7: negative ci literal → transpiler error ───────────────────────────
echo "Test 7: ci = -1 (negative literal) → transpiler error"
STDERR=$(run_transpiler "(
300000000000000000
100
300000000000000000
-1
)")
assert_contains \
"negative ci: transpiler emits error message" \
"$STDERR" \
"ci is a negative literal (-1)"
# ── Test 8: negative anchorShare literal → transpiler error ──────────────────
echo "Test 8: anchorShare = -5 (negative literal) → transpiler error"
STDERR=$(run_transpiler "(
300000000000000000
100
-5
0
)")
assert_contains \
"negative anchorShare: transpiler emits error message" \
"$STDERR" \
"anchorShare is a negative literal (-5)"
# ── Test 9: negative discoveryDepth literal → transpiler error ───────────────
echo "Test 9: discoveryDepth = -99 (negative literal) → transpiler error"
STDERR=$(run_transpiler "(
-99
100
300000000000000000
0
)")
assert_contains \
"negative discoveryDepth: transpiler emits error message" \
"$STDERR" \
"discoveryDepth is a negative literal (-99)"
# ── Test 10: negative anchorWidth literal → transpiler error ─────────────────
echo "Test 10: anchorWidth = -1 (negative literal) → transpiler error"
STDERR=$(run_transpiler "(
300000000000000000
-1
300000000000000000
0
)")
assert_contains \
"negative anchorWidth: transpiler emits range error message" \
"$STDERR" \
"anchorWidth literal (-1) is outside uint24 range [0, 16777215]"
# ── Test 11: forge build — transpiler output compiles (regression for #900) ──
echo "Test 11: forge build on valid transpiler output (regression for #900)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
FORGE_BIN="$(command -v forge 2>/dev/null || true)"
if [ -z "$FORGE_BIN" ] && [ -x /root/.foundry/bin/forge ]; then
FORGE_BIN=/root/.foundry/bin/forge
fi
if [ -z "$FORGE_BIN" ]; then
echo " SKIP: forge not found — add foundry bin to PATH to enable compile verification"
else
FORGE_DIR="$TMPDIR_T/forge_verify"
mkdir -p "$FORGE_DIR/src"
printf '[profile.default]\nsrc = "src"\nout = "out"\nlibs = []\nvia_ir = true\n' > "$FORGE_DIR/foundry.toml"
cp "$REPO_ROOT/onchain/src/IOptimizer.sol" "$FORGE_DIR/src/"
cp "$OUTPUT_6" "$FORGE_DIR/src/OptimizerV3Push3.sol"
FORGE_OUT=""
FORGE_RC=0
FORGE_OUT=$(cd "$FORGE_DIR" && "$FORGE_BIN" build 2>&1) || FORGE_RC=$?
if [ "$FORGE_RC" -eq 0 ]; then
echo " PASS: forge build succeeded on transpiled contract"
PASS=$((PASS + 1))
else
echo " FAIL: forge build failed — transpiled contract does not compile"
echo "$FORGE_OUT" | sed 's/^/ /'
FAIL=$((FAIL + 1))
fi
fi
# ── Test 12: ci literal > 1e18 → transpiler error ────────────────────────────
echo "Test 12: ci = 2e18 (literal) → transpiler error"
STDERR=$(run_transpiler "(
300000000000000000
100
300000000000000000
2000000000000000000
)")
assert_contains \
"ci 2e18: transpiler emits max1e18 error message" \
"$STDERR" \
"ci literal (2000000000000000000) exceeds 1e18 (max allowed)"
# ── Test 13: anchorShare literal > 1e18 → transpiler error ───────────────────
echo "Test 13: anchorShare = 2e18 (literal) → transpiler error"
STDERR=$(run_transpiler "(
300000000000000000
100
2000000000000000000
0
)")
assert_contains \
"anchorShare 2e18: transpiler emits max1e18 error message" \
"$STDERR" \
"anchorShare literal (2000000000000000000) exceeds 1e18 (max allowed)"
# ── Test 14: discoveryDepth literal > 1e18 → transpiler error ────────────────
echo "Test 14: discoveryDepth = 2e18 (literal) → transpiler error"
STDERR=$(run_transpiler "(
2000000000000000000
100
300000000000000000
0
)")
assert_contains \
"discoveryDepth 2e18: transpiler emits max1e18 error message" \
"$STDERR" \
"discoveryDepth literal (2000000000000000000) exceeds 1e18 (max allowed)"
# ── Summary ──────────────────────────────────────────────────────────────────
echo ""
echo "Results: $PASS passed, $FAIL failed"
[ "$FAIL" -eq 0 ]