Skip to content

fix(ops): cap the resource fee both signing paths will accept #1

Description

@0dillon

Summary

Neither extendViaContract nor extendViaFootprint puts a ceiling on what it will pay. Both take
minResourceFee from the simulation, hand the assembled transaction to sign, and submit. The
number is whatever the network says it will cost.

That is a real weakness rather than a theoretical one, because registration is permissionless.
Anyone can register a contract in the registry, and lkeep keep maintains every contract in it. A
contract written to be expensive to maintain gets maintained at whatever it costs, once per tick,
until the keeper's account is empty. The futility backoff added in src/keeper/futility.ts does not
help here: an expensive extension that genuinely moves TTL is productive by every measure the daemon
has, so the backoff never opens a window.

The mitigation documented in SECURITY.md today is "fund the keeper with only what you are willing
to lose in a day". That is advice, not a control.

Acceptance Criteria

  • A configurable ceiling — LK_MAX_FEE or similar — validated in src/config.ts alongside the
    existing integer fields, with a documented default.
  • Both signing paths compare the simulated fee against it and refuse to sign when it is
    exceeded. The refusal is logged with the contract, the simulated fee, and the ceiling.
  • Refusing is not an error that kills the daemon: runTick moves on to the next contract, the
    same way it handles a failed extension today.
  • The ceiling is checked before sign is called, not after submission.
  • Tests cover a fee under the ceiling, a fee over it, and a fee exactly at it.
  • The README env table and the SECURITY.md known-limits section are updated; the limit stops
    being described as unmitigated.

Tech Stack

TypeScript 5.7 on Node 22+ (CI runs 22 and 24), ESM with moduleResolution: "NodeNext" — relative
imports need the .js extension. @stellar/stellar-sdk is pinned to an exact version; verify RPC
API shape against the installed copy, not against documentation. Tests are Vitest under test/ and
run without a network. npm run format:check, npm run lint, npm run build and npm test must
all be clean.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: opssrc/ops, the two signing pathscomplexity: mediumHalf a day, some design judgement neededtype: bugExisting behaviour is wrong

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions