Skip to content

[Enhancement]: The globe-wallet <-> token-wrapper wiring flagged in docs/design/architecture.md needs a reentrancy-safe design, not just a naive sequential call #92

Description

@ndii-dev

Context

docs/design/architecture.md's "Future: wiring the contracts together" section, plus record_spend's existing doc comment on contracts/globe-wallet/src/lib.rs: "Reentrancy invariant: keep the interval from reading DailySpent through writing its replacement free of external contract calls."

Problem

architecture.md correctly identifies that globe-wallet and token-wrapper currently don't call each other, and that this is a real security gap (a payment through token-wrapper::transfer_from directly bypasses record_spend's daily-limit enforcement entirely). It proposes two concrete approaches for wiring them: either globe-wallet calls token-wrapper::transfer_from internally after record_spend succeeds, or a new send-style entry point on globe-wallet chains record_spendtransfer_from atomically.

Neither approach, as sketched, addresses what happens once that wiring calls into an arbitrary token_id — which by definition it must, since token-wrapper::transfer_from already takes token_id: Address as a caller-supplied parameter, and nothing constrains it to only the well-known, trusted Stellar Asset Contract implementation. record_spend's own reentrancy invariant is scoped to its own function body — it says nothing about what happens when a different function later in the same call chain (transfer_from) invokes a non-standard, potentially adversarial token contract's transfer, which — unlike the trusted SAC — could itself call back into globe-wallet's public API (e.g. another record_spend, or unrelated guardian/asset functions) before the outer call chain unwinds.

This is forward-looking rather than exploitable today (the wiring doesn't exist yet), but it's exactly the kind of constraint that needs to be part of the wiring's initial design, not retrofitted after a naive sequential implementation ships. record_spendtransfer_from as two separate contract invocations (even within one transaction) is not equivalent to record_spend's existing single-function atomicity guarantee.

Impact

If the wiring described in architecture.md is implemented as a simple sequential call without this in mind, and token_id is ever allowed to be anything other than a small, explicitly-trusted allowlist of token contracts, a malicious token contract's transfer implementation could re-enter globe-wallet mid-flight — for example calling record_spend again for the same or a different asset before the outer spend/transfer completes, potentially double-counting, under-counting, or otherwise desynchronizing the daily-spend bookkeeping from what actually settles on-chain.

Suggested fix

When implementing the wiring: (a) restrict token_id (at the globe-wallet or wiring layer) to a small, admin-curated allowlist of known-safe token contracts rather than accepting arbitrary caller-supplied addresses, and/or (b) apply a checks-effects-interactions ordering across the whole wired call chain — not just within record_spend — so that all of globe-wallet's own state changes (spend recording included) are finalized before the external token-contract call happens, and any reentrant call back into globe-wallet mid-flight sees fully-consistent state rather than a half-committed one. Document which of these (or both) was chosen and why, per CONTRIBUTING.md's design-decision expectations.

Definition of done

  • Threat model written out: what a malicious/non-standard token_id could do if allowed to reach the wiring, and why the chosen mitigation closes it
  • Either a token-contract allowlist mechanism, a CEI-ordering guarantee across the wired call, or both — implemented as part of the wiring work (not left as a follow-up)
  • Test using a mock malicious token contract that attempts to re-enter globe-wallet during transfer, proving the wiring is not exploitable
  • No regression to record_spend's existing single-function reentrancy invariant/tests
  • cargo test --workspace output pasted

Activity

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

Metadata

Metadata

Assignees

Labels

GrantFox OSSIssue tracked in GrantFox OSSMaybe RewardedIssue may be eligible for a GrantFox rewardThird CampaignCampaign: Third CampaignenhancementNew feature or requestvery hardDifficulty: very hard

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions