Merge pull request 'fix: ci, anchorShare, discoveryDepth casts are unguarded for the same literal problem (#905)' (#959) from fix/issue-905 into master
This commit is contained in:
commit
f4201ee7ef
2 changed files with 165 additions and 19 deletions
|
|
@ -13,6 +13,44 @@ import * as path from 'path';
|
|||
import { parse } from './parser.js';
|
||||
import { transpile } from './transpiler.js';
|
||||
|
||||
const INT_LITERAL_RE = /^-?\d+$/;
|
||||
|
||||
/**
|
||||
* Validates that a transpiler output variable is a non-negative integer literal
|
||||
* (or a Solidity expression, which is unchecked). Throws a clear error rather
|
||||
* than silently emitting `uint256(<negative>)` which Solidity would reject with a
|
||||
* cryptic compile-time message.
|
||||
*/
|
||||
function assertNonNegativeLiteral(value: string, name: string): void {
|
||||
if (INT_LITERAL_RE.test(value)) {
|
||||
const n = BigInt(value);
|
||||
if (n < 0n) {
|
||||
throw new Error(
|
||||
`Transpiler error: ${name} is a negative literal (${value}). ` +
|
||||
`uint256(${value}) is rejected by the Solidity compiler. ` +
|
||||
`Fix the Push3 program to produce a non-negative value for ${name}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a transpiler output variable for a uint24 field is within the
|
||||
* valid range [0, 16777215] when it is a literal constant. Large literals that
|
||||
* are computed at runtime are still wrapped with % (2**24) in the generated code.
|
||||
*/
|
||||
function assertUint24Range(value: string, name: string): void {
|
||||
if (INT_LITERAL_RE.test(value)) {
|
||||
const n = BigInt(value);
|
||||
if (n < 0n || n > 16777215n) {
|
||||
throw new Error(
|
||||
`Transpiler error: ${name} literal (${value}) is outside uint24 range [0, 16777215]. ` +
|
||||
`Fix the Push3 program to produce a value in [0, 16777215] for ${name}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function main(): void {
|
||||
const args = process.argv.slice(2);
|
||||
if (args.length < 2) {
|
||||
|
|
@ -31,6 +69,11 @@ function main(): void {
|
|||
|
||||
const { functionBody, ciVar, anchorShareVar, anchorWidthVar, discoveryDepthVar } = transpile(ast);
|
||||
|
||||
assertNonNegativeLiteral(ciVar, 'ci');
|
||||
assertNonNegativeLiteral(anchorShareVar, 'anchorShare');
|
||||
assertUint24Range(anchorWidthVar, 'anchorWidth');
|
||||
assertNonNegativeLiteral(discoveryDepthVar, 'discoveryDepth');
|
||||
|
||||
const solidityLines = [
|
||||
'// SPDX-License-Identifier: GPL-3.0-or-later',
|
||||
'pragma solidity ^0.8.19;',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
#!/usr/bin/env bash
|
||||
# test_transpiler_clamping.sh — Tests that the transpiler clamps anchorWidth to uint24 bounds.
|
||||
# Verifies that a Push3 program producing 1e18 for anchorWidth generates
|
||||
# `uint24(... % (2**24))` rather than a raw overflowing literal.
|
||||
# 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
|
||||
|
||||
|
|
@ -42,45 +45,145 @@ assert_not_contains() {
|
|||
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
|
||||
|
||||
# ── Test 5: anchorWidth = 1e18 is clamped to uint24 range ───────────────────
|
||||
echo "Test 5: anchorWidth overflow clamped via % (2**24)"
|
||||
|
||||
TMPDIR_T=$(mktemp -d)
|
||||
trap 'rm -rf "$TMPDIR_T"' EXIT
|
||||
|
||||
INPUT_PUSH3="$TMPDIR_T/overflow.push3"
|
||||
OUTPUT_SOL="$TMPDIR_T/overflow.sol"
|
||||
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" && node --loader ts-node/esm 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.
|
||||
cat > "$INPUT_PUSH3" <<'PUSH3EOF'
|
||||
(
|
||||
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" && node --loader ts-node/esm src/index.ts "$INPUT_PUSH3" "$OUTPUT_SOL" 2>/dev/null)
|
||||
(cd "$SCRIPT_DIR" && node --loader ts-node/esm src/index.ts "$INPUT_6" "$OUTPUT_6" 2>/dev/null)
|
||||
|
||||
SOL_CONTENT=$(cat "$OUTPUT_SOL")
|
||||
SOL_6=$(cat "$OUTPUT_6")
|
||||
assert_contains \
|
||||
"anchorWidth 100: output uses % (2**24)" \
|
||||
"$SOL_6" \
|
||||
"uint24(100 % (2**24))"
|
||||
|
||||
assert_not_contains \
|
||||
"anchorWidth: no raw uint24(1000000000000000000) literal" \
|
||||
"$SOL_CONTENT" \
|
||||
"uint24(1000000000000000000)"
|
||||
# ── 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 \
|
||||
"anchorWidth: output uses % (2**24) clamping" \
|
||||
"$SOL_CONTENT" \
|
||||
"uint24(1000000000000000000 % (2**24))"
|
||||
"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]"
|
||||
|
||||
# ── Summary ──────────────────────────────────────────────────────────────────
|
||||
echo ""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue