feat: migrate to new backend logging convention - #105
Merged
Conversation
AquiGorka
force-pushed
the
backend-logging-convention
branch
from
May 31, 2026 15:39
5631724 to
18b452e
Compare
7 tasks
AquiGorka
added a commit
that referenced
this pull request
Jun 4, 2026
) ## Summary Adds a pre-flight check on the per-provider OpEx Stellar account before the executor signs and submits a Soroban bundle. When the PP root account cannot cover the simulated tx fee after subtracting Stellar minimum reserves, the bundle is moved straight to **terminal `FAILED`** with a typed `InsufficientFees` reason — no retry, no mempool retention, no on-chain submission attempt. The structured detail (`feePayerPubkey`, `availableXlm`, `requiredXlm`, `shortfallXlm`, all stroop strings) is persisted on a new nullable `failure_detail jsonb` column on `operations_bundles` and surfaced through the existing entity-scoped bundle-status endpoint so the submitting wallet can read the cause via the response it already polls. Why terminal-fail (not retry): the mempool is shared across providers, and there is no mechanism today to notify a provider admin that OpEx is underfunded — retained bundles would just clog the shared mempool for every provider. The user must refund OpEx and resubmit. ClickUp: https://app.clickup.com/t/86c9xfd2e Milestone: Testnet Hardening. ### What changed - **Pre-flight check** at `src/core/service/executor/preflight-opex-balance.ts`: reads the fee-payer's XLM balance + `numSubEntries` via Soroban `getLedgerEntries`, simulates the channel-invoke transaction via `simulateTransaction` to extract `minResourceFee`, then compares `balance − (2 + numSubEntries) × BASE_RESERVE_STROOPS` against `NETWORK_FEE + minResourceFee`. - **Typed error** `InsufficientFees` (`EXC_005`) at `src/core/service/executor/executor.errors.ts`, carrying the four-field detail. - **Wired** into `Executor.executeNext` between `getTransactionExpiration` and `submitTransactionToNetwork`. The catch site fast-paths `error instanceof InsufficientFees` before reaching the existing retry helper: it sets `BundleStatus.FAILED`, persists the detail to `failure_detail`, and does **not** touch `retryCount`. - **DTO + API surface**: `BundleDTO` (`toBundleDTO`) and the bundle-status GET response schema both grew `failureDetail: Record<string, unknown> | null`. Null for success states and existing free-form failures — back-compat preserved. - **New env var** `BASE_RESERVE_STROOPS` (optional, default `5_000_000`). Soroban RPC does not expose `base_reserve` as a `ConfigSettingEntry`, so this is read from env per the project convention; override on protocol upgrade. Documented in `.env.example`. - **Drizzle migration** `0018_add_failure_detail_to_bundles.sql` (additive `ADD COLUMN IF NOT EXISTS`). - **Tests**: - `tests/unit/preflight-opex-balance.test.ts` — 8 tests covering the math (sufficient / exact / shortfall-by-one / missing account / high subentry) and the typed-error instance contract. - `tests/integration/service/executor-preflight-opex-fees.test.ts` — 6 tests proving the catch-site persists `FAILED` + `failure_detail` without incrementing `retryCount`, and that the bundle-status DTO + response schema carry the four fields end-to-end. - **Test stub fixes** (`test:` commit, drift from PR #105 / the router-factory migration): `executor-retry.test.ts`, `verifier-retry.test.ts`, and `waitlist.test.ts` now pass a real `Logger` (via `newNoop()`) and use `buildWaitlistRouter({log})`. `test:integration` goes from 82/17 to 99/0. ### Migration note `0018_add_failure_detail_to_bundles.sql` was force-added (migration files are gitignored by `*.sql`). Journal entries are chronological — idx 18 `when=1780531200000` follows idx 17's `1780300000000`. ## Test plan - [x] `deno fmt --check` — clean (300 files) - [x] `deno lint` — clean (258 files) - [x] `deno task test:unit` — 64 / 0 - [x] `deno task test:integration` — 99 / 0 (was 82/17 on `main` before this PR's test-stub fix) - [x] Local stack end-to-end on `0.7.2` (full local-dev `infra-up.sh` + `setup-c` + `setup-pp`): - happy-path bundle → `COMPLETED`, `failureDetail: null` - PP_1 root drained to `10,499,900` stroops (just above the 10M reserve) → next deposit goes to `FAILED` with `failureDetail = { feePayerPubkey, availableXlm:"499900", requiredXlm:"1504155", shortfallXlm:"1004255" }` - PP_1 re-funded via friendbot → subsequent bundle goes through normally - No `submitTransactionToNetwork` span recorded for the failed bundle in Jaeger — pre-flight throws before any submission attempt - [ ] Reviewer pass on the catch-site bypass (one fast-path before the existing retry helper; deliberately no refactor of `handleExecutionFailure`) - [ ] Reviewer pass on the migration ordering (idx 18 / `when` 1780531200000)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adopts the convention defined in
local-dev/logging.md. Final repo of the 4-repo backend logging migration.src/utils/logger/index.ts— TS port of AquiGorka/go-logger. Five methods, four levels.src/config/logger.tsexportscreateLogger(). No singleton.Mempool,Executor,Verifier,MetricsCollector,EventWatcher,ChannelRegistry,EventBus,InMemorySessionManager) take{ log }via constructor orsetLogger().stellar/auth/*,bundle/*,dashboard/*(auth, utxos, treasury, audit-export, transactions, pp, council, bundle-admin),pay/*(kyc, transactions, self/balance, custodial/, demo/, escrow/*, report) becomeshandle<X>(deps). Sub-routers becomebuild<X>Router(deps).appendRequestIdMiddlewarebecomes a factory.log-and-throwhelper retired — all 10logAndThrow(...)call sites replaced with barethrow.createDashboardChallenge,verifyDashboardChallenge,queryBalances,createEscrow,claimEscrowForAddress,getEscrowSummary,emit-helpers) take deps.LOG.tracedropped.PLG_ProcessErrorResponsetakes optional deps to keep pipeline factories caller-compatible.withSpan,span.addEvent,tracer.startSpanall preserved.Logging coverage
Verified locally (pre-push)
deno fmt --check: cleandeno lint: cleandeno task test:unit: 60 passed, 0 faileddeno task test:pay: 136 passed, 0 failedgrep -rn 'LOG\.(...)' src/: 0 hitsgrep -rn 'console\.(...)' src/(excl logger module + node_modules): 0 hitsTest plan