Skip to content

fix(credit-score): expose config_version in get_credit_score to detect stale scores#590

Open
thewealthyplace wants to merge 1 commit into
astera-hq:mainfrom
thewealthyplace:fix/credit-score-config-version
Open

fix(credit-score): expose config_version in get_credit_score to detect stale scores#590
thewealthyplace wants to merge 1 commit into
astera-hq:mainfrom
thewealthyplace:fix/credit-score-config-version

Conversation

@thewealthyplace

Copy link
Copy Markdown
Contributor

Closes #573

Problem

When set_scoring_config() updates point values the score_version field on existing CreditScoreData records is not updated. Consumers could not distinguish a score computed under the old config from one under the new config — a score of 720 before a config change is semantically different from 720 after, but both looked identical. Detecting staleness required calling get_scoring_config() separately and manually comparing score_version fields.

Solution

Add CreditScoreResponse — a new return type for get_credit_score() — that includes all existing CreditScoreData fields plus two additive fields:

Field Meaning
config_version Config version currently active on the contract
is_stale true when score_version != config_version

Consumers can now detect staleness in a single call:

let resp = credit_score_client.get_credit_score(&sme);
if resp.is_stale {
    // score was computed under an older config — treat as approximate
}

What changed

  • contracts/credit_score/src/lib.rs
    • Added CreditScoreResponse struct (#[contracttype]) with all existing fields + config_version + is_stale
    • Changed get_credit_score() return type from CreditScoreDataCreditScoreResponse
    • Five new tests covering the staleness lifecycle (fresh SME, payment under current config, stale after config bump, cleared after new payment, new SME with pre-bumped config)

No migration needed

CreditScoreData (on-chain persistent storage) is unchanged. Existing consumers that only read score, total_invoices, paid_on_time, etc. continue to work — the two new response fields are purely additive.

Test results

test result: ok. 66 passed; 0 failed (unit)
test result: ok. 2 passed; 0 failed (fuzz)
cargo fmt -- --check ✓
cargo clippy -- -D warnings ✓

…t stale scores

Closes astera-hq#573

When set_scoring_config() bumped the scoring parameters the score stored on
CreditScoreData was still computed under the old config, but get_credit_score()
returned only score_version (the config version active at last computation) with
no way to compare it against the current config version in a single call.

Consumers — the pool contract, frontend, and third-party integrations — had no
means to tell whether a returned score of 720 was current or stale without an
additional get_scoring_config() round-trip and manual comparison.

Fix: add CreditScoreResponse, a new return type for get_credit_score() that
includes all existing CreditScoreData fields plus:
  - config_version: the config version currently active on the contract
  - is_stale:       true when score_version != config_version

Scoring storage (CreditScoreData) is unchanged — no on-chain migration is
needed. Existing consumers that only read score/total_invoices/paid_on_time/etc.
continue to work unchanged; the two new fields are purely additive.

Five new tests cover:
  - config_version returned on fresh SME
  - not stale immediately after a payment under the current config
  - stale flag set after set_scoring_config() bumps the version
  - stale flag cleared once a new payment is recorded under the new config
  - new SMEs always start current even when config has already been bumped
@sanmipaul

Copy link
Copy Markdown
Contributor

The approach is right and the staleness lifecycle tests are exactly what we need here. One blocker and one thing to verify before I can merge:

Blocker

This is a breaking contract interface change. get_credit_score now returns CreditScoreResponse instead of CreditScoreData. Soroban generates client bindings from the contract ABI — any existing caller (frontend TypeScript client, SDK wrapper) will break after deployment.

The PR description says existing consumers continue to work, but that is only true for dynamically-typed callers, not our typed bindings. Before merge I need either:

  • The frontend/SDK call-site updates included here or in a linked PR that lands at the same time, or
  • Explicit confirmation that all downstream consumers have been identified and updated separately.

Please verify before merge

test_new_sme_always_current_even_after_config_update asserts resp.score_version == 2 for a brand-new SME when the config is already at v2. This only holds if get_or_create_credit_data seeds new records with the current config's score_version. If it hard-codes score_version = 1, this test fails — and more importantly, every new SME created after a config bump would incorrectly appear stale on first access.

The function is not visible in this diff. Please confirm this is how it behaves and add a brief comment on the test explaining the dependency.

Notes (no action needed)

  • load_scoring_config adds an extra ledger read on every get_credit_score call. Not a concern for individual lookups, but worth flagging to any consumer iterating over many SMEs.
  • is_stale is convenient but encodes a strict equality check — worth documenting so consumers know it isn't "stale if N versions behind".

The test structure and the decision to keep CreditScoreData unchanged on-chain are both correct. Fix the blocker and confirm point 2 and this is good to go.

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.

fix: credit score version not bumped on scoring config change — stale scores appear current

2 participants