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 { parse } from './parser.js';
|
||||||
import { transpile } from './transpiler.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 {
|
function main(): void {
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
if (args.length < 2) {
|
if (args.length < 2) {
|
||||||
|
|
@ -31,6 +69,11 @@ function main(): void {
|
||||||
|
|
||||||
const { functionBody, ciVar, anchorShareVar, anchorWidthVar, discoveryDepthVar } = transpile(ast);
|
const { functionBody, ciVar, anchorShareVar, anchorWidthVar, discoveryDepthVar } = transpile(ast);
|
||||||
|
|
||||||
|
assertNonNegativeLiteral(ciVar, 'ci');
|
||||||
|
assertNonNegativeLiteral(anchorShareVar, 'anchorShare');
|
||||||
|
assertUint24Range(anchorWidthVar, 'anchorWidth');
|
||||||
|
assertNonNegativeLiteral(discoveryDepthVar, 'discoveryDepth');
|
||||||
|
|
||||||
const solidityLines = [
|
const solidityLines = [
|
||||||
'// SPDX-License-Identifier: GPL-3.0-or-later',
|
'// SPDX-License-Identifier: GPL-3.0-or-later',
|
||||||
'pragma solidity ^0.8.19;',
|
'pragma solidity ^0.8.19;',
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# test_transpiler_clamping.sh — Tests that the transpiler clamps anchorWidth to uint24 bounds.
|
# test_transpiler_clamping.sh — Tests transpiler validation of output register values.
|
||||||
# Verifies that a Push3 program producing 1e18 for anchorWidth generates
|
# Verifies that:
|
||||||
# `uint24(... % (2**24))` rather than a raw overflowing literal.
|
# - 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.
|
# Exit: 0 if all tests pass, 1 if any fail.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
|
@ -42,45 +45,145 @@ assert_not_contains() {
|
||||||
fi
|
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 ────────────────────────────────────────────────
|
# ── Ensure node_modules exist ────────────────────────────────────────────────
|
||||||
if [ ! -d "$SCRIPT_DIR/node_modules" ]; then
|
if [ ! -d "$SCRIPT_DIR/node_modules" ]; then
|
||||||
(cd "$SCRIPT_DIR" && npm install --silent)
|
(cd "$SCRIPT_DIR" && npm install --silent)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Test 5: anchorWidth = 1e18 is clamped to uint24 range ───────────────────
|
|
||||||
echo "Test 5: anchorWidth overflow clamped via % (2**24)"
|
|
||||||
|
|
||||||
TMPDIR_T=$(mktemp -d)
|
TMPDIR_T=$(mktemp -d)
|
||||||
trap 'rm -rf "$TMPDIR_T"' EXIT
|
trap 'rm -rf "$TMPDIR_T"' EXIT
|
||||||
|
|
||||||
INPUT_PUSH3="$TMPDIR_T/overflow.push3"
|
run_transpiler() {
|
||||||
OUTPUT_SOL="$TMPDIR_T/overflow.sol"
|
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).
|
# 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
|
# 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.
|
# primed input slots, so they become the 4 output vars.
|
||||||
cat > "$INPUT_PUSH3" <<'PUSH3EOF'
|
STDERR=$(run_transpiler "(
|
||||||
(
|
|
||||||
300000000000000000
|
300000000000000000
|
||||||
1000000000000000000
|
1000000000000000000
|
||||||
300000000000000000
|
300000000000000000
|
||||||
0
|
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
|
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 \
|
# ── Test 7: negative ci literal → transpiler error ───────────────────────────
|
||||||
"anchorWidth: no raw uint24(1000000000000000000) literal" \
|
echo "Test 7: ci = -1 (negative literal) → transpiler error"
|
||||||
"$SOL_CONTENT" \
|
|
||||||
"uint24(1000000000000000000)"
|
STDERR=$(run_transpiler "(
|
||||||
|
300000000000000000
|
||||||
|
100
|
||||||
|
300000000000000000
|
||||||
|
-1
|
||||||
|
)")
|
||||||
|
|
||||||
assert_contains \
|
assert_contains \
|
||||||
"anchorWidth: output uses % (2**24) clamping" \
|
"negative ci: transpiler emits error message" \
|
||||||
"$SOL_CONTENT" \
|
"$STDERR" \
|
||||||
"uint24(1000000000000000000 % (2**24))"
|
"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 ──────────────────────────────────────────────────────────────────
|
# ── Summary ──────────────────────────────────────────────────────────────────
|
||||||
echo ""
|
echo ""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue