Skip to content

Commit af9de95

Browse files
Merge pull request #1 from annamary224411-sudo/codex/add-property-based-and-fuzz-tests-for-contracts
docs: document external contract invariant testing and enforce external contracts in CI
2 parents 0a89022 + ab688d5 commit af9de95

2 files changed

Lines changed: 58 additions & 39 deletions

File tree

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,31 @@
1-
name: Contract Fuzzing
1+
name: Contract Test Ownership
22

33
on:
44
push:
55
branches: [main, develop]
66
paths:
7-
- 'contracts/**'
7+
- '.github/workflows/contract-fuzzing.yml'
8+
- 'docs/contract-invariants.md'
89
pull_request:
910
paths:
10-
- 'contracts/**'
11+
- '.github/workflows/contract-fuzzing.yml'
12+
- 'docs/contract-invariants.md'
13+
14+
permissions:
15+
contents: read
1116

1217
jobs:
13-
fuzz:
14-
name: Property-based & Fuzz Tests
18+
verify-contracts-are-external:
19+
name: Verify contract test ownership
1520
runs-on: ubuntu-latest
16-
1721
steps:
1822
- uses: actions/checkout@v4
19-
20-
- name: Install Rust toolchain
21-
uses: dtolnay/rust-toolchain@stable
22-
with:
23-
targets: wasm32-unknown-unknown
24-
25-
- name: Cache cargo registry & build artefacts
26-
uses: actions/cache@v4
27-
with:
28-
path: |
29-
~/.cargo/registry
30-
~/.cargo/git
31-
contracts/target
32-
key: ${{ runner.os }}-contracts-fuzz-${{ hashFiles('contracts/Cargo.lock') }}
33-
restore-keys: ${{ runner.os }}-contracts-fuzz-
34-
35-
- name: Run analytics fuzz tests (1000+ iterations via bolero)
36-
working-directory: contracts
37-
env:
38-
BOLERO_ITERATIONS: 1000
39-
run: cargo test --package analytics --test '*' fuzz_ -- --nocapture
40-
41-
- name: Run analytics property-based tests
42-
working-directory: contracts
43-
run: cargo test --package analytics fuzz_ -- --nocapture
44-
45-
- name: Run governance fuzz tests (property-based)
46-
working-directory: contracts
47-
run: cargo test --package governance fuzz_ -- --nocapture
48-
49-
- name: Run all contract tests (fail CI on any failure)
50-
working-directory: contracts
51-
run: cargo test --workspace -- --nocapture
23+
- name: Reject stale vendored contracts
24+
run: |
25+
test ! -d contracts
26+
test -f docs/contract-invariants.md
27+
grep -q 'Stellar-Insightss/contracts' docs/contract-invariants.md
28+
- name: Explain the contract quality gate
29+
run: |
30+
echo 'Property tests, fuzzing, and contract coverage run in Stellar-Insightss/contracts.'
31+
echo 'See docs/contract-invariants.md for the per-contract safety invariants.'

docs/contract-invariants.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Smart-contract invariant testing
2+
3+
The Soroban contracts are maintained in the dedicated
4+
[`Stellar-Insightss/contracts`](https://github.com/Stellar-Insightss/contracts) repository.
5+
They were intentionally split out of this dashboard repository in commit `05fd5054` so that
6+
contract deployment, audit, and release controls are isolated from application changes.
7+
8+
## Contract ownership and required safety properties
9+
10+
The contracts repository is the source of truth for both the executable contract code and its
11+
property/fuzz suites. Every contract change must document and test the following invariants before
12+
it is merged:
13+
14+
| Contract | Core invariants |
15+
| --- | --- |
16+
| `access-control` | Only an authorised administrator can change roles or pause state; role membership is idempotent. |
17+
| `analytics` | Snapshot epochs are strictly monotonic; an accepted snapshot cannot be replaced by an older epoch. |
18+
| `stellar_insights` | Snapshot submissions are authorised, monotonically ordered, and cannot mutate state while paused. |
19+
| `governance` | A proposal executes only after its voting period and only when quorum and the passing rule are met; a voter votes at most once. |
20+
| `governance-voting` | Vote weights are counted exactly once, and finalisation is immutable after the deadline. |
21+
| `escrow` | An escrow reaches exactly one terminal state; deposited funds cannot be released to both parties. |
22+
| `multi-sig-wallet` | A transaction executes at most once and never below its configured owner threshold. |
23+
| `time-locked-transactions` | A transfer cannot be released before its unlock time and has one terminal state. |
24+
| `token-swap` | An offer is filled or cancelled at most once; token movement is atomic and respects the quoted amounts. |
25+
| `upgrade` | Only approved upgrades can change the active code/version, and each proposal has one final outcome. |
26+
27+
## Required verification in the contracts repository
28+
29+
Each deployable crate must have a `tests/properties.rs` suite using generated values to exercise
30+
its documented invariant, including numeric boundaries and call-order permutations. Parsing or
31+
deserialising attacker-controlled input must additionally have a `cargo-fuzz` target. The contract
32+
repository's CI runs property tests and time-boxed fuzz targets, then publishes an LCOV report so
33+
uncovered contract paths are visible in review.
34+
35+
This repository deliberately does **not** vendor a second copy of the contracts: doing so would
36+
make the dashboard CI test a potentially stale artifact rather than the code that is deployed.
37+
The workflow in `.github/workflows/contract-fuzzing.yml` therefore verifies that contract testing
38+
is owned by the contract repository and fails fast if a contracts directory is accidentally
39+
reintroduced here.

0 commit comments

Comments
 (0)