Reference

Evaluation

Release v0.1.1View Markdown

This page collects the numbers I publish about Nightshade, with the denominator, version and limits next to each one. All of them come from one proof run of the released 0.1.1 CLI, installed from the release tarball on 2026-10-24.

10 / 10

showcase findings fire

10 corrected versions and 9 lookalikes stay quiet. Synthetic, one rule per scan, compiler-validated.

263 / 266

synthetic positive checks detected

3 missed, all for one rule. Controls quiet: 240/242 corrected, 237/252 lookalike. 7 incomplete. 93 rules.

258 / 300

real contract roots fully analyzed

Baseline analysis. 237/300 complete for every selected rule; 27 compiler-rejected roots stay in the denominator; 0 process failures. Aggregate only.

Nightshade 0.1.1, proof run on 2026-10-24. None of these numbers is a precision or recall estimate.

Showcase examples#

Each of the 10 examples selects one rule. The positive fired in 10 of 10, the corrected version was quiet in 10 of 10 and the lookalike was quiet in 9 of 9 (one example has no lookalike). Every variant passed Compact 0.34.0 compiler validation and complete analysis. Exit codes are from the same runs.

ExampleRuleSeverityPositiveCorrectedLookalike
Private authentication preimage reaches the public ledgerledger-bound-preimage-publicationmedium1 finding, exit 1quiet, exit 0quiet, exit 0
A public key comparison does not prove ownershipspoofable-authorizationhigh1 finding, exit 1quiet, exit 0quiet, exit 0
The signature verification result is discardedignored-validation-resultmedium1 finding, exit 1quiet, exit 0quiet, exit 0
Caller time needs a chain-time boundfreshness-bound-caller-timemedium1 finding, exit 1quiet, exit 0quiet, exit 0
Initialization can run repeatedlyre-initializationlow1 finding, exit 1quiet, exit 0quiet, exit 0
Two shielded mints reuse the same descriptorduplicate-shielded-mintmedium1 finding, exit 1quiet, exit 0quiet, exit 0
The Merkle membership result is ignoredmerkle-membership-not-enforcedmedium1 finding, exit 1quiet, exit 0quiet, exit 0
An unchecked witness value becomes public stateunconstrained-witness-to-statemedium1 finding, exit 1quiet, exit 0quiet, exit 0
A freshness check never consumes the nullifiernullifier-check-without-insertmedium1 finding, exit 1quiet, exit 0quiet, exit 0
A private commitment uses fixed randomnesspredictable-commitment-randomnessmedium1 finding, exit 1quiet, exit 0none

Synthetic corpus#

The repository's test corpus pairs each rule with positive cases that should match and corrected, lookalike or other negative cases that should stay quiet. I ran every case declared in the five corpus registries with the installed CLI.

The run made 915 distinct CLI scans covering 1161 declarations across 93 rules. 915 scans passed compiler validation, 7 were incomplete and 0 had process failures. A check counts only when the compiler accepted the file and analysis completed. Registries overlap, so the last row counts each (scan, rule, role) once.

ScopePositive detectedCorrected quietLookalike quietOther negative quietIncomplete
detector-counterexamples/manifest.json20/2020/2020/200/00
known-bugs.json105/105105/105105/1050/00
manifest.json130/130106/106109/12189/967
policies-manifest.json6/66/60/00/00
synthetic-regressions.json104/107105/107104/1070/00
All declarations (overlap kept)365/368342/344338/35389/967
Unique checks263/266240/242237/25289/967

Misses and control hits. 3 positive checks were missed, all for identity-signature-key. 17 corrected or lookalike controls still matched; the corpus labels all of them as expected matches (0 were unexpected), and they get no quiet credit. 7 checks were incomplete and count as failures, not as quiet. 21 synthetic triplets have no mapped target rule; they were scanned but not scored.

Corpus scans used budgets of 100,000 steps, 256 paths and 100,000 abstract values, with no suppressions, severity filters or baselines. The run took 1105 seconds on 4 CPU cores.

Results for each of the 93 rules
RulePositive detectedCorrected quietLookalike quietOther negative quietIncomplete
always-true-assertion1/11/11/10/00
caller-key-zero-entry-review2/22/22/20/00
caller-supplied-deadline1/11/11/10/00
caller-supplied-ledger-equality1/11/11/10/00
checked-digest-narrowing1/11/11/10/00
checkpoint-splits-atomic-update1/11/11/10/00
circuit-cost1/11/11/10/00
circuit-work-budget2/22/21/10/00
closed-membership-capacity-review6/63/32/210/100
conditional-hash-range1/11/11/10/00
configured-membership-capacity-review3/33/33/30/00
constant-condition1/11/11/10/00
current-root-policy-mismatch1/11/11/10/00
dead-code1/11/11/10/00
declared-secret-publication3/33/32/20/00
default-recipient-or-owner2/22/22/20/00
destructive-list-reset2/22/22/20/00
discarded-comparison1/11/11/10/00
discarded-shielded-send-result5/55/55/50/00
duplicate-shielded-mint1/11/11/10/00
duplicated-branch-tail1/11/11/10/00
either-payload-mismatch2/22/22/20/00
enrolled-signature-key-review1/11/11/10/00
exact-balance-threshold1/11/11/10/00
exported-shielded-descriptor3/33/33/30/00
field-arithmetic-wraparound1/11/11/10/00
field-recomposition-range-review5/54/54/50/00
forbidden-state-transition2/22/21/10/00
freshness-bound-caller-time12/1212/1212/120/00
generic-witness-maybe-abi1/11/11/10/00
identity-signature-key1/44/44/40/00
ignored-validation-result3/33/33/30/00
impossible-assertion2/22/21/10/00
inconsistent-access-control2/22/22/20/00
inconsistent-keyed-state-guard2/22/22/20/00
ledger-bound-preimage-publication7/712/121/10/00
low-entropy-hash-disclosure1/11/11/10/00
membership-identity-binding-review4/43/32/35/50
merkle-insert-without-dedupe2/22/21/20/00
merkle-leaf-binding-review6/66/66/60/00
merkle-membership-not-enforced5/55/55/50/00
missing-role-bootstrap2/22/22/20/00
mutually-exclusive-terminal-payout1/11/11/10/00
native-key-authentication-review5/55/55/50/00
nullifier-check-without-insert2/22/22/20/00
openzeppelin-preconditions6/66/66/60/00
operation-intent-policy2/22/22/20/00
permissionless-mint-egress1/11/11/10/00
predictable-commitment-randomness2/22/21/10/00
public-key-membership-authorization9/96/66/63/30
public-two-component-domain-review6/65/53/44/51
publicly-derivable-encryption-secret1/11/11/10/00
publicly-derived-guard-tag1/11/11/10/00
published-digest-generator-scalar-review4/44/44/46/60
quorum-signer-uniqueness-review2/22/22/20/00
re-initialization2/22/22/20/00
received-coin-not-validated1/11/11/10/00
redundant-boolean-branch2/22/22/20/00
redundant-self-shielded-receive2/22/22/20/00
resettable-list-nonce4/44/44/40/00
resettable-memo-nonce-review2/22/27/70/00
secret-binding-divergence-review7/72/22/39/90
shadowing1/11/11/10/00
shared-module-once-gate2/22/22/21/10
shielded-burn-amount-width2/22/22/20/00
shielded-coin-runtime-width1/11/11/10/00
shielded-contract-recipient-review2/22/22/21/10
signature-key-identity-exclusion-review3/33/32/51/10
signed-effect-input3/33/33/30/00
source-claim-map-getter-review4/42/22/210/100
source-claim-shielded-issuance-review6/62/215/160/11
split-witness-identity2/22/22/20/00
spoofable-authorization28/2828/2828/280/00
stale-nft-metadata2/22/22/20/00
stored-schnorr-key-enrollment-review2/22/22/20/00
time-dependence1/11/11/10/00
transient-digest-to-ledger1/11/11/10/00
unbounded-mint1/11/11/10/00
unconstrained-witness-to-state1/11/11/10/00
unguarded-sensitive-state-write3/33/32/20/00
unnecessary-disclose1/11/11/10/00
unprotected-value-transfer2/22/22/20/00
unshielded-refund-endpoint-review5/51/11/37/92
unsupported-contract-owner1/11/11/10/00
unused-declaration1/11/11/10/00
write-only-ledger2/21/21/20/00
zero-amount-complementary-send-review4/42/23/47/81
zero-amount-initial-membership2/22/22/20/00
zero-amount-set-consumption-review2/22/24/58/80
zero-amount-unguarded-map-debit-review2/22/22/20/00
zero-amount-unshielded-send1/11/11/10/00
zero-default-getter-membership-review7/74/42/214/140
zero-transfer-retained-list-review3/32/23/43/52
All 31 deviations

Every failed check, exact-line disagreement and incomplete observation. Rows are manifest declarations, so two rows can describe the same scan.

Registry / caseRuleKindExpected to observed linesIssue
known-bugs.json / authorization-raw-native-witnessspoofable-authorizationpositive[7] to [6]exact expectation mismatch
synthetic-regressions.json / merkle-insert-without-dedupe-39merkle-insert-without-dedupelookalike[11] to [11]control hit
synthetic-regressions.json / write-only-ledger-70write-only-ledgercorrected[3] to [3]control hit
synthetic-regressions.json / write-only-ledger-70write-only-ledgerlookalike[3] to [3]control hit
synthetic-regressions.json / identity-signature-key-71identity-signature-keypositive[6] to []miss, exact expectation mismatch
synthetic-regressions.json / identity-signature-key-71identity-signature-keylookalike[7] to []exact expectation mismatch
synthetic-regressions.json / field-recomposition-range-review-86field-recomposition-range-reviewcorrected[7] to [7]control hit
synthetic-regressions.json / field-recomposition-range-review-86field-recomposition-range-reviewlookalike[7] to [7]control hit
synthetic-regressions.json / identity-signature-key-96identity-signature-keypositive[4,7] to []miss, exact expectation mismatch
synthetic-regressions.json / identity-signature-key-96identity-signature-keylookalike[5,9] to []exact expectation mismatch
synthetic-regressions.json / identity-signature-key-97identity-signature-keypositive[10,13] to []miss, exact expectation mismatch
synthetic-regressions.json / identity-signature-key-97identity-signature-keylookalike[11,15] to []exact expectation mismatch
manifest.json / zeroAmountSetConsumptionReview40/drafts/consumption40/intentional-zero-consumption.compactzero-amount-set-consumption-reviewlookalike[15] to [15]control hit
manifest.json / publicTwoComponentDomainReview43/drafts/owner-domain43/lookalike.compactpublic-two-component-domain-reviewlookalike[15] to [15]control hit
manifest.json / publicTwoComponentDomainReview43/drafts/owner-domain43/opaque-salt-guard.compactpublic-two-component-domain-reviewnegative[] to []incomplete or unvalidated; domain-review-evidence-unavailable
manifest.json / signatureIdentityExclusion46/drafts/signature46/caller-key/lookalike.compactsignature-key-identity-exclusion-reviewlookalike[5,9] to [5,9]control hit
manifest.json / signatureIdentityExclusion46/drafts/signature46/persistent-key/lookalike.compactsignature-key-identity-exclusion-reviewlookalike[12,17] to [12,17]control hit
manifest.json / signatureIdentityExclusion46/drafts/signature46/counter/lookalike.compactsignature-key-identity-exclusion-reviewlookalike[44,45] to [44,45]control hit
manifest.json / complementarySendReview47/drafts/complementary47/boundaries/intentional-same-executable-as-positive.compactzero-amount-complementary-send-reviewlookalike[9] to [9]control hit
manifest.json / complementarySendReview47/drafts/complementary47/boundaries/persistent-map-lost-lineage-refusal.compactzero-amount-complementary-send-reviewnegative[] to []incomplete or unvalidated; complementary-send-proof-unavailable, complementary-send-preparation-unavailable
manifest.json / secretBindingDivergence49/drafts/secret-binding49/controls/intentional-weaker-same-key.compactsecret-binding-divergence-reviewlookalike[18] to [18]control hit
manifest.json / zeroListGrowth50/drafts/zero-list50/controls/intentional-zero-linked-growth.compactzero-transfer-retained-list-reviewlookalike[12] to [12]control hit
manifest.json / zeroListGrowth50/drafts/zero-list50/controls/append-then-reset.compactzero-transfer-retained-list-reviewnegative[] to []incomplete or unvalidated; list-growth-evidence-incomplete, list-growth-preparation-unavailable
manifest.json / zeroListGrowth50/drafts/zero-list50/controls/append-then-pop.compactzero-transfer-retained-list-reviewnegative[] to []incomplete or unvalidated; list-growth-evidence-incomplete, list-growth-preparation-unavailable
manifest.json / sourceClaims52/drafts/source-claims52/incomplete-unknown-tag.compactsource-claim-shielded-issuance-reviewnegative[] to []incomplete or unvalidated; source-claim-evidence-unavailable, source-claim-evidence-unavailable
manifest.json / sourceClaims52/drafts/source-claims52/must-match-intentional-claim.compactsource-claim-shielded-issuance-reviewlookalike[6] to [6]control hit
manifest.json / independentMembershipIdentity54/drafts/memo-identity44-boundaries/intentional-admin.compactmembership-identity-binding-reviewlookalike[20] to [20]control hit
manifest.json / refundEndpoint57/drafts/arithmetic-endpoint57/dynamic-map-snapshots.compactunshielded-refund-endpoint-reviewnegative[] to []incomplete or unvalidated; refund-endpoint-source-unavailable
manifest.json / refundEndpoint57/drafts/arithmetic-endpoint57/intentional-endpoint.compactunshielded-refund-endpoint-reviewlookalike[58] to [58]control hit
manifest.json / refundEndpoint57/drafts/arithmetic-endpoint57/intentional.compactunshielded-refund-endpoint-reviewlookalike[58] to [58]control hit
manifest.json / refundEndpoint57/drafts/arithmetic-endpoint57/scalar-ledger-snapshots.compactunshielded-refund-endpoint-reviewnegative[] to []incomplete or unvalidated; refund-endpoint-source-unavailable

Real contracts#

I reran the installed 0.1.1 CLI on a fixed set of 300 contract roots from 144 repositories. The assembled inputs are not redistributed, so only aggregate counts are published: no project names, paths or findings. All 300 roots stay in every denominator, including the 27 the compiler rejected, which were not scanned.

OutcomeResult
Contract roots300 (from 144 repositories)
Compiler accepted / rejected273 / 27
Baseline analysis complete258 / 300 (86.0%)
All selected rules complete237 / 300 (79.0%)
Incomplete: budget limit12
Incomplete: frontend1
Incomplete: other2
Process failures / worker-failure roots0 / 0
Finding instances315
Wall time, 4 workers157.9 s
Per root: median / p95 / max1.36 s / 6.26 s / 32.3 s

The 15 accepted roots without complete baseline analysis are coverage gaps, not clean results. One root did not get the semantic profile it requested and is counted as incomplete. The 315 finding instances are static observations under the default audit selection; I did not review them for accuracy here, so they are not confirmed vulnerabilities.

Compiler accept and reject decisions came from the authenticated frozen manifest of the original run; compilers were not rerun. Source hashes for all 300 roots and 66 dependency trees were checked before and after the run and did not change. The earlier pre-release 0.1.0 measurement on the same inputs recorded the same completion counts (258/300 and 237/300) and 315 findings.

What these numbers do not show#

  • There is no independently established precision or recall estimate for the release. Synthetic tests check defined rule behavior, not unknown vulnerabilities or ecosystem-wide recall.
  • A quiet corrected or lookalike case shows that the selected rule stopped firing. It does not establish that a contract is secure.
  • Finding counts on real contracts are static observations, not confirmed vulnerabilities, and completion counts are not accuracy.
  • Incomplete analysis and compiler rejection are never counted as clean results.
  • Compiler validation used --skip-zk, so it skips proof generation and does not establish runtime behavior.
  • Results apply to version 0.1.1 and the compiler profiles shown. Other versions can differ.

Versions and identities#

ItemIdentity
Nightshade CLINightshade 0.1.1, installed with npm from the release tarball
Release tarball SHA-256fee7af6cb124d677e7b62c90da22466dd2c0b5819de9625c11de398635a0a3d6
Node / npmv24.20.0 / 11.19.0
Grammar revision22de6ee9dbf4b92fe4e2fe3602e53f1e352b79fd
Grammar buildfef63bac6b6120f35b9e64ec29134a40a1fd5a1008ec8e8ae714f523788b5a93
Compact compiler 0.31.1 binary3054ffa89d7a4dfe24afd31c27ef37e87a95757de0fc24485f335635e26dce57
Compact compiler 0.34.0 binarybaa50cd002492b3163ac3d25bc88904d4c7a40ad490fbeb3b970ea48c7f1d3b5
Corpus source checkoutdb27d3bd3c772bd1ab181bcf0c4f86e6f8621893 (release-0.1)
Public import dependencyOpenZeppelin compact-contracts at ec1331cd8195694c747d291e8fc8ab6d2914a976
Proof run2026-10-24, CPU cores 0 to 3
Public proof packnightshade-proof-0.1.1.tar.gz, SHA-256 9d95e631dc318006610882911c0d58b7c25fa7754c39b49205c309d2e2e57b3b

The showcase source fixtures match the files under tests/corpus in the repository at v0.1.1 by SHA-256; showcase files that were trimmed say so. The corpus also contains no public audit reproductions in this release, so no result here is a claim about a third-party project.

Reproduce#

Download the proof packnightshade-proof-0.1.1.tar.gz · 1.7 MB · 4692 files

SHA-256 9d95e631dc318006610882911c0d58b7c25fa7754c39b49205c309d2e2e57b3b

It holds the synthetic input snapshot, every raw scan output with its recorded command and exit code, the per-scan configs, these ten examples, the report and the scripts that produced and check them. Local paths in it are replaced with placeholders such as <pack> and <compact-toolchains>, the release tarball is left out (it is the v0.1.1 release asset) and real-contract records keep only the aggregate numbers on the evaluation page. PUBLIC-PACK.md inside lists every change.

One example. Install the release as in getting started, download an example's files from the examples page, and run from that folder:

Shell
./.nightshade/node_modules/.bin/nightshade vulnerable.compact --config config.json
./.nightshade/node_modules/.bin/nightshade corrected.compact --config config.json

The recorded runs also passed --compiler with the path to a Compact 0.34.0 compactc to validate each file with the compiler. Expect exit code 1 and one finding for the flagged file, and exit code 0 with no findings for the corrected one.

The corpus. The fixtures and the five registries are in the repository under tests/corpus at v0.1.1. The proof pack holds the input snapshot, raw outputs, scripts and checksums of this run. To check it without rescanning:

Shell
tar -xzf nightshade-proof-0.1.1.tar.gz
cd nightshade-proof-0.1.1
sha256sum --check --quiet SHA256SUMS
mkdir -p release
curl -fL -o release/nightshade-compact-0.1.1.tgz https://github.com/DicksonWu654/nightshade/releases/download/v0.1.1/nightshade-compact-0.1.1.tgz
python3 scripts/validate.py

validate.py recomputes every count on this page from the recorded outputs and checks their hashes. To rescan the synthetic corpus, follow REPORT.md in the pack: install the release into the pack, then run reproduce.py --stage synthetic with --toolchains pointing at Compact 0.31.1 and 0.34.0 compilers and --oz-root at the OpenZeppelin checkout above. The real-contract inputs cannot be redistributed, so only the synthetic results are publicly reproducible.

Pack SHA-256: 9d95e631dc318006610882911c0d58b7c25fa7754c39b49205c309d2e2e57b3b (1.7 MB).