Skip to content

(Medium) CON-E3.2 cache ProtocolParameters reads on hot paths #154

Description

@EmeditWeb

Summary

Three contracts fetch the full ProtocolParameters struct from the parameters contract multiple times inside a single transaction, and each fetch is a cross-contract invocation. creditline's get_protocol_parameters (contracts/creditline-contract/src/lib.rs:1189-1203) is called from validate_guarantee (:336), validate_reputation (:380), interest_rate_bps (:502), calculate_default_penalty (:530), warn_grace_period (:561) and mark_defaulted (:601) — so a single create_loan/mark_defaulted can invoke the parameters contract three or more times. liquidity-pool's get_upgrade_delay_seconds (contracts/liquidity-pool-contract/src/lib.rs:885-901) and reputation's (contracts/reputation-contract/src/lib.rs:292-308) each re-invoke get_parameters per upgrade call. This workstream reads the parameters once per transaction and passes the struct down.

Why this matters: the property is a transaction reads the parameters contract at most once. Parameters change only through a governed multisig proposal, so re-fetching mid-transaction is both wasteful and a (theoretical) consistency hazard if a proposal executes between reads; read-once/pass-down removes both.

Labels

area: contracts type: refactor priority: medium


Workstream 1 — Read-once/pass-down parameters across the hot paths

Objective

Fetch ProtocolParameters exactly once per entrypoint invocation and thread it into every internal helper, across creditline, liquidity-pool and reputation.

Problem

creditline's helpers each call Self::get_protocol_parameters(env) independently (contracts/creditline-contract/src/lib.rs:336,380,502,530,561,601), and the function itself performs a cross-contract try_invoke_contract("get_parameters") (:1193-1198) with a fail-soft default (:1199-1201). create_loan reaches validate_guarantee (:336) and validate_reputation (:380) and interest_rate_bps (:502) → at least three parameter invocations. mark_defaulted reaches warn_grace_period-style grace logic (:561,601) plus calculate_default_penalty (:530) → multiple invocations. liquidity-pool's propose_upgrade calls get_upgrade_delay_seconds (contracts/liquidity-pool-contract/src/lib.rs:127) which invokes parameters (:885-901); reputation's propose_upgrade does the same (contracts/reputation-contract/src/lib.rs:235,292-308). Each invocation is a separate host call plus deserialization of the whole struct.

Scope

  • contracts/creditline-contract/src/lib.rs (edit — fetch once, pass down)
  • contracts/liquidity-pool-contract/src/lib.rs (edit — same for upgrade delay)
  • contracts/reputation-contract/src/lib.rs (edit — same for upgrade delay)
  • each affected src/tests.rs (edit — parity tests)

Implementation

  1. In creditline, fetch ProtocolParameters once at the top of each public entrypoint (create_loan, request_loan, approve_loan, mark_defaulted, warn_grace_period, apply_late_fees) and pass &ProtocolParameters into the helpers instead of having them re-read (:336,380,502,530,561,601).
  2. Keep get_protocol_parameters as the single fetch point (fail-soft default preserved at :1199-1201); add a PARAMS_CACHE_PER_TX (default true) gate so the old per-call fetch can be restored for bisecting.
  3. In liquidity-pool and reputation, compute the delay from one fetch in propose_upgrade rather than re-invoking in execute_upgrade — or cache the fetched params for the call if only propose needs it (liquidity-pool lib.rs:127; reputation lib.rs:235).
  4. Preserve the fail-soft semantics: when the parameters contract is unset or the call fails, both fall back to default_protocol_parameters() / DEFAULT_UPGRADE_DELAY_SECONDS exactly as today.
  5. Emit harness-only parameters_invocations counter (via (Medium) CON-E3.1 gas harness: resource-fee benchmark scaffold + reproducible measurement script #118); default off.

Acceptance Criteria

  • A single create_loan / mark_defaulted invokes the parameters contract exactly once (observable via a counting-parameters mock or the (Medium) CON-E3.1 gas harness: resource-fee benchmark scaffold + reproducible measurement script #118 harness), down from 2+.
  • Fail-soft behaviour is unchanged: unset/failing parameters contract still yields default_protocol_parameters() and the bounded defaults (min_guarantee_percent, grace_period_seconds, upgrade_delay_seconds).
  • All existing parameters-driven tests (setup_parameters_with_grace_period at tests.rs:1638-1650, upgrade-delay tests) stay green.

Testing

Shared acceptance criteria (whole epic)

  • cargo fmt --all --check, cargo clippy --workspace --all-targets -- -D warnings, cargo build --locked --target wasm32-unknown-unknown --release, cargo test --locked all exit 0; test count does not drop; coverage on touched code not reduced.
  • No entrypoint signature, error code, event or parameter-default change; fail-soft semantics preserved.
  • No secrets; no changes outside the three contracts' src/*.

Security & compatibility considerations

  • The fail-soft default path (unwrap_or_else(|_| default_protocol_parameters()), creditline lib.rs:1199-1201) is a security-relevant fallback: threading a cached struct must not change which default is used when the parameters contract is missing or reverts.
  • Reading parameters once per transaction is also a mild consistency win — it closes the window where a proposal executed between two reads could produce a mixed-parameter decision; call this out as an intended (and safe) consequence.
  • Do not cache parameters across transactions (that would defeat governance); the cache is strictly per-invocation.

References

  • contracts/creditline-contract/src/lib.rs:336,380,502,530,561,601,1189-1203 — the repeated fetch sites.
  • contracts/liquidity-pool-contract/src/lib.rs:127,885-901 — upgrade-delay fetch.
  • contracts/reputation-contract/src/lib.rs:235,292-308 — upgrade-delay fetch.
  • contracts/parameters-contract/src/lib.rs — get_parameters entrypoint.
  • contracts/creditline-contract/src/tests.rs:1638-1650 — parameters wiring helper that must stay valid.
  • Depends on issue (Medium) CON-E3.1 gas harness: resource-fee benchmark scaffold + reproducible measurement script #118 (gas harness + baselines).

If you're solving this with AI

In scope: contracts/creditline-contract/src/lib.rs, contracts/liquidity-pool-contract/src/lib.rs, contracts/reputation-contract/src/lib.rs, and the matching src/tests.rs — only.
Out of scope: the parameters contract itself; the vendor-registry/vouching contracts; changing fail-soft defaults or governance; new dependencies; reformatting untouched modules.
Must: follow the repo PR template exactly; keep CI green for real (fmt + clippy + build + test); add the counting-mock + fail-soft tests above; reference this issue (Closes #<n>); no secrets.

Contribution requirements: Follow the repo PR template exactly — https://github.com/StepFi-app/StepFi-Contracts/blob/main/.github/pull_request_template.md. Reference this issue in your PR. CI must pass green (fmt + clippy + build + tests). No secrets; no out-of-scope changes.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions