Skip to content
Open
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
178 changes: 155 additions & 23 deletions script/check-published-deploy-constants.sh
Original file line numberDiff line numberDiff line change
Expand Up@@ -2,50 +2,182 @@
# SPDX-License-Identifier: LicenseRef-DCL-1.0
# SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
#
# Prints "OK" iff every version published to the soldeer registry for
# `rain-math-float-deploy` has a full suite of pinned deploy constants in
# LibDecimalFloatDeploy.sol: a log-tables address + codehash and a DecimalFloat
# address + codehash, each suffixed with the version.
# Checks that the per-version deploy constants pinned in LibDecimalFloatDeploy.sol
# are complete. Each published version needs a full suite: a log-tables address +
# codehash and a DecimalFloat address + codehash, each suffixed with the version
# (dots replaced by underscores).
#
# Two halves, split by what they depend on:
#
# 1. STRUCTURAL (offline). Every version suffix that carries any pinned
# constant must carry all four. Catches a half-written release snapshot.
# Pure file inspection, so it is deterministic and always runs.
# 2. REGISTRY (online). Every version published to the soldeer registry must
# carry a pinned suite, so publishing a tag without pinning its constants
# is caught. Needs api.soldeer.xyz.
#
# Usage:
# check-published-deploy-constants.sh [--lib <path>] [--offline]
# [--registry-response <path>]
#
# --lib <path> file to inspect (default src/lib/deploy/LibDecimalFloatDeploy.sol)
# --offline run the structural half only, and never touch the network.
# Lets a test assert the structural half deterministically
# instead of depending on whether the registry answered.
# --registry-response <path>
# use the contents of <path> as the registry response instead
# of fetching one, so a test can drive the registry half
# deterministically. Stands in for a fetch that SUCCEEDED;
# there is no stand-in for one that failed, because that path
# is what every offline CI run already takes.
#
# Consumed by LibDecimalFloatDeployTaggedConstants.t.sol via FFI. Output is one
# of:
# OK - every published version has its full constant suite
# MISSING: <names...> - one or more expected constants are absent
# SKIP: <reason> - the registry could not be reached (nothing verified)
# OK - every half that ran, passed
# MISSING: <names...> - one or more expected constants are absent
# SKIP: <reason> - default mode only: the structural half passed but
# the registry could not be fetched, so that half
# did not run
# UNREADABLE: <reason> - the registry answered and no version could be read
# out of the answer. A failure, not a skip: see the
# registry half below for why.
#
# Always exits 0 so the test sees the message rather than an ffi failure.

set -uo pipefail

lib="src/lib/deploy/LibDecimalFloatDeploy.sol"
offline=0
registry_response=""

versions=$(
curl -fsS --connect-timeout 5 --max-time 20 --retry 2 --retry-delay 1 \
"https://api.soldeer.xyz/api/v1/revision?project_name=rain-math-float-deploy" 2>/dev/null \
| grep -oE '"version":"[0-9][0-9.]*"' | cut -d'"' -f4 | sort -u
)
while [ "$#" -gt 0 ]; do
case "$1" in
--lib)
lib="${2:-}"
if [ -z "$lib" ]; then
printf 'MISSING: --lib requires a path'
exit 0
fi
shift 2
;;
--offline)
offline=1
shift
;;
--registry-response)
registry_response="${2:-}"
if [ -z "$registry_response" ]; then
printf 'MISSING: --registry-response requires a path'
exit 0
fi
if [ ! -f "$registry_response" ]; then
printf 'MISSING: no such registry response file %s' "$registry_response"
exit 0
fi
shift 2
;;
*)
printf 'MISSING: unknown argument %s' "$1"
exit 0
;;
esac
done

if [ ! -f "$lib" ]; then
printf 'MISSING: no such file %s' "$lib"
exit 0
fi

if [ -z "$versions" ]; then
printf 'SKIP: could not fetch published soldeer versions'
if [ "$offline" -eq 1 ] && [ -n "$registry_response" ]; then
printf 'MISSING: --offline and --registry-response are mutually exclusive'
exit 0
fi

# The deploy constants that must be pinned for every published version, suffixed
# with the version (dots replaced by underscores).
# The deploy constants that must be pinned for every version.
bases="ZOLTU_DEPLOYED_LOG_TABLES_ADDRESS \
LOG_TABLES_DATA_CONTRACT_HASH \
ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESS \
DECIMAL_FLOAT_CONTRACT_HASH"

missing=""
for v in $versions; do
suffix=$(printf '%s' "$v" | tr . _)
for b in $bases; do
name="${b}_${suffix}"
grep -qE "constant ${name} =" "$lib" || missing="${missing} ${name}"

# Append every `<base>_<suffix>` absent from the lib to $missing.
check_suffixes() {
for suffix in $1; do
for b in $bases; do
name="${b}_${suffix}"
grep -qE "constant ${name} =" "$lib" || missing="${missing} ${name}"
done
done
done
}

# 1. Structural half. Collect every version suffix carrying at least one pinned
# constant, then demand the whole suite for each. Requiring `_[0-9]` after the
# base keeps the un-suffixed "current" constants out of the suffix set.
pinned_suffixes=$(
for b in $bases; do
grep -oE "constant ${b}_[0-9][0-9_]* =" "$lib" \
| sed -E "s/^constant ${b}_//; s/ =\$//"
done | sort -u
)
check_suffixes "$pinned_suffixes"

# 2. Registry half.
#
# Fetching and reading are kept apart on purpose. Collapsing them makes an
# answer nobody can read look exactly like no answer at all, and "no answer" is
# the branch that reports SKIP — so a change in the registry's response shape
# would silently disable this half forever while every run stayed green.
#
# A fetch that fails is a SKIP: the endpoint 404s for a project with no
# published revisions, which is the state this repo is in until the first
# `sol-v*` tag publishes, and a network that is simply down is not a finding.
#
# A fetch that SUCCEEDS and yields no version is UNREADABLE, and that is a
# failure. This endpoint only answers 2xx for a project that exists, and a
# project exists on the registry because it has revisions; so a readable 2xx
# always carries at least one `"version"`. Zero of them means the response no
# longer looks the way this script reads it.
versions=""
registry_answered=0
if [ "$offline" -eq 0 ]; then
if [ -n "$registry_response" ]; then
payload=$(cat "$registry_response")
registry_answered=1
elif payload=$(
curl -fsS --connect-timeout 5 --max-time 20 --retry 2 --retry-delay 1 \
"https://api.soldeer.xyz/api/v1/revision?project_name=rain-math-float-deploy" 2>/dev/null
); then
registry_answered=1
fi

if [ "$registry_answered" -eq 1 ]; then
# Tolerant of whitespace around the colon so that a pretty-printed response
# reads as a response rather than as an unreadable one.
versions=$(
printf '%s' "$payload" \
| grep -oE '"version"[[:space:]]*:[[:space:]]*"[0-9][0-9.]*"' \
| sed -E 's/.*"([0-9][0-9.]*)"$/\1/' \
| sort -u
)
fi

if [ -n "$versions" ]; then
check_suffixes "$(printf '%s' "$versions" | tr . _)"
fi
fi

# An absence outranks an unreachable registry: a MISSING from the structural
# half is a real failure whether or not the registry answered.
if [ -n "$missing" ]; then
printf 'MISSING:%s' "$missing"
printf 'MISSING:'
printf '%s' "$missing" | tr ' ' '\n' | grep -v '^$' | sort -u | while IFS= read -r n; do
printf ' %s' "$n"
done
elif [ "$offline" -eq 0 ] && [ "$registry_answered" -eq 1 ] && [ -z "$versions" ]; then
printf 'UNREADABLE: the soldeer registry answered but no version could be read from the response; the registry half did not run'
elif [ "$offline" -eq 0 ] && [ "$registry_answered" -eq 0 ]; then
printf 'SKIP: could not fetch published soldeer versions; pinned constant suites are structurally complete'
else
printf 'OK'
fi
16 changes: 16 additions & 0 deletions test/fixtures/half-pinned-deploy-constants.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
# SPDX-License-Identifier: LicenseRef-DCL-1.0
# SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
#
# Fixture for LibDecimalFloatDeployTaggedConstantsTest. Stands in for
# LibDecimalFloatDeploy.sol and deliberately pins version 9.9.9 only HALFWAY:
# the two address constants are present, the two codehash constants are not.
# The structural half of script/check-published-deploy-constants.sh must report
# both absent codehashes for it.
#
# Deliberately NOT a .sol file: forge compiles everything under test/, and this
# is grep fodder rather than Solidity. The script only ever matches
# `constant <NAME> =`, so the surrounding prose is inert.

address constant ZOLTU_DEPLOYED_LOG_TABLES_ADDRESS_9_9_9 = address(0xc51a14251b0dcF0ae24A96b7153991378938f5F5);

address constant ZOLTU_DEPLOYED_DECIMAL_FLOAT_ADDRESS_9_9_9 = address(0x799632d282178e770C7465cad54aDA1021A913D6);
35 changes: 35 additions & 0 deletions test/fixtures/registry-response-pretty-printed.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
# SPDX-License-Identifier: LicenseRef-DCL-1.0
# SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
#
# Fixture for LibDecimalFloatDeployTaggedConstantsTest. The same single
# published version as registry-response-unpinned-version.txt, laid out with
# indentation and a space after each colon.
#
# Whitespace inside JSON carries no meaning, so both spellings say the same
# thing and script/check-published-deploy-constants.sh must read the same
# version out of either. A version scan that only matched the compact spelling
# would read this one as carrying no versions at all, which is the shape the
# script treats as a failure - so this fixture is what keeps a purely cosmetic
# change at the registry from being reported as a broken response.
#
# Deliberately NOT a .json file: this is grep fodder, and the SPDX header above
# is not JSON. The script only ever matches the `"version"` key, so the
# surrounding prose is inert.

{
"data": [
{
"created_at": "2026-08-21T09:09:09.999999Z",
"deleted": false,
"downloads": 999,
"file_size": 99999,
"id": "99999999-9999-4999-8999-999999999999",
"internal_name": "rain-math-float-deploy/9_9_9_21-08-2026_09:09:09_rain-math-float-deploy-9.9.zip",
"private": false,
"project_id": "88888888-8888-4888-8888-888888888888",
"uploader": "77777777-7777-4777-8777-777777777777",
"url": "https://soldeer-revisions.s3.amazonaws.com/rain-math-float-deploy/9_9_9_21-08-2026_09:09:09_rain-math-float-deploy-9.9.zip",
"version": "9.9.9"
}
]
}
20 changes: 20 additions & 0 deletions test/fixtures/registry-response-unpinned-version.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
# SPDX-License-Identifier: LicenseRef-DCL-1.0
# SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
#
# Fixture for LibDecimalFloatDeployTaggedConstantsTest. Stands in for the
# api.soldeer.xyz response that
# script/check-published-deploy-constants.sh reads, fed to it with
# --registry-response. Shaped like the live endpoint's answer for a project that
# has revisions: a `data` array of revision records, compact, no spaces around
# the colons.
#
# It publishes 9.9.9, a version LibDecimalFloatDeploy pins nothing for, so the
# registry half must report all four of that version's constants absent. The
# other fields carry digits of their own, so a scan that reads any number it
# finds rather than the version key reports the wrong thing.
#
# Deliberately NOT a .json file: this is grep fodder, and the SPDX header above
# is not JSON. The script only ever matches the `"version"` key, so the
# surrounding prose is inert.

{"data":[{"created_at":"2026-08-21T09:09:09.999999Z","deleted":false,"downloads":999,"file_size":99999,"id":"99999999-9999-4999-8999-999999999999","internal_name":"rain-math-float-deploy/9_9_9_21-08-2026_09:09:09_rain-math-float-deploy-9.9.zip","private":false,"project_id":"88888888-8888-4888-8888-888888888888","uploader":"77777777-7777-4777-8777-777777777777","url":"https://soldeer-revisions.s3.amazonaws.com/rain-math-float-deploy/9_9_9_21-08-2026_09:09:09_rain-math-float-deploy-9.9.zip","version":"9.9.9"}]}
22 changes: 22 additions & 0 deletions test/fixtures/registry-response-unreadable.txt
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
# SPDX-License-Identifier: LicenseRef-DCL-1.0
# SPDX-FileCopyrightText: Copyright (c) 2020 Rain Open Source Software Ltd
#
# Fixture for LibDecimalFloatDeployTaggedConstantsTest. A registry answer that
# arrived intact and that script/check-published-deploy-constants.sh cannot read
# a version out of: every revision record names its version under
# `revision_number` instead, which is what a rename of that field at the
# registry would look like.
#
# The endpoint answers 404 for a project with no published revisions, so a
# response that arrives at all describes a project that has revisions and must
# name their versions somewhere. Reading none out of it therefore means the
# response no longer looks the way the script reads it, and the script must
# report UNREADABLE rather than SKIP: SKIP retires the registry half, and
# retiring it on a response shape change is how the half would go quiet
# permanently while every run stayed green.
#
# Deliberately NOT a .json file: this is grep fodder, and the SPDX header above
# is not JSON. The script only ever matches the `"version"` key, so the
# surrounding prose is inert.

{"data":[{"created_at":"2026-08-21T09:09:09.999999Z","deleted":false,"downloads":999,"file_size":99999,"id":"99999999-9999-4999-8999-999999999999","internal_name":"rain-math-float-deploy/9_9_9_21-08-2026_09:09:09_rain-math-float-deploy-9.9.zip","private":false,"project_id":"88888888-8888-4888-8888-888888888888","uploader":"77777777-7777-4777-8777-777777777777","url":"https://soldeer-revisions.s3.amazonaws.com/rain-math-float-deploy/9_9_9_21-08-2026_09:09:09_rain-math-float-deploy-9.9.zip","revision_number":"9.9.9"}]}
Loading
Loading