Skip to content

bolt12: add Invoice codec and structural validators#10941

Open
bitromortac wants to merge 5 commits into
lightningnetwork:masterfrom
bitromortac:2604-bolt12-1c
Open

bolt12: add Invoice codec and structural validators#10941
bitromortac wants to merge 5 commits into
lightningnetwork:masterfrom
bitromortac:2604-bolt12-1c

Conversation

@bitromortac

Copy link
Copy Markdown
Collaborator

Based on #10832, part of #10736.

Adds the BOLT 12 Invoice message struct with PureTLVMessage-based Encode/Decode, alongside the structural ValidateInvoiceRead/Write and cross-message ValidateInvoiceAgainstRequest validators.

Additionally, this PR injects known feature-bit catalogues into the Offer and InvoiceRequest read-side validators to enable must-understand capability checks at runtime, because features are defined outside of bolt12's boundaries.

@github-actions github-actions Bot added the severity-critical Requires expert review - security/consensus critical label Jun 30, 2026
@github-actions

Copy link
Copy Markdown

PR Severity: CRITICAL

Highest-severity file determines label | 9 non-test/non-generated files | ~2,317 lines changed

Critical (1 file)
  • rpcserver.go - Core server coordination (explicitly listed as CRITICAL)
Medium (7 files)
  • bolt12/invoice.go - New BOLT 12 invoice type (uncategorized package, medium by default)
  • bolt12/invoice_request.go - New BOLT 12 invoice request type
  • bolt12/subtypes.go - BOLT 12 subtype definitions
  • bolt12/tlv_types.go - BOLT 12 TLV type constants
  • bolt12/validate.go - BOLT 12 validation logic (large: +1247/-11)
  • lnrpc/lightning.proto - API definition change (.proto files are medium)
  • lnrpc/lightning.swagger.json - Swagger API spec update
Low (1 file)
  • docs/release-notes/release-notes-0.22.0.md - Documentation
Excluded from counting (5 files)
  • bolt12/invoice_request_test.go - test file
  • bolt12/invoice_test.go - test file
  • bolt12/subtypes_test.go - test file
  • bolt12/validate_test.go - test file
  • lnrpc/lightning.pb.go - auto-generated protobuf file

Analysis

The critical classification is driven by changes to rpcserver.go (+30/-19 lines), which is the core RPC server and explicitly listed as a CRITICAL file requiring expert review.

The bulk of this PR (~2,300 non-test lines) is new BOLT 12 invoice parsing/validation logic in the bolt12 package. The BOLT 12 code adds new invoice/offer support without touching existing wallet, channel, or HTLC state machine code.

The rpcserver.go change wires the new BOLT 12 parsing into existing RPC handlers. Reviewers should pay particular attention to how new BOLT 12 paths interact with existing payment flows and whether error paths in the new validation code are handled safely before reaching the RPC layer.


To override, add a severity-override-{critical,high,medium,low} label.
<!-- pr-severity-bot -->

@bitromortac bitromortac force-pushed the 2604-bolt12-1c branch 2 times, most recently from 03cc142 to 2ab1ae2 Compare June 30, 2026 17:53
@github-actions github-actions Bot added severity-high Requires knowledgeable engineer review and removed severity-critical Requires expert review - security/consensus critical labels Jun 30, 2026
@github-actions

Copy link
Copy Markdown

⚠️ Severity changed: severity-criticalseverity-high (files changed since last classification)

🟠 PR Severity: HIGH

Automated classification | 5 non-test files | 1,278 non-test lines changed

🟡 Medium (4 files)
  • bolt12/invoice.go — BOLT 12 invoice types/structures (similar to zpay32/*, not in CRITICAL/HIGH tiers)
  • bolt12/subtypes.go — BOLT 12 subtype definitions
  • bolt12/tlv_types.go — TLV type definitions for BOLT 12
  • bolt12/validate.go — BOLT 12 invoice/offer validation logic
🟢 Low (1 file)
  • docs/release-notes/release-notes-0.22.0.md — Release notes documentation

Analysis

The changed files are all in the bolt12/ package, which handles BOLT 12 offer/invoice protocol definitions and validation. This is analogous to zpay32/ (BOLT 11 invoices), which falls under MEDIUM. No files touch CRITICAL-tier packages (lnwallet/, htlcswitch/, channeldb/, lnwire/, etc.) or HIGH-tier packages (routing/, invoices/, lnrpc/, etc.).

Severity bump applied: 1,278 non-test lines changed exceeds the 500-line threshold, bumping the classification from MEDIUM → HIGH.

Test files excluded from counting: bolt12/invoice_test.go (286 lines), bolt12/subtypes_test.go (223 lines), bolt12/validate_test.go (864 lines).


To override, add a severity-override-{critical,high,medium,low} label.
<!-- pr-severity-bot -->

@bitromortac

Copy link
Copy Markdown
Collaborator Author

/gateway review

@lightninglabs-gateway

lightninglabs-gateway Bot commented Jul 1, 2026

Copy link
Copy Markdown

✅ Review posted: #10941 (review)

6 finding(s); 6 inline, 0 in body.

🔁 Need a re-review after pushing changes? Reply with /gateway re-review.
Maintainers can also /gateway dismiss <id> to silence specific findings, or anyone can /gateway explain <id> for elaboration.

@lightninglabs-gateway lightninglabs-gateway Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR adds the BOLT 12 Invoice message, its blinded_payinfo/fallback_address subtype codecs, a truncated-uint32 TLV type, and the structural reader/writer/cross-message validators, and threads a runtime feature-bit catalogue into the offer and invoice_request readers. The codec work is careful and unusually well-tested: round-trip bijection, truncation rejection, unknown-field mirroring, and every reader/writer rejection path are all pinned by table tests, and the deferred concerns (signing, expiry clock, on-chain fallback rules) are documented inline.

The concerns worth resolving before merge are not in the new codec math but at its edges. First, the feature-catalogue refactor is a breaking signature change to three already-shipped validators, and this diff contains no non-test caller updates even though rpcserver.go was dropped from the PR since the last severity classification — confirm every caller compiles. Second, the same refactor silently removes the write-side unknown-even-feature-bit rejection that landed three weeks ago in #10832. Third, ValidateInvoiceRead authenticates nothing cryptographically yet; that is documented and intentional, but the identity binding it does perform has a gap when offer_issuer_id is absent, so callers must not mistake a passing invoice for an authentic one.

Findings: 🔴 0 Blocker · 🟠 3 Major · 🟡 3 Minor · 🔵 0 Nit

Comment thread bolt12/validate.go
return err
}

// check UTF-8 constraints and BIP 353

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed feature bit validation from writer validation as otherwise we'd need to pass in known features. I think it should be ok to rely on ourselves to set feature bits correctly.

Comment thread bolt12/validate.go
Comment thread bolt12/validate.go
// - MUST reject the invoice if this leaves no usable paths.
var usablePaths int
for i := range bp.Infos {
fv := bp.Infos[i].Features

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have added the docstring and signature validation will come later, in addition to checking against the blinded path.

Comment thread bolt12/subtypes.go
// BlindedPayInfos holds a list of BlindedPayInfo entries for the
// invoice_blindedpay field.
type BlindedPayInfos struct {
Infos []BlindedPayInfo

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a known issue as documented. I favored using the internal data structure for tlv mechanics over keeping the byte-wise representation. Added an error path to detect this explicitly.

Comment thread bolt12/subtypes.go
Comment thread bolt12/validate.go
return err
}

// - MUST reject the invoice if signature is not a valid signature using

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, documented explicitly. Imo validation should only read and not modify in this case, so this has to be checked by the pathfinding system later.

@lightninglabs-gateway

lightninglabs-gateway Bot commented Jul 1, 2026

Copy link
Copy Markdown

🤖 gateway audit metadata for this PR — auto-generated, please don't edit.

Add the truncated uint32 (tu32) TLV type used by invoice_relative_expiry
and the dynamic invoice subtypes BlindedPayInfo and FallbackAddress,
along with their encode/decode helpers and round-trip tests.

These primitives are the building blocks for the BOLT 12 Invoice message
struct that follows. Isolating them keeps that codec commit focused on
the message shape rather than its component records.
…alidators

Inject known feature-bit catalogues into the read-side validators to enable
correct must-understand capability checks, and remove write-side feature
enforcement entirely.

Whether a feature bit is "unknown" is a runtime property of the reading node,
not of the wire format or pure codec.
Add the BOLT 12 Invoice message: a struct mirroring the invoice_request
fields (types 0-91) plus the invoice-specific fields (types 160-176) and
the signature (type 240), together with its pure-TLV Encode/DecodeInvoice
codec and the UsableFallbackAddresses accessor that applies the spec's
MUST-ignore filter.

Additionally, add the NewInvoiceFromRequest constructor to build an Invoice
from a corresponding request. This copies all non-signature fields from the
request (including unknown signed-range TLVs via the decodedTLVs sidecar)
and mirrors invreq_amount into invoice_amount.
Implement the structural validators for the BOLT 12 invoice, adding
ValidateInvoiceWrite, ValidateInvoiceRead, ValidateInvoiceExpiry, and
ValidateInvoiceAgainstRequest.

The validators implement the spec writer and reader requirements in the
order the spec lists them. The reader confirms the signature TLV is
present but defers actual Schnorr verification until the merkle and
signing primitives land, mirroring the ValidateInvoiceRequestRead
precedent.
Add release notes for the BOLT 12 invoice codec.
Comment thread bolt12/validate.go
return err
}

// check UTF-8 constraints and BIP 353

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed feature bit validation from writer validation as otherwise we'd need to pass in known features. I think it should be ok to rely on ourselves to set feature bits correctly.

Comment thread bolt12/validate.go
// - MUST reject the invoice if this leaves no usable paths.
var usablePaths int
for i := range bp.Infos {
fv := bp.Infos[i].Features

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have added the docstring and signature validation will come later, in addition to checking against the blinded path.

Comment thread bolt12/subtypes.go
// BlindedPayInfos holds a list of BlindedPayInfo entries for the
// invoice_blindedpay field.
type BlindedPayInfos struct {
Infos []BlindedPayInfo

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a known issue as documented. I favored using the internal data structure for tlv mechanics over keeping the byte-wise representation. Added an error path to detect this explicitly.

Comment thread bolt12/validate.go
return err
}

// - MUST reject the invoice if signature is not a valid signature using

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, documented explicitly. Imo validation should only read and not modify in this case, so this has to be checked by the pathfinding system later.

Comment thread bolt12/subtypes.go
Comment on lines +133 to +135
// WARNING: RawFeatureVector re-encodes to minimal length, so setting
// non-minimal feature bytes (trailing zeros) yields different wire
// bytes than were read and invalidates the invoice signature.

@erickcestari erickcestari Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it would be worth a change in the spec to only allow minimal encoded features in all lightning types.

This would be similar to what is being done with channel_announcement and node_announcement:
lightning/bolts#1341 (review)

@saubyk saubyk added this to lnd v0.22 Jul 2, 2026
@github-project-automation github-project-automation Bot moved this to Backlog in lnd v0.22 Jul 2, 2026
@saubyk saubyk added this to the v0.22.0 milestone Jul 2, 2026
@saubyk saubyk moved this from Backlog to In progress in lnd v0.22 Jul 2, 2026

@erickcestari erickcestari left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Nice work!🏅

There are only some nits that are non-blocking from my side.

RPC response rather than being emitted as an empty struct.

* [BOLT 12 invoice
codec](https://github.com/lightningnetwork/lnd/pull/10999): add the

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
codec](https://github.com/lightningnetwork/lnd/pull/10999): add the
codec](https://github.com/lightningnetwork/lnd/pull/10941): add the

Comment thread bolt12/invoice.go

// InvoiceNodeID is the public key of the recipient node, used to verify
// the signature.
InvoiceNodeID tlv.OptionalRecordT[tlv.TlvType176, [33]byte]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
InvoiceNodeID tlv.OptionalRecordT[tlv.TlvType176, [33]byte]
InvoiceNodeID tlv.OptionalRecordT[tlv.TlvType176, *btcec.PublicKey]

Comment thread bolt12/validate.go
Comment on lines +1606 to +1625
// - For each invoice_blindedpay.payinfo:
// - MUST NOT use the corresponding invoice_paths.path if
// payinfo.features has any unknown even bits set.
// - MUST reject the invoice if this leaves no usable paths.
// NOTE: This loop only counts usable paths to ensure at least one
// exists. Callers MUST re-apply the same knownBlindedFeatures filter
// when selecting a path downstream to avoid using an unusable path,
// as the unfiltered list is returned.
var usablePaths int
for i := range bp.Infos {
fv := bp.Infos[i].Features
wrapped := lnwire.NewFeatureVector(&fv, knownBlindedFeatures)
if len(wrapped.UnknownRequiredFeatures()) == 0 {
usablePaths++
}
}

if usablePaths == 0 {
return ErrNoUsablePaths
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: We could have a similar method of UsableFallbackAddresses, but for the BlindedPaths fields.

// UsablePath pairs a blinded path with its payment parameters, as returned by
// UsablePaths after the BOLT 12 reader's feature filter has been applied.
type UsablePath struct {
	// Path is the blinded path to the recipient.
	Path lnwire.BlindedPath

	// PayInfo is the blinded_payinfo for Path.
	PayInfo BlindedPayInfo
}

// UsablePaths returns the invoice_paths entries a payer may use, each paired
// with its blinded_payinfo, after applying the BOLT 12 reader rule that a path
// MUST NOT be used when its payinfo.features has unknown required (even) bits
// set. knownBlindedFeatures names the feature bits the reader understands.
//
// The result is empty when invoice_paths or invoice_blindedpay is absent, or
// when the two lists differ in length; ValidateInvoiceRead rejects those cases
// separately, so a caller that validates first can treat an empty result as
// "no usable paths".
func (inv *Invoice) UsablePaths(
	knownBlindedFeatures map[lnwire.FeatureBit]string) []UsablePath {

	paths := inv.InvoicePaths.ValOpt().UnwrapOr(lnwire.BlindedPaths{})
	bp := inv.InvoiceBlindedPay.ValOpt().UnwrapOr(BlindedPayInfos{})

	// Entries pair by index; a length mismatch is rejected upstream by
	// ValidateInvoiceRead, so guard here to stay in bounds.
	if len(paths.Paths) != len(bp.Infos) {
		return nil
	}

	var usable []UsablePath
	for i := range bp.Infos {
		// MUST NOT use the path if payinfo.features has any unknown even
		// bits set.
		fv := bp.Infos[i].Features
		wrapped := lnwire.NewFeatureVector(&fv, knownBlindedFeatures)
		if len(wrapped.UnknownRequiredFeatures()) > 0 {
			continue
		}

		usable = append(usable, UsablePath{
			Path:    paths.Paths[i],
			PayInfo: bp.Infos[i],
		})
	}

	return usable
}

Then:

Suggested change
// - For each invoice_blindedpay.payinfo:
// - MUST NOT use the corresponding invoice_paths.path if
// payinfo.features has any unknown even bits set.
// - MUST reject the invoice if this leaves no usable paths.
// NOTE: This loop only counts usable paths to ensure at least one
// exists. Callers MUST re-apply the same knownBlindedFeatures filter
// when selecting a path downstream to avoid using an unusable path,
// as the unfiltered list is returned.
var usablePaths int
for i := range bp.Infos {
fv := bp.Infos[i].Features
wrapped := lnwire.NewFeatureVector(&fv, knownBlindedFeatures)
if len(wrapped.UnknownRequiredFeatures()) == 0 {
usablePaths++
}
}
if usablePaths == 0 {
return ErrNoUsablePaths
}
// - For each invoice_blindedpay.payinfo:
// - MUST NOT use the corresponding invoice_paths.path if
// payinfo.features has any unknown even bits set.
// - MUST reject the invoice if this leaves no usable paths.
// UsablePaths applies that filter; a caller selecting a path downstream
// should use it rather than the unfiltered invoice_paths.
if len(inv.UsablePaths(knownBlindedFeatures)) == 0 {
return ErrNoUsablePaths
}

@saubyk

saubyk commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

/gateway dismiss F1 per #10941 (comment)

@lightninglabs-gateway

Copy link
Copy Markdown

🚫 Dismissed F1 (major) by @saubyk — per #10941 (comment)

Open findings on this PR: 🟠 F2 (major) · 🟠 F3 (major) · 🟡 F4 (minor) · 🟡 F5 (minor) · 🟡 F6 (minor)

@saubyk

saubyk commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

/gateway dismiss F6 per #10941 (comment)

@lightninglabs-gateway

Copy link
Copy Markdown

🚫 Dismissed F6 (minor) by @saubyk — per #10941 (comment)

Open findings on this PR: 🟠 F2 (major) · 🟠 F3 (major) · 🟡 F4 (minor) · 🟡 F5 (minor)

@saubyk

saubyk commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

/gateway dismiss F3 per #10941 (comment)

@lightninglabs-gateway

Copy link
Copy Markdown

🚫 Dismissed F3 (major) by @saubyk — per #10941 (comment)

Open findings on this PR: 🟠 F2 (major) · 🟡 F4 (minor) · 🟡 F5 (minor)

@saubyk

saubyk commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

/gateway dismiss F4 per #10941 (comment)

@lightninglabs-gateway

Copy link
Copy Markdown

🚫 Dismissed F4 (minor) by @saubyk — per #10941 (comment)

Open findings on this PR: 🟠 F2 (major) · 🟡 F5 (minor)

@saubyk

saubyk commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

/gateway re-review

@lightninglabs-gateway

lightninglabs-gateway Bot commented Jul 4, 2026

Copy link
Copy Markdown

✅ Re-review posted: #10941 (review)

Prior findings: 1 addressed, 1 still unresolved, 0 withdrawn.
New findings: 3.
Total inline: 4; in body: 0.

🔁 Need another re-review after pushing changes? Reply with /gateway re-review.
Maintainers can also /gateway dismiss <id> to silence specific findings, or anyone can /gateway explain <id> for elaboration.

@lightninglabs-gateway lightninglabs-gateway Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The maintainer dismissed F1, F3, F4, and F6, and this revision resolved F5 by adding the entry-count caps to both list decoders. F2 remains open: the breaking arity change to ValidateOfferRead/ValidateInvoiceRequestRead/checkFeatures still carries no in-diff caller update, and independent change-impact analysis reconfirms the build-break risk cannot be cleared from the reach index alone. Three new, low-severity concerns surfaced in the invoice validators — an expiry boundary off-by-one, an unbounded invoice_amount for fixed-amount offers, and a feature-map parameter-transposition hazard.

Findings: 🔴 0 Blocker · 🟠 1 Major · 🟡 3 Minor · 🔵 0 Nit


Status of prior findings

  • F5 addressed: Fixed in bolt12/subtypes.godecodeBlindedPayInfos and decodeFallbackAddrs now cap entries at maxBlindedPayInfos/maxFallbackAddrs (32) and return ErrTooManyBlindedPayInfos/ErrTooManyFallbackAddrs, matching the sibling decodeChainsRecord bound, with "exceeds cap" test coverage added for both.

Comment thread bolt12/validate.go

return nil
}); err != nil {
return err

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Minor F7: Invoice expiry rejects one second early vs BOLT 12

ValidateInvoiceExpiry treats the invoice as expired when carry == 0 && uint64(now.Unix()) >= expiry, where expiry = invoice_created_at + invoice_relative_expiry. BOLT 12's reader rule rejects only when the current time is greater than created_at + seconds_from_creation, so >= expires the invoice at the exact boundary second — one second early — rejecting a payment the spec still considers valid. This is also internally inconsistent with ValidateOfferRead, which uses strict > (uint64(now.Unix()) > expiry) for offer_absolute_expiry. Change the comparator to >.

Comment thread bolt12/validate.go
// Spec MUST: if invreq_amount (type 82) is present, invoice_amount
// (type 170) must equal it. The byte-mirror loop cannot relate fields
// with differing type numbers.
return fn.MapOptionZ(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Minor F8: invoice_amount unbounded to offer_amount when invreq_amount absent

ValidateInvoiceAgainstRequest's only amount cross-check is req.InvreqAmount present → inv.InvoiceAmount must equal it. When the payer relied on a fixed offer_amount and set no invreq_amount, invoice_amount is left unconstrained: the byte-mirror excludes it (type 170 is in the invoice-specific 160-176 range, outside invreqAllowedRange), and the WhenSome cross-check never fires. A payee can then return an invoice charging more than the offer advertised and this validator accepts it. BOLT 12 makes this a SHOULD ("confirm invoice_amount.msat is within the authorized range"), and the currency case genuinely needs a caller-supplied exchange rate — but the native fixed-amount case is offer_amount × invreq_quantity, computable here and already done for the request side in checkInvreqAmountMeetsOffer. Either add that comparison or state in this function's contract that the offer-amount bound is delegated.

Comment thread bolt12/validate.go
}

// - if invoice_features contains unknown odd bits that are non-zero:
// - MUST ignore the bit.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Minor F9: Adjacent identical-type feature maps invite a silent swap

ValidateInvoiceRead(inv, activeChain, knownFeatures, knownBlindedFeatures map[lnwire.FeatureBit]string) places two parameters of identical type adjacent in the signature. ValidateInvoiceRead(inv, chain, knownBlindedFeatures, knownFeatures) — transposed — compiles cleanly and validates invoice_features against the blinded-path catalogue and vice versa, either spuriously rejecting valid invoices or accepting required bits that should have been rejected. A single options struct with named fields makes the two sets impossible to swap positionally.

Comment thread bolt12/validate.go
// caller wiring this into a handler MUST verify the signature itself.
func ValidateInvoiceRequestRead(ir *InvoiceRequest,
activeChain [32]byte) error {
activeChain [32]byte,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 Major F2 · unresolved

Still unresolved on the current head. ValidateOfferRead, ValidateInvoiceRequestRead, and checkFeatures retain the added required knownFeatures map[lnwire.FeatureBit]string parameter — a breaking signature change to code that landed weeks ago (#10789/#10832, per blame). The current diff contains no non-test caller update, and the callers[] "low" reach bucket does not clear this: that index is built from the default branch and cannot see this PR series' own callers. Two signals still point at real blast radius: rpcserver.go (+30/-19) rode an earlier push of this PR and was then dropped from the file set, and no out-of-package call site appears in the diff. The author states callers were updated, but that is not evidenced here. Before merge, confirm every out-of-package caller compiles against the new arity — land the caller updates in this PR or cite the merged PR that carries them.

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

Labels

gateway-active severity-high Requires knowledgeable engineer review

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

3 participants