Update LightningReceiveRequestStatus.ts - #134
Open
TwistedHardware wants to merge 2438 commits into
Open
Conversation
GitOrigin-RevId: a085b0ec84082fa73d470513c710b31ae251f0b8
## Summary bump `retypeCursorKeyPrefix` to v2 to reset the cursor position and take another pass given that there were a slew of timeout errors on dkg0 yesterday. this is for sanity ## Test Plan How was this tested? Include steps to verify the changes work. N/A if not applicable. GitOrigin-RevId: fd727a6800ff3623f50cab0ab1345ebff54dcc5b
## Summary When saving the UTXO, the follower SO's chain view can be a few seconds behind the coordinator's, so it must wait till it catches up. This resolves on retry, but the initial error surfaces as a server failure, which causes alert noise. This reclassifies it. GitOrigin-RevId: 0dfe3d78cac01bea532177d4db297d023f653f5a
## Summary Adds `queryPendingTransfersMIMO` behind `KnobReadMIMODataModelQueryPendingTransfers`. Partial-index-driven raw SQL for the three production-relevant participant shapes (Receiver, Sender, SenderOrReceiver). Routing falls back to legacy `queryTransfers` when the knob is off (default) or `participant` is nil (audit-confirmed: no production caller hits the nil case; added alerts to confirm fallback. #### Performance — verified post-Phase-4 Three test beds: dbseed `full` (62M rows, sparkoperator_0), `realistic_ssp` (~92 pending, sparkoperator_1, prod-cardinality-matched within 1% via `spark-rds.sh`), `stuck_user` (~120K pending TRANSFER-only, sparkoperator_2). `EXPLAIN (ANALYZE, BUFFERS)`, warm-cache repeats. "Real legacy" = non-MIMO `queryTransfers`. **Query shape labels** (used in test names + the rows below): - `R1` — receiver participant + network filter (bare) - `R2` — receiver + types filter (e.g. `[SWAP]`) - `R3` — receiver + transfer_id filter (singular lookup) - `S1` — sender participant + network filter (bare) - `SR1` — sender_or_receiver participant — UNION ALL of S1 + R1 arms | Shape | Cardinality | New MIMO | Real legacy | Notes | |---|---|---|---|---| | R1 receiver bare + network | realistic SSP (92) | 0.19 ms | 0.06 ms | both sub-ms | | R1 receiver bare + network | medium (2.1K) | **0.74 ms** | 1.05 ms | 🟢 **MIMO ~1.4× faster** (1.05 / 0.74) | | R1 receiver bare + network | extreme (104K) | **0.37 ms** | 0.69 ms | 🟢 **MIMO ~1.9× faster** (0.69 / 0.37) | | R1 receiver bare + network | stuck-user (59K) | 0.08 ms | 0.077 ms | both sub-ms, essentially tied | | R2 receiver + types=`[SWAP]` | realistic SSP (92) | 0.11 ms | 0.09 ms | both sub-ms | | R2 receiver + types=`[SWAP]` | extreme (104K) | **85 ms** | 22.6 s | 🟢 **MIMO ~268× faster** (22 600 / 85) | | R3 singular by `transfer_id` | realistic SSP (92) | **0.12 ms** | 0.85 ms | 🟢 **MIMO ~7× faster** (0.85 / 0.12) | | **S1 sender bare + network** | extreme (T1, 6.2K sender-pending) | 1.14 ms | **0.041 ms** |⚠️ **MIMO ~28× slower** — JOIN-based S1 can't use the new partial's leading-equality predicate. Audit indicates no internal callers; external SDK use unverified. Strengthens the case for [SP-2914](https://lightspark.atlassian.net/browse/SP-2914?search_id=64a42ed8-5a84-4b86-8717-fa906e84cc2d) (denormalize `transfers.status` onto `transfer_senders`, mirror the receiver-arm pattern). | | **S1 sender bare + network** | medium (T3, 105 sender-pending) | 83 ms | **0.058 ms** |⚠️ **MIMO ~1430× slower** — same root cause; medium cardinality is the planner-flip regime where the partial gets walked without leading scope. Same [SP-2914](https://lightspark.atlassian.net/browse/SP-2914?search_id=64a42ed8-5a84-4b86-8717-fa906e84cc2d) mitigation. | | **SR1 UNION ALL** | realistic SSP (92) | **0.09 ms** | 0.13 ms | 🟢 **MIMO ~1.4× faster** (0.13 / 0.09) — Merge Append over UNION ALL | | **SR1 UNION ALL** | medium (2.1K) | **0.12 ms** | 9.8 ms | 🟢 **MIMO ~82× faster** (9800 / 120) — the cardinality where an early JOIN-based MIMO design wiped at 10.4 s | | **SR1 UNION ALL** | extreme (104K) | **0.36 ms** | 3.84 s | 🟢 **MIMO ~10 000× faster** (3840 / 0.36) | At production cardinality, R1/R2/R3/SR1 are sub-ms in both paths; legacy edges out by tens to hundreds of µs (comfortably under the prod p50 of 5.5 ms per Grafana). At synthetic stress, legacy `queryTransfers` catastrophically degrades on filter-heavy receiver and sender-or-receiver queries — those paths show 80–10 000× MIMO speedups. S1 is the outlier: MIMO regresses 28–1430× across all sender-pending cardinalities. The MIMO MVP asymmetry — JOIN-based sender arm via `transfer_senders` while the new partial keys on `t.sender_identity_pubkey` — can't drive the leading-equality predicate the partial wants. [SP-2914](https://lightspark.atlassian.net/browse/SP-2914?search_id=64a42ed8-5a84-4b86-8717-fa906e84cc2d) retires this asymmetry by denormalizing `transfers.status` onto `transfer_senders` so the sender arm can use the same column-on-edge-table pattern the receiver arm already uses. A [follow-up PR](lightsparkdev/spark#6362) will improve metric capture in these endpoints and allow us to more accurately measure performance and actual query combinatorics used in production callers. ## Test Plan - `./scripts/check-migration-safety.sh` on the new migration — clean - 28-case equivalence suite (`transfer_handler_query_equivalence_test.go`) — pass — validates legacy `queryTransfers` and `queryPendingTransfersMIMO` return identical transfer IDs, pagination offsets, and per-transfer projections across all pending pairs. - 25+ post-Phase-4 `EXPLAIN ANALYZE` probes across R1/R2/R3/S1/SR1 shapes at SSP-realistic, medium, extreme, and stuck-user pubkey magnitudes (numbers above). Contributes to SP-2917 (parent — MIMO query rework with custom SQL and stress testing) Contributes to SP-1214 (epic — MIMO MVP) Contributes to SP-2923 (Phase 4 read-path swap is in this PR) Related to [SP-2914](https://lightspark.atlassian.net/browse/SP-2914?search_id=64a42ed8-5a84-4b86-8717-fa906e84cc2d) (follow-up: denormalize `transfers.status` onto `transfer_senders` to retire the SR sender-arm asymmetry and the S1 regression documented in the table above) Related to SP-2916 (follow-up: consolidate MIMO code into spark/so/mimo package) Related to SP-2727 (counter-swap filter retirement, spark#6103) [SP-2914]: https://lightspark.atlassian.net/browse/SP-2914?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> GitOrigin-RevId: 628cb442c482681a79e8d9e7a71622afd10c85bd
## Summary Merged a PR that added tests referencing a variable renamed upstream. This fixes that. GitOrigin-RevId: f66d2709be8f8134717a48d18d9679767ecd59c5
## Summary
Adds a deferred-exit `Info` log `"transfer query invoked"` to
`queryTransfers` and `queryPendingTransfersMIMO` capturing filter
parameters (network, types, statuses, participant pubkey hash,
pagination, time bounds) plus elapsed duration, result count, and error.
Lets us attribute query patterns to callers in OpenSearch — "which
wallets are calling with custom statuses, and what's their p99?" —
without adding high-cardinality Prometheus labels.
The log is gated by `KnobLogTransferQueryInvocations` (per-call rollout,
0–100, default 0). Steady-state cost is one knob check; bump to 100
during diagnosis or to a small percentage for a continuous low-cost
sample. Histograms record every call regardless.
The participant pubkey is hashed (first 8 bytes of `sha256`, 16 hex
chars) to avoid writing raw identity pubkeys to logs while preserving
per-wallet grouping.
#### Other changes
- Adds three boolean labels to the `spark_transfer_query_duration`
histogram: `has_pubkey`, `has_status_filter`, `has_type_filter`. Enables
PromQL slicing of "did the caller pass a status/type filter?" without
listing values (cardinality-safe).
- Refactors `newTransferQueryRecorder` to take a `transferQueryAttrs`
struct of named fields instead of positional bools (signature was
getting unwieldy past 5 params).
- Moves `shortPubkeyHash` and `logQueryTransfersInvocation` out of
`transfer_handler.go` into `transfer_query_observability.go` (renamed
from `transfer_query_metrics.go`). The file now houses both metric
recording and structured logging for transfer query paths.
### Technical Notes
The deferred-exit log uses named return values (`resp`, `err`) so the
closure captures the final response shape and any error returned via
early validation paths. Because `defer` registers the call before
validation runs, `logQueryTransfersInvocation` explicitly guards against
`filter == nil`: when nil, it emits a minimal record (`query_path` +
extras like `elapsed`/`error`) instead of dereferencing
`filter.Participant`.
## Test Plan
- Equivalence with the existing `transferQueryRecorder` API at all 6
call sites (3 in `ssp_request_handler.go`, 3 in `transfer_handler.go`);
migration to the new `transferQueryAttrs` struct preserves attribute
values bit-for-bit.
- Knob default behavior: with `KnobLogTransferQueryInvocations` at 0
(Tiltfile default), the function returns before any field construction;
histograms still record every call.
- Nil-filter resilience: the deferred closure in
`queryPendingTransfersMIMO` fires after the nil-participant validation
returns; the explicit `filter == nil` guard inside
`logQueryTransfersInvocation` prevents a panic when the sampling knob is
on.
- Post-deploy: search OpenSearch for `"transfer query invoked"` (after
bumping the knob) to confirm structured fields appear; query
VictoriaMetrics for
`spark_transfer_query_duration_milliseconds_bucket{has_pubkey="true"}`
to confirm new histogram labels are recorded.
Contributes to SP-1214
Contributes to SP-2917
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitOrigin-RevId: f62e7ed01eef568e6d957bf550b843455050024c
## Summary Adds targeted DEBUG-level SDK breadcrumbs for support debugging without enabling TRACE-level method logging. The new logs cover sanitized fetch and gRPC request lifecycle summaries, Bare transport abnormal events, stream reconnect and heartbeat lifecycle, transfer claim summaries, token sync summaries, and leaf-cache reconciliation/optimization summaries. The DEBUG logs intentionally avoid request/response bodies, headers, auth tokens, query strings, raw invoices, and per-leaf detail; those remain absent or TRACE-only. ## Test Plan - `yarn --cwd sdks/js workspace @buildonspark/spark-sdk test-cmd src/tests/spark-fetch.test.ts src/tests/bare-http-transport.test.ts src/tests/logging-service.test.ts` - `yarn --cwd sdks/js workspace @buildonspark/spark-sdk test-cmd src/tests/spark-wallet-background-stream.test.ts src/tests/leaf-manager/leaf-manager.test.ts` - `yarn --cwd sdks/js workspace @buildonspark/spark-sdk types` - `yarn --cwd sdks/js workspace @buildonspark/spark-sdk format` - `git diff --check` GitOrigin-RevId: c8b81ad044cddde79db6a98ab2947b40e5ceab8d
## Summary When running with separate gRPC and HTTP listeners, the HTTP catch-all handler was registered without the otelhttp wrapper, so HTTP metrics were only collected in single-port (ServeHTTP multiplexing) mode. Wrap the dual-port catch-all in otelhttp.NewHandler with the same options used in the multiplexed path so metrics are emitted in both configurations. ## Test Plan N/A. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> GitOrigin-RevId: f586b6e0bdf74b8f83c438683489bf1c1149eb59
## Summary Removes the `tx-is-dirty` optimization that conditionally rolled back "clean" transactions instead of committing them, gated by the `spark.database.only_commit_dirty` knob. Main DB sessions now always finalize active transactions: commit on the success path, rollback on the error/panic path. The recent middleware fix that prevents rollback hooks from firing after successful commits (`mainCommitted` / `ephemeralCommitted` guards) is preserved. This was an internal-only optimization. No proto, GraphQL, or wire-level changes. ## Why The dirty-state mechanism was a footgun: it rolled back transactions that had only issued raw SQL writes through `db.QueryContext` unless those callers remembered to call `ent.MarkTxDirty(ctx)`. Forgetting that call silently discarded writes when the knob was rolled out. The IOPS savings from skipping commits on read-only transactions did not materialize meaningfully, so the complexity isn't worth keeping. It also caused connection churns on two occasions because changing the logic caused transactions to be neither committed nor rolled back, which made the connection unusable for the next task. ## Changes - **`db.Session`**: dropped `currentIsDirty`, the clean-tx rollback branch in the commit hook, and the `knobs.Knobs` dependency. `Commit()` always invokes the underlying ent commit, then flushes notifications and clears session state on success. - **Session interfaces**: removed `MarkTxDirty(context.Context)` from `ent.Session` and `entephemeral.Session`, deleted the `ent.MarkTxDirty` / `entephemeral.MarkTxDirty` package helpers, and removed all no-op implementations (read-only sessions, ephemeral sessions, chain `txBacked*` adapters, test fakes). - **Call sites**: removed explicit `ent.MarkTxDirty(ctx)` calls in `transfer_handler.go` and `fix_leaf_keyshare_split_handler.go` (and the `txIsDirtyHook` / `txIsDirtyEphemeralHook` mutator hooks wired up in `bin/operator/main.go`). - **Knob removal**: deleted `KnobDatabaseOnlyCommitDirty` and removed `spark.database.only_commit_dirty` from `so.template.config.yaml`, `docker/operator.config.yaml`, and `tilt/spark/Tiltfile`. - **Factory signature**: `db.NewDefaultSessionFactory(dbClient *ent.Client)` no longer takes `knobs.Knobs`. All call sites in `bin/operator`, `so/task`, and tests updated. - **Middleware**: kept `mainCommitted` / `ephemeralCommitted` guards and ephemeral-first commit ordering; updated the stale comment that referenced `MarkTxDirty`. - **Tests**: replaced `TestSession_OnlyCommitDirtyRollsBackCleanTx` with `TestSession_CleanCommitClearsCurrentTx`, which verifies a write-free transaction goes through the commit path and clears `currentTx`. Updated test fakes to drop `MarkTxDirty`. ## Test plan - [x] `go build ./...` - [x] `go vet ./...` - [x] `go test ./so/db/... ./so/grpc/... ./so/stream/... ./so/task/... ./so/ent/... ./so/entephemeral/... ./so/chain/... -count=1` - [x] `rg "currentIsDirty|MarkTxDirty|OnlyCommitDirty|only_commit_dirty|KnobDatabaseOnlyCommitDirty"` — no remaining references. - [x] `./scripts/lint-knobs.sh` - [x] Integration tests (`mise test-grpc-minikube`) ## Deployment notes External knob/config systems may still carry `spark.database.only_commit_dirty`. After this lands the knob is unused; clean it up out-of-band. GitOrigin-RevId: a3fa5a309ba5ce04aaa20e81150358d290681dff
## Summary Splits the SSP unilateral-exit integration tests into two RC workflow jobs so they run in parallel against fresh Kind clusters instead of sharing one. - `rc.yaml`: adds `trigger-ssp-hermetic-unilateral-exit-lightning` and `trigger-ssp-hermetic-unilateral-exit-transfer` jobs that dispatch `webdev/ssp-spark-hermetic-test.yaml` with `spark_ssp_test_suite` set to the matching suite; existing standard ssp<>so run now passes `spark_ssp_test_suite: "standard"`. - `run-ssp-hermetic-test.yaml`: forwards `spark_ssp_test_suite: "standard"` to keep behavior unchanged for existing callers. - `package.json`: replaces `test:integration:ssp:unilateral` with `:unilateral:lightning` and `:unilateral:transfer` scripts targeting the two test files individually. GitOrigin-RevId: 394084d96d59dc0bdfbeb3e4a0690ef6fe5f2df5
## Summary Removes dead code from the unilateral exit fee bump construction logic. The ephemeral anchor output script detection blocks (which iterated over transaction outputs looking for zero-amount outputs) were never actually used after being computed — the `anchorOutputScriptHex` variable was assigned but never referenced downstream. Additionally, the `previousFeeBumpTx` variable was declared but never used. These unused code paths have been cleaned up for both the node transaction and refund transaction handling sections. ## Test Plan N/A — dead code removal only, no behavioral changes. GitOrigin-RevId: 3992032e32859e10b980a9ea0def058a3c6997a3
## Summary Fixes a bug in `constructUnilateralExitFeeBumpPackages` where the loop iterating over used UTXOs was referencing the outer `usedUtxos` variable instead of `refundFeeBump.usedUtxos`. This caused incorrect UTXO tracking when determining the `feeBumpOutPubKey`, as it was not scoped to the specific fee bump package being processed. ## Test Plan Verify that unilateral exit fee bump package construction correctly identifies the `feeBumpOutPubKey` by ensuring the loop references the UTXOs associated with each individual `refundFeeBump` entry. GitOrigin-RevId: 2e90b42ed0b819b7915f34e038671ab8c84cd4bf
## Summary
Allow Spark SDK wallet logging config to accept a bare log level such as
`log: "DEBUG"` as shorthand for `log: { level: "DEBUG" }`. This keeps
the existing object-form defaults, including TRACE method logging
defaults, while making the documented shorthand work at runtime and in
TypeScript.
## Test Plan
- `yarn --cwd sdks/js workspace @buildonspark/spark-sdk format`
- `yarn --cwd sdks/js workspace @buildonspark/spark-sdk test-cmd
src/tests/wallet-config.test.ts`
- `yarn --cwd sdks/js workspace @buildonspark/spark-sdk types`
- `git diff --check`
GitOrigin-RevId: d1021f185431ca84796568be5340d76f94b4a6ff
## Summary Replace the edge-join predicate in `validateOutputsMatchSenderAndNetwork` with a direct equality on the denormalized `created_transaction_finalized_hash` column on `token_outputs`. The previous form built `OR`'d `EXISTS` subqueries against `token_transactions` for each input being spent. Postgres optimizes those poorly: the OR'd-EXISTS shape forces it to pick the `(owner_public_key, status, network)` index on its leading-and-only-constrained column (`network`), then filter every row through the EXISTS subqueries. With ~43M MAINNET token_outputs in prod and no `owner_public_key` constraint (which only gets added when the spark invoice carries a sender public key), this falls back to scanning the whole network slice. The denormalized `created_transaction_finalized_hash` column was added in migration `20251206031312` together with a unique index on `(created_transaction_finalized_hash, created_transaction_output_vout)`. `FetchAndLockTokenInputs` already uses this fast path; this brings `validateOutputsMatchSenderAndNetwork` in line. This function is only reachable from `validateSparkInvoicesForTransaction`, so the fix is scoped to the invoice-attached transfer path — which is exactly where the slow query was observed in production traces. ### Empirical impact (prod replica, spark-1) Same query, same inputs, same data: | Form | Plan summary | Execution time | Rows scanned | |---|---|---|---| | Before (edge join, OR'd EXISTS) | `Index Scan` on `(owner_public_key, status, network)`, filter on EXISTS hashes | **132,985 ms** | 43,322,657 rows removed by filter | | After (direct denormalized column) | `BitmapOr` of 3 `Bitmap Index Scan`s on `(created_transaction_finalized_hash, created_transaction_output_vout)` | **1.6 ms** | 3 rows | Same impact in both: returns 3 rows. The new form is ~81,500× faster. Cold runs of the old form exceed the 60s gRPC handler timeout, which is exactly the symptom we observed in the OTel trace from the affected `sign_token_transaction` peer call. ## Test Plan - `mise lint` — clean. - `go test ./so/handler/tokens/... -count=1 -short` — passes. - Existing tests in `internal_prepare_token_handler_postgres_test.go` exercise `validateOutputsMatchSenderAndNetwork` indirectly via `validateSparkInvoicesForTransaction` and continue to pass. - `EXPLAIN (ANALYZE, BUFFERS)` on the prod-replica `spark-1` database (results in the table above) confirms the plan switches to bitmap-index scans and execution time drops from ~133s to ~1.6ms. GitOrigin-RevId: fed96b3fb18fe7d23f143e2b8ba382f33a04a8ff
## Summary The `retype_ssp_compensation` scheduled task finished its prod rollout — both pods reached `phase=Done` after the v1 sweep and the v2 cursor-bump re-sweep, and subsequent invocations no-op. This PR removes the task, its tunable batch-size knob (`spark.so.retype_ssp_compensation_batch_size`), the Tiltfile registration, and the companion unit test. The generic `spark.so.task.enabled@retype_ssp_compensation` and `spark.so.task.timeout@retype_ssp_compensation` knobs are dynamic config with no source references; they become harmless dead config once the task is gone and need no code change here. ## Test Plan - Purely a deletion — no behavior change to any remaining task. `mise test-unit` passes (3044 tests). - Confirmed grep returns zero remaining references to `retype` / `Retype` across the repo. Contributes to SP-2727 Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> GitOrigin-RevId: e636d00021ebe7b6304cf1e42bb3a1e5204f2950
## Summary
The transfer query metric recorder labels both `QueryPendingTransfers`
(when `KnobReadMIMODataModelQueryPendingTransfers` is off) and
`QueryTransfers` / `QueryAllTransfers` traffic as
`query_path="query_transfers"`, making it impossible to baseline
pending-RPC traffic before ramping the MIMO knob. Adds a `pending_only`
label to `transferQueryAttrs` so the legacy bucket splits cleanly:
pre-rollout you can size pending traffic via
`query_path="query_transfers" pending_only="true"`, and during/after
rollout the same series A/B-compares against
`query_path="query_pending_transfers"`.
The other recorder call sites (`get_ssp_counter_swap_filter`, both
`get_stuck_transfers` paths, `query_node_transfer_history`) pick up
`pending_only=false` via Go's zero value — no per-call-site change
needed.
## Test Plan
- Pure observability label addition; no behavior change.
- Post-deploy: confirm
`spark_transfer_query_duration_milliseconds_count{query_path="query_transfers"}`
series now break out by `pending_only="true"` vs `"false"` (one extra
dimension; existing label values unchanged).
Contributes to SP-2917
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
GitOrigin-RevId: a059c7d2ec3d7e76381ed24f159dc247e192f1a5
## Summary Version the JS packages after publishing `@buildonspark/spark-sdk@0.7.17`. This removes the consumed changeset, updates generated changelog entries, bumps the linked workspace package versions, and refreshes the JS lockfile to point dependents at the published SDK version. ## Test Plan - `yarn --cwd sdks/js install --immutable` - `git diff --check` --------- Co-authored-by: Lightspark Eng <engineering@lightspark.com> GitOrigin-RevId: 9074c7bd827660f13f450a4c86c26a99e055ef60
## Summary When coop exit key tweaking fails on one of the leaves, the partial state causes an error that prevents progress later. The coop exit then becomes stuck. This fixes that by handling the partial state gracefully. GitOrigin-RevId: 6e29bf2a2a725ec40894727d5c02eaa7c6d2bb09
## Summary User errors are being returned as general errors in a variety of RPC endpoints, which contributes to alert noise in monitoring. This classifies them correctly. GitOrigin-RevId: c3fc161d8cccd221e4e1679685bd036613b1d658
## Summary Extracts the duplicated unilateral-exit test logic that was copy-pasted verbatim between `unilateral-exit-lightning.test.ts` and `unilateral-exit-transfer.test.ts` into two shared modules: - **`tests/utils/unilateral-exit-helpers.ts`** — reusable test utilities: `didTxSucceed`, `makeExternalFundingUtxo` (faucets a fresh P2WPKH wallet and returns a ready-to-use UTXO + signing key), and `broadcastUnilateralExit` (drives the full fee-bump broadcast pipeline, mining timelocks and asserting confirmations). - **`tests/integration/ssp/unilateral/shared.ts`** — shared constants (`DEPOSIT_AMOUNT`, `TRANSFER_AMOUNT`, `EXTERNAL_FUNDING_AMOUNT`) and wallet helpers (`closeWallets`, `waitForWalletBalance`, `waitForWalletLeaves`, `initializeWalletWithConnectedStream`, `createClaimedWallet`, `unilateralExitLargestLeaf`) used by both SSP unilateral-exit integration tests. Both SSP test files now import from `shared.ts` instead of duplicating ~170 lines each. The `unilateral-exit.test.ts` "should unilateral exit" test is also simplified to use `broadcastUnilateralExit`, and the "watchtower" test is refactored to use `makeExternalFundingUtxo`. Describe labels are updated from `"SSP unilateral exit1/2"` to `"SSP unilateral exit — spark transfer"` and `"SSP unilateral exit — lightning receive"` for clarity. ## Test Plan Existing integration tests cover this — no behavioral changes were made, only deduplication. Run the unilateral-exit integration test suites against a local Spark stack to verify. GitOrigin-RevId: f52792e81d0a4d579f0ffe16f5cd790020dd4230
## Summary Update cargo.lock file to include FROST 3.0.0 ## Test Plan CI GitOrigin-RevId: 7297e1cfb6428450042f97cd5d9e27f30c87e4c9
…pening (#6414) ## Summary Fixes a coop exit recovery issue where signing keyshare rotation could commit the ephemeral DB transaction during chain watcher block processing, leaving later leaf tweaks unable to continue. The chain watcher can now reopen ephemeral transactions after inline rotation commits, and coop exit key tweaking can resume when some leaves were already applied. ## Summary - Allow chain watcher tx-backed ephemeral sessions to reopen a fresh ephemeral transaction after the current one is committed or rolled back. - Roll back the current ephemeral session transaction instead of a stale captured transaction handle. - Make coop exit key tweaking retryable by skipping leaves whose key tweak was already cleared. - Freeze the signing keyshare dual-write decision for the whole coop exit tweak loop. - Update ephemeral DB docs for chain watcher rotation behavior. ## Testing - `mise exec -- go test ./so/chain` - `mise exec -- go test ./so/ent ./so/db` - `git diff --check` GitOrigin-RevId: 5f91a1980babb0f4af3a7acee6f12c91e4b13103
## Summary Bounds Spark SDK file logging shutdown so wallet cleanup cannot wait indefinitely on a stalled file sink. The close path still flushes method logs, remains idempotent, and treats file logging failures as non-fatal. ## Test Plan - `yarn --cwd sdks/js workspace @buildonspark/spark-sdk test-cmd src/tests/logging-service.test.ts src/tests/wrapPublicMethod.test.ts` - `yarn --cwd sdks/js workspace @buildonspark/spark-sdk types` - `yarn --cwd sdks/js workspace @buildonspark/spark-sdk format` - `git diff --check` GitOrigin-RevId: 768e427c68e7c537dd2e3db2515dead69a234ae3
…#6407) ## Summary Phase 5 cleanup for the `RECEIVER_CLAIM_PENDING` rollout. Phase 2a (#6354) added `INITIATED` as a transitional companion to `RECEIVER_CLAIM_PENDING` in the MIMO claim path's receiver-status switches. With Phase 2b dual-writes merged and the Phase 3 backfill task drained + removed (#6379), every post-sender-tweak receiver row now goes straight to `RECEIVER_CLAIM_PENDING`, so the transitional `INITIATED` arms are unreachable in this code path. Five sites in `so/handler/transfer_handler.go`: - `ClaimTransfer` claimable-status check — drop `INITIATED` from the "ok" cases. `validateTransferReadyForReceiverClaim` rejects pre-sender-tweak transfers, so a receiver at `INITIATED` can no longer legitimately reach this code. - `ClaimTransfer` `useStoredKeyTweaks` switch — collapse the second case group into a `default`, which captures the existing "no stored tweaks yet" comment more directly. Satisfies the exhaustive lint without re-enumerating the dead `INITIATED` arm. - `InitiateSettleReceiverKeyTweak` `validateClaim` — drop `INITIATED`; receiver at `INITIATED` now falls through to `default` with a clear "unexpected status" error. - `InitiateSettleReceiverKeyTweak` auto-promote — simplify from `(INITIATED || RECEIVER_CLAIM_PENDING)` to just `RECEIVER_CLAIM_PENDING`. Comment rewritten to drop the transitional disclaimer. - `SettleReceiverKeyTweak` rollback switch — drop `INITIATED` from the "do nothing" case list. Plus one doc-comment update on `mimoStuckReceiverStatuses` in `so/handler/ssp_request_handler.go` documenting the deliberate exclusion of `INITIATED` and `RECEIVER_CLAIM_PENDING` per the open question in SP-2923's step 5 — pre-claim states aren't "stuck", they just haven't been polled yet. ####⚠️ Risks Each `INITIATED` removal is a behavior change at the case boundary: branches that currently silently tolerate `INITIATED` now fall through to `default`, which (mostly) returns an explicit error. The audit trail is "Phase 2b dual-write + Phase 3 backfill drained → no post-tweak `INITIATED` rows exist". A pre-flight prod check is in the Test Plan below. This PR ships independently of Phase 4 (#6345) ramp — `KnobReadMIMODataModelQueryPendingTransfers` is fully off in prod, so the read path swap is dormant code; the claim-path changes here are gated only on dual-write + backfill, both done. ## Test Plan - Pre-merge prod check: confirm no post-tweak rows are still at `INITIATED`. Should return zero: ```sql SELECT COUNT(*) FROM transfer_receivers tr JOIN transfers t ON t.id = tr.transfer_id WHERE tr.status = 'INITIATED' AND t.status IN ('SENDER_KEY_TWEAKED', 'RECEIVER_KEY_TWEAKED', 'RECEIVER_KEY_TWEAK_LOCKED', 'RECEIVER_KEY_TWEAK_APPLIED', 'RECEIVER_REFUND_SIGNED'); ``` - Existing handler test suite (`go test ./so/handler/`) covers each of the 5 modified switches — including `TestInitiateSettleReceiverKeyTweak_RefundSignedReturnsEarly`, `TestSettleReceiverKeyTweak_RejectsEarlyTransferStatus`, and the `revertClaimTransfer` table tests that assert receiver post-state — all pass against the simplified branches. Contributes to SP-2923 --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> GitOrigin-RevId: 9d1afa66203ddfd0e8600c8a9353a331685e03d2
## Summary Drops the legacy partial index `idx_transferreceiver_pending_pubkey_time` on `transfer_receivers`, which was added in #6109 to cover `INITIATED` + 4 `RECEIVER_*` stuck statuses. After the `RECEIVER_CLAIM_PENDING` rollout completed (Phase 1's new partial in #6350, Phase 2b dual-write in #6351, Phase 3 backfill, Phase 4 read-path swap in #6345), the legacy index is no longer driving any query and is dead weight on writes. The new partial `idx_transferreceiver_claim_pending_pubkey_time` (`RECEIVER_CLAIM_PENDING` + 4 `RECEIVER_*` stuck) takes over both consumers cleanly: - `queryPendingTransfersMIMO` receiver arm — Phase 4 dropped `INITIATED` from `mimo.PendingReceiverStatuses()`, so it filters on the exact set the new partial covers. (Note: this query is gated by `KnobReadMIMODataModelQueryPendingTransfers`, which is fully off in prod — so it isn't running today regardless.) - `buildStuckTransferIDsQueryByPubkey` receiver arm — filters on the 4 stuck statuses, a strict subset of the new partial's `WHERE`. Same leading-equality on `identity_pubkey`, same `(create_time DESC, transfer_id DESC)` ordering. The planner picks it up; the residual `r.status = ANY($3)` filter drops the `RECEIVER_CLAIM_PENDING` rows. The receiver-arm comment in `ssp_request_handler.go` is updated to reflect the new index, and the `dbseed` README + CLAUDE.md sections describing partial-index population are simplified to the single remaining receiver-pending partial. ####⚠️ Migration safety `./scripts/check-migration-safety.sh` flagged `DROP INDEX` as REVIEW (yellow). Analysis: - The drop uses `DROP INDEX CONCURRENTLY IF EXISTS` with `-- atlas:txmode none` — non-blocking and idempotent. - During the canary window where one prod pod has the legacy index and one doesn't, both consumers' queries continue to work: pre-PR-2 pods walk the legacy partial, post-PR-2 pods walk the new one. The read knob being off prevents the most sensitive consumer (`queryPendingTransfersMIMO`) from running at all. ## Test Plan - `./scripts/check-migration-safety.sh spark/so/ent/migrate/migrations/20260501185133_drop_legacy_pending_pubkey_time_index.sql` — REVIEW (yellow) for `DROP INDEX`, no hard failures. Backwards-compatibility analysis above. - Existing `go test ./so/handler/ ./so/ent/...` test suite passes against the regenerated schema. - Pre-merge prod planner check (recommended): run `EXPLAIN (ANALYZE, BUFFERS)` for the receiver arm of `buildStuckTransferIDsQueryByPubkey` against `sparkoperator_0` with a medium-cardinality pubkey, both before and after dropping the legacy index locally on a copy of the dataset. Confirm the plan switches to `idx_transferreceiver_claim_pending_pubkey_time` and exec time / buffer counts stay in the same ballpark. Mirrors the perf probe approach used for the rollout's earlier phases. ```sql EXPLAIN (ANALYZE, BUFFERS) SELECT r.transfer_id FROM transfer_receivers r INNER JOIN transfers t ON t.id = r.transfer_id WHERE r.identity_pubkey = '\\x...' AND r.status = ANY(ARRAY['RECEIVER_KEY_TWEAKED', 'RECEIVER_KEY_TWEAK_LOCKED', 'RECEIVER_KEY_TWEAK_APPLIED', 'RECEIVER_REFUND_SIGNED']) AND r.create_time < NOW() ORDER BY r.create_time DESC, r.transfer_id DESC LIMIT 100; ``` Contributes to SP-2923 Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> GitOrigin-RevId: 4cb30407d49812574d1906b6a5de24a5d7cdf476
…#6429) ## Summary When a coordinator SO's request transaction aborts (e.g. the gRPC client cancels mid-flow after `ConsensusPrepare` fan-out has already succeeded against participants), its `FlowExecution` row never lands in the database. Participant rows stay in `IN_FLIGHT` with locked resources, and the reconciler — seeing `OUTCOME_UNSPECIFIED` from `ConsensusQueryOutcome` — currently logs "possible data loss" and leaves the row alone, forcing manual intervention. This adds a presumed-abort recovery path: - New nullable `prepare_payload` bytea column on `flow_executions`. Set on PARTICIPANT rows at Create time by the consensus dispatcher; immutable afterward. Schema hook is relaxed to permit this on PARTICIPANT and to forbid it on COORDINATOR (mirroring the existing `decision_payload` constraint). - In the reconciler's `UNSPECIFIED` branch, if the row is past `defaultFlowExecutionPresumedAbortAgeSec` (1200s — twice the existing coordinator stall threshold) and carries a persisted prepare op, synthesize a local `ConsensusRollback` gossip with the stored op as `Operation` and dispatch it through `GossipHandler.HandleGossipMessage`. That runs `FlowHandler.Rollback` to release any locked state and transitions the participant row to `ROLLED_BACK` in one shot — no second sweep tick required. - Pre-rollout rows (NULL `prepare_payload`) hit the unchanged log-and-skip path, so the change is safe to deploy ahead of any flips. - Gated by the existing `KnobFlowExecutionReconcileEnabled` (off by default); no new knobs introduced. This is defense-in-depth. The underlying engine bug — coordinator `FlowExecution` row write tied to the request transaction and cleanup paths running on the user-cancellable context — will be fixed separately. With this patch in, even a recurrence will self-heal once a row crosses the 20-minute threshold. ## Test Plan - New schema-hook tests in `spark/so/ent/flow_execution_test.go` cover `COORDINATOR must not set prepare_payload` and `PARTICIPANT may set prepare_payload`. - New reconcile tests in `spark/so/task/flow_execution_reconcile_test.go` cover the three cases: legacy row (NULL payload, log+skip), young row + payload (under threshold, leaves IN_FLIGHT), old row + payload (presumed-abort fires, row → ROLLED_BACK). - `mise lint` (Go) — `0 issues`. - `mise test-go` — `3048 tests passed, 2 unrelated skips`. GitOrigin-RevId: ded0be6b92111938905c8f45d0c9c123931bfeb2
## Summary Pure relocation cleanup, no behavior change. Centralizes the status sets, migration-phase compat helpers, and SQL fragment builders that the pending- and stuck-transfer raw-SQL queries rely on into `spark/so/mimo`. The package was introduced in #6345 with a deliberately narrow initial scope (just `PendingReceiverStatuses` / `PendingSenderStatuses`); this completes the migration. ### Technical Notes The signature shift on the SQL helpers is the only non-mechanical bit: the old `pendingCommonFilters(sqlArgs, args queryMIMOPendingArgs)` couldn't move cleanly because `queryMIMOPendingArgs` is handler-private. The new `mimo.AppendPendingCommonFilters(sqlArgs, network, types, transferIDsFilter)` takes the individual fields, leaving the struct in handler/ alongside the query builders that own it. `appendNetworkFilter` and the network branch of `pendingCommonFilters` were near-duplicates; both relocated as-is rather than deduped, to keep this PR strictly relocation. A follow-up can collapse them once the error-string difference (`"failed to convert proto network"` vs `"invalid network"`) is reviewed. ## Test Plan - Existing `mimo/status_test.go` (`TestPendingStatusesDisjoint`) and the relocated `mimo/compat_test.go` suite (6 tests covering edge/column fallback for sender + receiver) all pass against the new package. - `so/handler/...` test suite green — including `TestGetStuckTransfers_*` which exercises the relocated stuck-status sets and SQL helpers end-to-end through `getStuckTransfersMIMO`, and `TestQueryPendingTransfersMIMO_*` which covers all three pending-arm SQL builders that now call `mimo.AppendPendingCommonFilters` / `AppendPendingTimeFilter`. - Net diff is −262 LOC; reviewer can sanity-check by greping for any remaining references to the old unexported names — none should exist. Resolves SP-2916 --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> GitOrigin-RevId: 2e4c9baa0016aff039e2b9678c610203d4eafeaa
## Summary Adds ESLint to the `@buildonspark/spark-sdk` package using the published `@lightsparkdev/eslint-config` base config. The package lint scripts now run ESLint directly, and the package declares the shared config, ESLint, and eslint-watch as dev dependencies. This first PR keeps the rollout intentionally broad-but-contained: it adds the config and initial mechanical cleanup while leaving generated artifacts, tests, and remaining dynamic boundary modules ignored so the shared base rules can be enabled without weakening them. Follow-up stacked PRs opt additional files in with focused cleanup. ## Test Plan - `yarn workspace @buildonspark/spark-sdk lint` - `yarn workspace @buildonspark/spark-sdk types` - `yarn workspace @buildonspark/spark-sdk format` --------- Co-authored-by: Lightspark Eng <engineering@lightspark.com> GitOrigin-RevId: ab2c397bc61c4a9bae4ff9b357e78c643e25f9e6
…ound (#6440)
## Summary
Both `reconcile_stuck_flow_executions` and `sweep_stale_coordinator_flow_executions` now surface their findings as a task-level error so the scheduler's failure-logging pipeline routes them to the Slack alerting channel. Pre-change the tasks returned nil regardless of what they found, so a stuck participant row past the 300s threshold or a stale coordinator row past the 600s threshold only manifested via dashboard gauges or follow-on user-visible effects.
### Behavior
- **Healthy steady-state (no stuck rows) still returns nil** — quiet operators stay quiet.
- **Any stuck PARTICIPANT or stale COORDINATOR rows found by the threshold-based query → task returns an error.** This fires whether or not the reconciler successfully transitioned the rows on this tick: the alert is about the existence of the stuck condition, not the recovery outcome.
- **Recovery is unchanged.** Participant rows still go through the existing query-coordinator-then-dispatch-gossip path; coordinator rows still get bulk-transitioned to ROLLED_BACK (when the recovery knob is on) or logged-only (monitor-only mode). The error fires alongside that work.
- **Error message format** — `found N stuck PARTICIPANT/COORDINATOR flow_execution rows: {id=… op_type=… age=…}, {…}, {…} (+N more)`. Sample size capped at 3 with a count suffix so the Slack alert is human-skimmable but still indicates scale.
### Why no knob
Default-on (always alert when stuck rows are found). Stuck rows past these thresholds are abnormal under healthy operation; the existing alerting infrastructure already routes task errors, so the simplest implementation is an unconditional return-error. If a specific environment proves too noisy we can add a knob later — but with the engine cleanup-ctx fix in place this signal should be quiet most of the time.
## Test Plan
Updated all 13 existing reconcile/sweep tests to expect the new error contract via a small `requireStuckRowsErr(t, err, role)` helper — they continue to verify per-row recovery behavior alongside.
Three new tests:
- `TestReconcile_NoStuckRows_ReturnsNil` — steady-state participant reconcile must stay quiet.
- `TestSweepStaleCoordinatorFlows_NoStaleRows_ReturnsNil` — same for coordinator sweep.
- `TestStuckFlowExecutionError_IncludesActionableContext` — seeds 5 stuck rows and asserts the error message contains the role-qualified count, per-row `op_type=…`, and the `+2 more` suffix so the Slack alert reflects scale.
### Verification
- `mise lint` — `0 issues`.
- `mise test-go` — `3063 tests passed, 2 unrelated skips`.
- **Live integration sanity** — restarted the 5-SO `run-everything.sh` env, observed `reconcile_stuck_flow_executions` tick at the 30s interval and report `Task executed successfully` (no stuck rows in steady state). Confirms the task scheduler doesn't break under the new error contract when there's nothing to alert on.
GitOrigin-RevId: b3e8958bf4f06cb98ae87e63bbd07844e56a13a0
## Summary Continues the spark-sdk ESLint rollout on top of the base config PR by opting additional source modules and targeted test-adjacent files into the shared base rules. The changes are grouped as typed-boundary cleanup, async mock normalization, and small lint fixes needed before enabling the remaining test tree. This PR intentionally keeps the final broad `src/tests/**` opt-in out of scope; that is isolated in the next stacked PR. ## Test Plan - `yarn workspace @buildonspark/spark-sdk lint` - `yarn workspace @buildonspark/spark-sdk types` - `yarn workspace @buildonspark/spark-sdk format` GitOrigin-RevId: 3cd138f33827d811360dcfbde8a3a2b0515a10e6
…ld (#6485) ## Summary `ValidateShare` accepted fewer Feldman VSS proofs than required by the threshold, causing the verification loop to degenerate to a trivially-satisfiable check. An attacker could provide independent random shares to each SO that all pass validation individually but are not on the same polynomial, destroying FROST's threshold reconstruction property. ## Test plan - [ ] `go test ./common/secret_sharing/... -run TestVerifiableSecretSharing` passes, including new `CatchInvalidProofLengthTooFew` sub-test - [ ] `golangci-lint run ./common/...` reports 0 issues - [ ] Existing `CatchInvalidProofLengthTooMany` sub-test continues to pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> GitOrigin-RevId: 103ef52895213ed7d25301e7e2d29ee48742bd6d
## Summary This change closes a gap in the key tweak validation path: without the cross-check, a client could submit a `SendLeafKeyTweak` whose `SecretShareTweak` and `PubkeySharesTweak` are inconsistent. The SO relies on `PubkeySharesTweak` to aggregate the combined public key after a transfer, so an inconsistent value could corrupt the resulting leaf ownership key. ## Test plan - [ ] `golangci-lint run ./so/...` passes (0 issues) - [ ] `TestValidateTransferPackage_PubkeyShareTweakMismatch` fails before the fix and passes after - [ ] All existing `TestValidateTransferPackage_*` tests continue to pass - [ ] Integration tests via `mise test-grpc-minikube` (pre-merge) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> GitOrigin-RevId: c328ec7ee3e77d95ef4a7d4fa119407266578fff
## Summary `SparkSspInternalService.CreateTree` accepted SSP/client-provided node and refund transactions after checking only that their output totals did not exceed the supplied previous output. It did not prove that the transaction input actually spent the parent node/deposit outpoint before building SO signing jobs. A malformed SSP request could therefore get SO signature shares over a transaction that is not anchored to the requested Spark parent, or store refund transactions that do not spend the created node and make funds unrecoverable. This change binds every CreateTree signing job to the expected outpoint: - node and direct-node transactions must have exactly one input spending the parent outpoint - refund transactions must have exactly one input spending the node output they refund - direct refund transactions must spend the direct node output - direct-from-CPFP refund transactions must spend the CPFP node output For parent-node splits, CreateTree now also requires the parent node to belong to the authenticated request identity and to be in a split-eligible state (`CREATING` for in-flight tree construction or `AVAILABLE` for an existing live node). This prevents an SSP-side request from locking another owner's parent node or creating children for nodes already locked by another operation. ## Test Plan - `go test -tags lightspark ./so/handler -run 'TestPrepareSigningJobsRejects(NodeTxWrongParentOutpoint|RefundTxWrongNodeOutpoint|ParentNodeOwnerMismatch|IneligibleParentNodeStatus)' -count=1` - `go test -tags lightspark ./so/handler -count=1` - `git diff --check` - Attempted `go test -tags lightspark ./so/grpc_test_internal -run 'TestTreeCreationWithMultiLevels|TestTreeCreationAddressGeneration' -count=1`; local cluster failed before exercising the patch with `unknown service spark_ssp_internal.SparkSspInternalService`. GitOrigin-RevId: 41fe18b89c68feff3110f4985cacc0944a2ecf6c
## Summary Bind the SendTransferV3 refund signing helper to the concrete parent transaction it signs against. Previously `buildSigningJobForRefund` computed a Taproot sighash using parent tx output 0, but did not check that the caller-provided refund transaction actually spent `parent_tx:0`. The higher-level StartTransferV3 path validates transfer packages before signing, but this helper is the boundary that constructs the FROST signing message. If a future path or validation gap reached it with a same-value/same-script refund for a different outpoint, an SO could sign the wrong spend. This change rejects refund transactions unless they have exactly one input and input 0 spends the parent tx output being signed. Tests now cover the shared helper directly and the package-level CPFP, direct, and direct-from-CPFP transfer-package lists that feed it. ## Test Plan - `env -u MINIKUBE_IP go test ./so/handler -run 'TestBuildSigningJobForRefundValidatesParentOutpoint|TestBuildSendTransferAggregationJobsValidatesAllRefundPackageOutpoints' -count=1 -timeout=180s`\n- `env -u MINIKUBE_IP go test ./so/handler -count=1 -timeout=300s`\n- `git diff --check` GitOrigin-RevId: 8a986e4e242a7bdd3ed6904f2542b7f85d0c105e
## Summary Force-resolve transitive `protobufjs@8.0.0` to `8.0.1` to clear a Critical advisory ([GHSA-xq3m-2v4x-88gg](GHSA-xq3m-2v4x-88gg) — arbitrary code execution in protobufjs). Dependency path (transitive only — nothing in the repo depends on it directly): `sdks/js/apps/artillery` → `artillery@^2.0.31` → `@artilleryio/int-core@2.26.0` → `@opentelemetry/otlp-transformer@0.212.0` → `protobufjs@8.0.0` `@opentelemetry/otlp-transformer@0.212.0` pins `protobufjs` exactly at `8.0.0` (no caret), so a normal upgrade won't pick up the fix. Added a Yarn `resolutions` entry in `sdks/js/package.json` forcing `protobufjs@8.0.0` → `8.0.1`. Rest of the workspace was already on the safe `protobufjs@7.5.7` line, and Artillery is dev-only, so production runtime exposure was effectively nil — this is hygiene to keep `yarn npm audit` clean. ## Test Plan - `yarn install` from `sdks/js/` — succeeds; lockfile resolves `protobufjs@8.0.1`. - `yarn why protobufjs` — otlp-transformer branch shows `8.0.1`; other branches still on `7.5.7`. - `yarn npm audit --recursive --all | grep -c "Severity: critical"` → `0` (was `1`). GitOrigin-RevId: 546397492c143ca2e0d312c96c01934ebd28eef4
## Summary Recheck tree status immediately before creating exit transaction signing jobs. ## Test Plan - `/Users/kph/.local/share/mise/installs/go/1.25.1/bin/go test -tags lightspark ./so/handler -run TestSignExitTransactionRejectsTreeExitedAfterValidation -count=1 -v` GitOrigin-RevId: fd8374277142e354ce68807c51ea8fc27962abd4
## Summary Fail closed when a legacy claim path sees an existing multi-receiver transfer. ## Test Plan - go test -tags lightspark ./so/handler -run 'TestClaimTransferRejectsExistingMultiReceiverWhenMimoReadDisabled' -count=1 GitOrigin-RevId: f639d784716f31103e1b5f1afde5c45a9bce489f
## Summary Require verifiable secret shares to include exactly one polynomial commitment per threshold coefficient. This prevents malformed short proof sets from passing preimage-share validation. ## Test Plan - `GOFLAGS=-tags=lightspark go test ./common/secret_sharing -run 'TestVerifiableSecretSharing' -count=1 -timeout=5m`\n- `GOFLAGS=-tags=lightspark go test ./so/handler -run 'TestValidatePreimageShareRejectsShortProofSet' -count=1 -timeout=5m` GitOrigin-RevId: 4d35d6a542a5f920a902824103357bf1477b00a0
## Summary Keep unit testing to not rely on running services GitOrigin-RevId: 023f36ece3b08bdf4cfeaff8c526cdaff7d0d7ba
## Summary Extends the proto layer so transfer responses carry richer per-participant data. - Adds `id` and `completion_time` to `TransferReceiver` (`status` was added in #7287) - Adds a new `TransferSender` message with `id` + `identity_public_key` - Adds `repeated TransferSender senders = 14` to `Transfer`. For single-sender flows today this always has one entry mirroring `sender_identity_public_key` — starts the deprecation clock on that legacy field ahead of MIMO v1 - Adds `string transfer_receiver_id = 8` to `TransferLeaf` so consumers can associate each leaf with its receiver (load-bearing for MIMO v0 multi-receiver transfers) Go marshaling in `marshalWithLeaves` and `marshalTransferLeafProto` populates the new fields from the preloaded `TransferSenders` / `TransferReceivers` edges. All changes are additive — older SDK clients that don't read the new fields are unaffected. ## Test Plan - `TestMarshalProto_PopulatesSenders` — confirms `Senders[]` is populated from the `TransferSenders` edge - `TestMarshalProto_PopulatesReceiverIDAndCompletionTime` — confirms `Id` populates always; `CompletionTime` only when the schema field is non-zero - `TestMarshalProto_PopulatesLeafTransferReceiverID` — confirms each marshaled leaf carries its `transfer_receiver_id` --- Contributes to SP-2721 created with claude session ec9dd432-5dc5-409c-9a8c-89e7cfd57e47 --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> GitOrigin-RevId: 1f213e38cd2486a8e3f27672edba5e3011da363b
…ageRequests (#5528) ## Summary Fixed a race condition where returning a stuck outbound transfer could leave the corresponding inbound transfer claimable by the receiver. ## Test Plan - New unit test `TestReturnStuckTransfers_AlsoCancelsSiblingPreimageRequest`: creates Transfer A and B sharing a `payment_hash`, calls `ReturnStuckTransfers` with Transfer A, asserts both PreimageRequests are `RETURNED`. - `mise lint && mise test-unit` pass. --------- Co-authored-by: Claude <noreply@anthropic.com> GitOrigin-RevId: a2e4032a5d31eaed2666532967b9df921978a6f8
## Summary Swap transfer packages can include optional direct and direct-from-CPFP refund transactions. The existing swap validation path only validated CPFP refund transactions, while the signing path still built FROST signing jobs for the optional direct refund jobs when they were present. This change keeps those direct refund jobs optional for swap flows, but validates any supplied direct or direct-from-CPFP refund transaction before the transfer is persisted or signed. It adds regression coverage for invalid direct refund payloads across swap, counter-swap, and swap-v3 transfer types. ## Test Plan - `go test ./so/handler -run 'TestValidateUserTxs_Swap_Package' -count=1`\n- `go test ./so/handler -count=1` GitOrigin-RevId: ee6bb405b372f0da87e36ffc94428d138a4ae9e9
## Summary `PrepareTreeAddress` and `CreateTree` can operate from either an on-chain deposit output or an existing parent node output. Both requests are authenticated as `user_identity_public_key`, but the tree-creation path did not verify every deposit-address row or parent node used by the request belonged to that identity. That left the SSP-facing path able to target another user identity deposit address or available parent node. Even without the caller having the victim user signing share, the operator-side work can allocate derived keyshares, create incorrect in-flight tree state, and, for parent-node splits, eventually mark the victim parent as split. This change requires the request identity to match the parent deposit address before `PrepareTreeAddress` or `CreateTree` continues, requires `ParentNodeOutput` sources to match the parent node owner, and requires every child output resolved during `CreateTree` to use a deposit address owned by the same request identity. The tests cover parent-node owner mismatch, `CreateTree` deposit-address owner mismatch, `PrepareTreeAddress` deposit-address owner mismatch, and child-output deposit-address owner mismatch. ## Test Plan - `env -u MINIKUBE_IP go test -tags lightspark ./so/handler -run "TestPrepare(SigningJobs|TreeAddress)Rejects(ParentNodeOwnerMismatch|DepositAddressOwnerMismatch|ChildDepositAddressOwnerMismatch)" -count=1 -v -timeout=180s` - `env -u MINIKUBE_IP go test -tags lightspark ./so/handler -count=1 -timeout=240s` - `git diff --check` GitOrigin-RevId: 4ce7ac8dd0e3fa6e5fbca47b7a3897eae22e0267
## Summary PR 3 of 3 in the **migrate v3 send-transfer to 2PC** stack. Stacked on #6560 (flow body) and #6528 (plumbing). Final cutover plus integration tests. Flips \`StartTransferV3\` to route through the 2PC engine when \`KnobUseConsensusTransfer\` is set. The legacy \`syncTransferV3Init\` + \`syncSettleSenderKeyTweaks\` fanout stays in place for one release cycle behind the knob default of 0. ### Production change - **\`StartTransferV3\`** reads \`KnobUseConsensusTransfer\`; non-zero routes to \`startTransferV3Consensus\`, zero falls through to \`startTransferV3Internal\`. - **\`startTransferV3Consensus\` (new)** runs the coordinator-only gates that participants can't redo from inside the engine — single-sender guard, sender identity auth (\`EnforceSessionIdentityPublicKeyMatches\`), the multi-receiver MIMO knob check, and the transfer-size limit guard. Then builds the coordinator flow via \`buildSendTransferCoordinatorFlow\`, fetches the engine, and runs \`Execute\` over \`CONSENSUS_OPERATION_TYPE_SEND_TRANSFER\`. The response is the one \`BuildCommitPayload\` populated on the coordinator flow. ### Flow handler fixes (uncovered during end-to-end local verification) Local verification baked the knob into \`so.template.config.yaml\` temporarily (reverted before commit). Two issues showed up that didn't surface in PR 2's unit tests: 1. **Per-job public-shares filter.** \`aggregateLeafSignature\` now derives \`publicShares\` from the per-job signature shares it actually received, not the global \`participantIDs\` set. For send-transfer, different leaves can carry different round1 commitment sets (the user picks the t-of-n signing set per leaf), so \`participantIDs\` is a superset of any given job's signing set. The renew_leaf pattern got away with the superset because every job in renew is for the same leaf and thus the same signing set; transfer can't rely on that. 2. **Missing \`UserSignatureShare\`.** \`aggregateLeafSignature\` now passes \`UserSignatureShare\` to \`AggregateFrost\`. The user provides one signature share per refund tx via \`UserSignedTxSigningJob.UserSignature\`; FROST aggregation needs that share. Tracked alongside the \`helper.SigningJobWithPregeneratedNonce\` on \`sendTransferLeafSigningJobs\` so \`BuildCommitPayload\` can thread it through. ### Integration tests (\`send_transfer_consensus_test.go\`) | Test | What it asserts | |------|------------------| | \`TestSendTransferV3_Consensus_HappyPath\` | Knob on, v3 send → \`SENDER_KEY_TWEAKED\` on every operator's DB, receiver claims successfully end-to-end. | | \`TestSendTransferV3_Consensus_WritesFlowExecutionRows\` | Knob on, v3 send → each operator writes exactly one new \`FlowExecution\` row, same id across operators, \`COMMITTED\`, \`COORDINATOR\` role on the coordinator and \`PARTICIPANT\` on the rest. | | \`TestSendTransferV3_Consensus_KnobOffUsesLegacyPath\` | Knob off, v3 send still succeeds and writes **no** SEND_TRANSFER \`FlowExecution\` rows — guards against the routing check silently flipping. | All three gate on \`HasLocalSparkIngressHost\` (canonical "minikube-only" check) plus \`NewKnobController\` availability — same pattern as the renew_leaf consensus tests. ## Proof of Work End-to-end run against the local env (5 operators, knob baked into static config, then reverted before commit): \`\`\` $ go test -v -run TestV3ClaimTransferSingleReceiver ... transfer reached the consensus path, completed signing, applied tweaks ... $ psql -d sparkoperator_N -c "SELECT status FROM transfers ORDER BY create_time DESC LIMIT 1;" operator_0: COMPLETED operator_1: COMPLETED operator_2: COMPLETED operator_3: COMPLETED operator_4: COMPLETED $ psql -d sparkoperator_N -c "SELECT role, status, op_type FROM flow_executions WHERE op_type = 4 ORDER BY create_time DESC LIMIT 1;" operator_0: COORDINATOR | COMMITTED | 4 operator_1: PARTICIPANT | COMMITTED | 4 operator_2: PARTICIPANT | COMMITTED | 4 operator_3: PARTICIPANT | COMMITTED | 4 operator_4: PARTICIPANT | COMMITTED | 4 \`\`\` ## Test Plan - \`go build ./...\` → clean - \`golangci-lint run ./so/handler/... ./so/grpc_test/...\` → 0 issues - \`go test ./so/handler/... ./so/knobs/...\` → all pass - End-to-end local verification confirmed the consensus path produces the same observable end-state as the legacy fanout (see Proof of Work above). GitOrigin-RevId: 667e956448d06aded48ce4facb1e0d63b1272383
## Summary Adds a scheduled, knob-gated backfill for legacy `signing_keyshares` rows that still store their secret only in the main database. The backfill copies each legacy `secret_share` into the ephemeral `signing_keyshare_secrets` table at version `0`, then updates the main `signing_keyshares.secret_version` pointer. It processes rows in bounded batches ordered by UUIDv7 `id`, uses `FOR UPDATE SKIP LOCKED` to avoid blocking production operations, and is idempotent if the ephemeral write succeeds but the main DB commit fails. The task is disabled by default behind: `spark.so.signing_keyshare.backfill_secrets_enabled` ## Test Plan - `mise exec -- go test ./so/task -run TestBackfillSigningKeyshareSecrets` - `mise exec -- go test ./so/task ./so/knobs` GitOrigin-RevId: a5537a422d7034cb341096fe408737ba7571437f
## Summary When `claim_transfer` hits a `NOWAIT` lock conflict on the transfer row, the SO returns gRPC `ABORTED` with a message that leaks Postgres `SQLSTATE 55P03` and the internal `"transfers"` table name — and no retry hint. Third-party clients can't tell the operation is safe to retry. This PR sanitizes the wire message at all three `claim_transfer` paths (`ClaimTransfer`, `ClaimTransferTweakKeys`, `claimTransferSignRefunds`) and attaches `google.rpc.RetryInfo` (100ms) to `AbortedConcurrentClaimConflict` and `AbortedTransactionPreempted`. The original Postgres error stays in server-side logs for debuggability. ## Test Plan - New unit tests in `errors_test.go` cover `RetryInfo` attachment, wire-message sanitization, and retry-hint preservation across `WrapErrorWithMessage` / `WrapErrorWithReasonPrefix` - Round-trip test verifies `RetryInfo` survives serialization to a bare `*status.Error` (the form non-SDK clients see) --- Resolves SP-3153 created with claude session fb9e54f1-6a3a-4b69-95d6-debb37735543 --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> GitOrigin-RevId: 0ecd8ebb92c65e7f22473d0fbe464b26d55c2b79
## Summary The proto contract on `GetSigningCommitmentsResponse` ([`spark.proto:1007-1013`](../blob/main/protos/spark.proto#L1007-L1013)) states that commitments are returned in the requested node-ID order, `[count] x [num_node_ids]`. The handler didn't enforce that — when one or more requested IDs weren't in `tree_nodes`, it silently returned commitments only for the IDs it found, with no indication the response was truncated. Downstream, that surfaces as an opaque `IndexError` in sparkcore's `gen_signing_commitments` (which assumes the contract holds). This PR rejects requests for unknown node IDs with `NotFound` and names the missing IDs, so callers can fix the reference (or surface the failure cleanly) instead of debugging an off-by-one in the consumer. ## Test Plan - New `TestGetSigningCommitmentsRejectsUnknownNodeIDs` covers the missing-ID path - Updated the existing `TestGetSigningCommitments/non-existent_node_ID` case (which codified the buggy behavior) to assert the new `NotFound` response --- Contributes to SP-3154 created with claude session fb9e54f1-6a3a-4b69-95d6-debb37735543 --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> GitOrigin-RevId: 794fb53443bcf03888444a72d53e6c5bdf8fa7dd
## Summary Adds CAS for StrorePreimage, updating the PreimageRequest with preimage if status=WaitingForPreimage, returning idempotently if status=PreimageShared or returning an error otherwise. ## Test Plan - Added `TestStorePreimage_RejectsConcurrentReturnedWriteback` in `lightning_preimage_exploit_test.go`: 1. Creates a `PreimageRequest` in `WAITING_FOR_PREIMAGE`. 2. Simulates a racing cancel by directly setting the row's status to `RETURNED` while keeping the in-memory entity stale. 3. Calls `StorePreimage` with the stale entity and asserts an error is returned, the row stays `RETURNED`, and its `preimage` column is not overwritten by the racing write. - Verified the regression test fails against the pre-fix code (no error returned; preimage persisted) and passes after the fix. - `go test -tags lightspark ./so/handler/...` — green. - `mise lint` — 0 issues. [SP-3105]: https://lightspark.atlassian.net/browse/SP-3105?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> GitOrigin-RevId: d036cafb7e73fc94e7af9accf9f4c1121e3c5422
…af validation (#6486) ## Summary Security: renew leaf validation now has explicit test coverage that a `refundTimelock` below `TimeLockInterval` (100 blocks) is rejected on both the regular and zero-timelock renewal paths, preventing a previous owner from racing against a recipient with zero CSV separation. GitOrigin-RevId: dbb7f4e2f765e4424138ea08f2b6d2f86cbf8c9c
…7319) ## Summary Legacy `queryTransfers` filters status on `transfers.status` (parent axis). For multi-receiver MIMO transfers an individual `transfer_receivers.status` can diverge — a receiver querying `[COMPLETED]` while the parent lags silently loses their own row. This adds an Ent-based by-participant fallback handler as the final routing step in `QueryAllTransfers` for participant-bearing shapes. Receiver-arm filters apply to `transfer_receivers.status` via the existing translation in `mimo.ReceiverArmFilters`; sender and SR1 arms compose naturally. Nil-participant traffic stays on legacy, which already has the per-transfer access-check pass. Gated by `KnobReadMIMODataModelByParticipantFallback` (0 prod, 100 Tiltfile). The legacy path has multi-second outliers today on participant-bearing shapes the fallback will claim. `KnobLogTransferQueryInvocationsSlowMs` (ms threshold, default 0) makes `logQueryTransfersInvocation` emit unconditionally when `elapsed >= threshold` (`slow_bypass=true`) — diagnostic surface for slow callers without cranking the per-call sampling knob to 100. Applies across all 7 transfer-query handlers. ## Test Plan - Equivalence cases (12 shapes) confirm fallback matches legacy on single-receiver fixtures: bare sender/receiver/SR1, sender + statuses, SR1 + statuses, pagination, time bounds, ascending order, negative-pagination / network-unset / invalid-status rejection - `TestQueryAllTransfers_ByParticipantFallback_PerReceiverDivergence` constructs a multi-receiver MIMO transfer where one receiver is `COMPLETED` and the parent lags — fallback returns the row, legacy drops it --- Contributes to SP-3135 created with claude session b53e1f4d-94ac-4294-b860-d86c6874657a --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> GitOrigin-RevId: 43520b7cd2fffee58d179e182bdff9491e958345
## Summary Update the maximum runtime for the ephemeral database backfill to 2 minutes. Currently, it keeps trying to backfill for up to 50s. If the last batch of backfill rows pushes the task time over a minute, that batch is lost (not permanently; it is retried on the next run) and will need to be redone in the next job. This is not a breaking problem, but it does cause alert noise, so here we'll double the time "budget" to 2 minutes to let the last batch finish before the job cancels it. ## Test Plan It builds / passes tests GitOrigin-RevId: 48b476ca42ad52f9c5ef830c9485197acc7da861
## Summary Fixes a wedged-RKT recovery hole in `claim_transfer`'s 2PC. Once `persistCoordinatorClaimKeyTweak` has committed `leaf.KeyTweak` on the coordinator (status `RECEIVER_KEY_TWEAKED`), a retry with a fresh polynomial no longer silently overwrites the anchored proofs. Instead, the retry drives the 2PC with the stored proofs and either heals to a consistent commit (peers match) or rolls the cluster back to `SENDER_KEY_TWEAKED` (peers diverge or never committed Phase 1). The next user retry can then install a fresh polynomial cleanly. Two changes to `ClaimTransfer` / `persistCoordinatorClaimKeyTweak`: 1. Add `ReceiverKeyTweaked` (and the MIMO `TransferReceiverStatusKeyTweaked`) to the `useStoredKeyTweaks=true` set so a retry at RKT reuses anchored proofs and skips forwarding the SDK's fresh encrypted package to peers. 2. Restore the `len(leaf.KeyTweak) == 0` guard in `persistCoordinatorClaimKeyTweak` so it never overwrites stored proofs. The PR #7280 regression test that asserted the prior override behavior is replaced with one that drives the full stranded-RKT → rollback → retry-succeeds recovery through `wallet.ClaimTransferV2` and asserts cluster keyshare consistency at the end. The companion `…RejectedWhenPeerLockedAtRKL` test is unchanged. ## Test Plan - [x] `golangci-lint run ./so/handler/ ./so/grpc_test/` clean. - [x] `go test ./so/handler/` green (both `-tags=lightspark` and OSS). - [ ] `go test ./so/grpc_test -run 'TestClaimTransferV2_SettleAtomicity|TestClaimTransferV2_StrandedRKT|TestClaimTransferV2_FreshPolynomialRejected'` against a 5-SO local cluster. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> GitOrigin-RevId: 31edcd9943a26ba64218c4e500f314e90bf48cec
## Summary Fixes a nil-pointer panic in the token signing path on `SparkTokenInternalService/exchange_revocation_secrets_shares`. Two call sites in `so/handler/tokens/internal_sign_token_handler.go` were reading `RevocationKeyshare.SecretShare` directly off the ent entity instead of going through `SigningKeyshare.GetSecretShare(ctx)`: 1. `getSecretSharesNotInInputFromSpentOutputs` — dereferenced the field with no nil check, panicking when `secret_share` was NULL on the main DB row. 2. `buildOperatorPubkeyToRevocationSecretShareMap` — nil-checked the field but returned an error instead of falling back to the ephemeral store, so the RPC would have failed (no panic) for the same rows. Since #5376 made `secret_share` `Optional().Nillable()` and the SP-3127 ephemeral-DB backfill began moving secrets out of the main DB, the hydration helpers (`ent.HydrateSigningKeyshareSecrets` + `SigningKeyshare.GetSecretShare(ctx)`) are the only correct way to resolve a keyshare's secret — direct field access only works on the shrinking subset of rows that still have a populated `secret_share` column (or while `KnobSoSigningKeyshareDualWriteSecret` is at 100%). Both call sites in this file already use `GetSecretShare(ctx)` correctly (see `canRecoverAndFinalizeTransaction` and `recoverFullRevocationSecrets`); these two were missed. ### Changes - `getSecretSharesNotInInputFromSpentOutputs`, `prepareResponseForExchangeRevocationSecretsShare`, and `buildOperatorPubkeyToRevocationSecretShareMap` now take a `ctx context.Context`; ctx is threaded from the existing `exchangeTransferRevocationSecrets` and `getSecretSharesNotInInput` callers. - Both functions batch-hydrate `RevocationKeyshare` secrets via a new `hydrateRevocationKeyshareSecrets` helper before the per-output loop, so the subsequent `GetSecretShare(ctx)` calls hit the cache rather than issuing N round-trips to the ephemeral DB. - `TokenPartialRevocationSecretShare.SecretShare` accesses are unchanged — that field is a value-type `keys.Private`, not Optional/Nillable, and can't exhibit the same bug. ## Test Plan - `go build ./so/handler/tokens/...` - `go test ./so/handler/tokens/...` — passes - `golangci-lint run ./so/handler/tokens/...` — 0 issues Existing unit tests cover the populated-`secret_share` happy path through `getSecretSharesNotInInput`. Pre-merge follow-up: add a postgres test that creates a `SigningKeyshare` with NULL `secret_share` and a populated `secret_version` (matching loadtest's post-backfill shape) and exercises `exchange_revocation_secrets_shares` end-to-end to guard against this regressing. GitOrigin-RevId: a8ea1d77ea2003069bec91bf4b05a5be2a9efc5e
## Summary Add instructions to code agents on how to load signing keyshare secrets when the ephemeral database is active. ## Test Plan Builds; this is doc-only GitOrigin-RevId: fea1be8060af46b56564b9f3b5f511407f1d3b47
## Summary Adds the published `@lightsparkdev/eslint-config` React app preset to the Spark Vite example so it participates in workspace linting. The Vite template had its app TypeScript config in `tsconfig.app.json`, but the shared base lint config expects source files to be covered by package-level `tsconfig.json`. This PR moves the app compiler options into `tsconfig.json`, deletes `tsconfig.app.json`, and keeps the build script typechecking both `tsconfig.json` and `tsconfig.node.json`. Fixes the lint findings surfaced in the app source by typing the debug window hook globally, formatting unknown errors explicitly, and marking async UI callbacks as intentionally fire-and-forget. ## Test Plan - `yarn workspace @buildonspark/spark-vite-app lint` - `yarn workspace @buildonspark/spark-vite-app format` - `yarn workspace @buildonspark/spark-vite-app build` - `yarn turbo run lint --filter @buildonspark/spark-vite-app` GitOrigin-RevId: a0cb6ff2a32c7142740ab777e51d2cea3590a4e8
…s (#7339)
## Summary
The OpenTelemetry SDK ships gRPC duration histograms with default boundaries that
top out at **10s**. Any unary handler that legitimately runs longer than 10s
(`dkg.DKGService/start_dkg`, large gossip batches, slow client RPCs) falls into
the `+Inf` bucket — `histogram_quantile` returns `+Inf` and the latency panels
flatline at the precise moment they are most useful.
This shows up in two existing panels on the Spark dashboard:
- **DKG Service Latency p95** (`rpc_server_duration_milliseconds`)
- **DKG Latency p95** (`rpc_client_duration_milliseconds`)
Both saturate during recent `spark-dkg-failures` incidents on `spark-dkg-0`,
where protocol phases consistently take ~46s, and the recent bump of the
`spark.so.grpc.server.unary_handler_timeout` and `spark.so.grpc.client.timeout`
knobs to 180s for `/dkg.DKGService/start_dkg` will keep many invocations in the
60–180s range.
### Change
Register two OpenTelemetry `MeterProvider` views — one for `rpc.server.duration`
and one for `rpc.client.duration` — that override the histogram boundaries with
an extended set. The new boundaries:
```
0, 5, 10, 25, 50, 75, 100, 250, 500, 750, 1000,
2500, 5000, 7500, 10000, 15000, 30000, 60000, 90000, 120000, 180000
```
This preserves every existing boundary (so sub-100ms detail for fast RPCs is
unchanged) and adds five new high-end buckets at 15s, 30s, 60s, 90s, 120s, 180s.
The 180000 boundary matches the new handler/client timeout exactly, so timeouts
will count there rather than `+Inf`.
Cardinality impact is small: ~6 extra bucket series per existing histogram
labelset (rpc_service × rpc_method × pod × workspace), well under VictoriaMetrics
budget.
### Notes
- No behaviour change beyond histogram bucket layout. Existing alerts that
threshold on p95/p99 will start returning real numbers instead of `+Inf` for
long-tail RPCs.
- Histogram bucket changes are not retroactive — existing series for the old
boundaries will continue to be written by older pods until they roll, and
Prometheus will accumulate new labelsets with the extended `le` values from
the moment this rolls out. p95/p99 over the rollout window will look noisy
for a few minutes while both labelsets are live.
## Test Plan
- [x] `go build ./bin/operator/...` clean
- [x] `mise lint` clean (0 issues, no ent field removal violations)
- [ ] After deploy, confirm `count(group by (le) (rpc_server_duration_milliseconds_bucket{rpc_service="dkg.DKGService"}))` returns 22 boundaries (was 16) and that 'DKG Service Latency p95' renders an actual value during a slow run instead of `+Inf`.
GitOrigin-RevId: 2f2d574f712b7cd2362668fcf8460eee6335ba48
## Summary Adds the published `@lightsparkdev/eslint-config` base config to `@buildonspark/spark-frost-bare-addon` so the package participates in workspace linting. This package is intentionally CommonJS/Bare runtime code, so the local ESLint config keeps the base rules but disables `@typescript-eslint/no-require-imports` for JS files. The lint fixes remove unused test bindings and an unused catch binding. ## Test Plan - `yarn workspace @buildonspark/spark-frost-bare-addon lint` - `yarn workspace @buildonspark/spark-frost-bare-addon format` - `yarn workspace @buildonspark/spark-frost-bare-addon test` - `yarn workspace @buildonspark/spark-frost-bare-addon package:checks` - `yarn turbo run lint --filter @buildonspark/spark-frost-bare-addon` --------- Co-authored-by: Lightspark Eng <engineering@lightspark.com> GitOrigin-RevId: 4aff4ccb3c04cf91340c80cb9cd8d15f7dbea15a
Sync the SDK values for `LightningReceiveRequestStatus` with the values from GraphQL definition file.
Author
|
Any chance this gets merged? Working with Hodl LN invoices without these status values is guess work instead of a clear process. |
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.
Sync the SDK values for
LightningReceiveRequestStatuswith the values from GraphQL definition file.