Skip to content

feat: add related_contracts() WASM export, full RelatedContracts API, and proptest fuzzing#59

Closed
xotatera wants to merge 6 commits intofreenet:mainfrom
xotatera:feat/related-contracts-full-impl
Closed

feat: add related_contracts() WASM export, full RelatedContracts API, and proptest fuzzing#59
xotatera wants to merge 6 commits intofreenet:mainfrom
xotatera:feat/related-contracts-full-impl

Conversation

@xotatera
Copy link

@xotatera xotatera commented Mar 7, 2026

Problem

Contracts that depend on other contracts had no way to declare their dependencies upfront. The runtime had to discover dependencies reactively through ValidateResult::RequestRelated responses, causing unnecessary round-trips. RelatedContractsContainer also lacked depth/cycle tracking, making it vulnerable to infinite loops from malicious or buggy contracts.

Approach

WASM Export for Upfront Dependency Declaration

  • New related_contracts() WASM export (zero-arg, returns serialized Vec<RelatedContract>) lets contracts declare dependencies at compile time
  • ContractInterface trait gets a default related_contracts() method (returns empty vec)
  • freenet-macros generates the FFI wrapper automatically
  • inner_related_contracts in wasm_interface.rs handles serialization across the WASM boundary

Full RelatedContracts API

  • RelatedContracts: query helpers (len, is_empty, contains, get, insert, resolved_count, pending_count, all_resolved)
  • RelatedContract: builder pattern with with_timeout/with_ttl (reserved for future runtime enforcement)
  • RelatedMode: derives Copy + Clone for ergonomic use in mock/test code
  • ValidateResult: derives Clone (needed by proptest strategies)

RelatedContractsContainer Safety

  • request_depth + seen set for depth limiting and cycle detection
  • MAX_REQUEST_DEPTH = 10 with documented effective budget
  • set_initial_depth() for sharing depth budget across nested resolution
  • request_with_tracking() combines depth check + cycle detection
  • increment_depth() checks before incrementing (bug found by proptest)
  • merge() preserves max depth and unions seen sets
  • set_initial_depth() clamps to MAX (bug found by proptest)

Proptest Fuzzing

Property-based tests that already found and fixed 2 bugs:

  1. increment_depth() could overshoot MAX_REQUEST_DEPTH when set_initial_depth was called first (incremented before checking)
  2. set_initial_depth() accepted values above MAX without clamping

12 proptest tests covering:

  • Serde round-trips: UpdateData (all 6 variants), ValidateResult, RelatedContracts, RelatedContract
  • into_owned() preserves serialized form
  • Query invariants: resolved_count + pending_count == len
  • Container op sequences: depth never exceeds MAX under any operation ordering
  • Cycle detection: duplicate requests always fail
  • Merge: depth = max, seen = union
  • From<RelatedContracts> splits resolved/not_found correctly
  • From<Vec<UpdateData>> extracts only RelatedState variants

Testing

  • 25 lib tests pass (including 12 new proptest tests)
  • cargo fmt && cargo clippy clean
  • Proptest default 256 cases per test; verified with PROPTEST_CASES=1000

Files Changed

File Changes
rust-macros/src/contract_impl.rs Generate related_contracts() FFI export
rust/Cargo.toml Add proptest dev-dependency
rust/src/contract_composition.rs Add related_contracts() to ContractComponent trait
rust/src/contract_interface/encoding.rs Depth/cycle tracking, container API, proptest module
rust/src/contract_interface/trait_def.rs Add related_contracts() to ContractInterface
rust/src/contract_interface/update.rs Query helpers, builder pattern, Clone on ValidateResult, proptest module
rust/src/contract_interface/wasm_interface.rs inner_related_contracts WASM boundary helper
rust/src/memory.rs wasm_return_0arg for zero-arg WASM exports

[AI-assisted - Claude]

xotatera and others added 5 commits March 7, 2026 10:17
Add WASM FFI export for related_contracts() so the runtime can query
a contract's declared dependencies before validation. Also includes
RelatedContract with timeout/ttl fields, RelatedContractsContainer
with cycle detection and depth limiting, and ContractInterface default
method for declaring dependencies upfront.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
set_initial_depth allows sharing depth budget across nested dependency
resolution. RelatedMode derives Copy+Clone for ergonomic use in mocks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…served fields

- increment_depth now uses >= (consistent with request_with_tracking)
- merge() preserves max(self.depth, other.depth) instead of discarding
- timeout/ttl on RelatedContract documented as reserved for future use

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tainer logic

Property-based tests found and fixed a bug where increment_depth() could
exceed MAX_REQUEST_DEPTH when set_initial_depth was called first. Also
clamps set_initial_depth to MAX and derives Clone on ValidateResult.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@CLAassistant
Copy link

CLAassistant commented Mar 7, 2026

CLA assistant check
All committers have signed the CLA.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@xotatera xotatera closed this Mar 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants