Go SDK 8/8: examples, README, manual chapter, release notes - #519
Open
bkeroack wants to merge 1 commit into
Open
Go SDK 8/8: examples, README, manual chapter, release notes#519bkeroack wants to merge 1 commit into
bkeroack wants to merge 1 commit into
Conversation
The last PR of the Go SDK stack. Thirteen runnable examples, a real README, a manual chapter, changelog and release-note entries, and cross-links from the wire spec. Examples are their own module. Two of them (sp_wallet, sp_light_scan) do BIP 352 receiver derivation, which needs real secp256k1 scalar and point arithmetic — and a dependency declared in the SDK's go.mod reaches every consumer's module graph whether they import the package or not. A nested module keeps btcec where it belongs, the same reason `tools` and `e2e` are separate. The alternative, hand-rolling curve math with math/big in an example, would have been worse than the dependency: an example teaches, and that one would teach the wrong thing. The BIP 352 derivation is shared between those two examples rather than copied, because the label arm is exactly what integrators get wrong — omit the label tweak and you derive a key that looks fine and does not control the output, so change silently becomes unspendable. One reviewed implementation with tests beats two copy-pasteable ones. Those tests play the SENDER, deriving the same output key by a genuinely different route: the shared secret from the other side of the ECDH (a·B_scan rather than b_scan·T), and the output key by point addition rather than the scalar shortcut the receiver takes. A test that re-derived the receiver's way would agree with any bug it had. Ten perturbations, ten caught — including the first attempt at this file, which computed the label tweak by calling the function under test and so could not see that function keyed on the wrong secret. The test now restates BIP 352 §5 directly. What these tests do NOT establish is stated in the file: they are self-consistency and algebra checks, not BIP 352 known-answer vectors. The authoritative check that this agrees with the deployed protocol is the node's own silent-payment index matching real payments, which the E2E suite covers. deposit_notify is the README quickstart, verbatim and compiled. The most-copied code in the repository is the code that must not rot, and nothing was compiling it. Also in this PR: - lint.sh now runs vet/staticcheck/errcheck in every module. The analyzers resolve ./... against the main module and stop at a nested go.mod, so `examples` and `e2e` were silently unlinted — e2e since it landed. Both are clean; e2e needs its build tag or the analyzers report a clean run over nothing at all. - E2E deadlines: positive waits widened 30s → 60s, and a fired-alarm prune poll 10s → 45s. These return the instant their condition holds, so their deadline is paid only when the test was going to fail anyway — generous costs nothing and buys headroom on a loaded runner. The negative windows (collect, and the context deadline proving nothing re-fires) are deliberately left alone, and harness_test.go now says why: those are paid in full on every run, since "nothing happened" can only be established by waiting. Widening one slows every green run to buy confidence in a claim already anchored by the positive wait ahead of it. I had planned to widen "the tight 30s/60s deadlines" wholesale; reading them showed the two kinds want opposite treatment. Parity inventory (plan §3), walked item by item: - §3.1 connection/transport: Dial + WithBearerToken / WithTLS / WithTLSCAPem / WithMTLS / WithTLSServerName / WithKeepalive / WithoutKeepalive / WithGRPCDialOption; Category* consts; MaxSPLabelsPerTarget, MaxSPTargetsPerConnection. Complete. - §3.2 streams and watch control: Subscribe/Stream.Recv, Watch → (*WatchHandle, *Stream), all eight watch kinds with Add/Remove, plus SetCategories, SetWatchOptions, SetCursor, SetWatchSet, Rescan, SendControl; SilentPaymentTarget.Validate/ScanPubkey; Stream.Cursor. Complete. - §3.3 typed events: 31 concrete types, not the 30 the plan lists — the plan's enumeration omitted PrefixMatched. The count is not what guarantees completeness; the protobuf-reflection test walking the NodeEvent oneof descriptor is. All seven enums carry Known(). - §3.4 resilience: CursorStore/FileCursorStore/NoopCursorStore, Backoff, LagPolicy, ResilientSubscription, ResilientWatch with the full add/remove set, SetCursor, Rescan, Reload → ReloadSummary, WatchSet, WatchSetLoader. Complete. - §3.5 prefix privacy and helpers: ScripthashOf, PrefixOf, PrefixWatcher, FundingHit/SpendingHit/PrefixHits with IsMatch/HasUnresolved, DisplayHex, DisplayHexUnreversed, ParseTxid, TxidFromDisplayHex. Complete. - §3.6 examples: all twelve Rust counterparts, plus deposit_notify. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HZw7Sf8PZj3qPJ6orxDH17
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.
8 of 8 in the Go SDK stack. Base:
feat/go-sdk-prefix→feat/go-sdk-parity→ this.Merge order: #515 → #516 → #517 → #518 → this one.
Thirteen runnable examples, a real README, a manual chapter, changelog and
release-note entries, cross-links from the wire spec, and two fixes to the test
infrastructure that landed along the way.
Examples are their own module
Two of them (
sp_wallet,sp_light_scan) do BIP 352 receiver derivation, whichneeds real secp256k1 scalar and point arithmetic. A dependency declared in the
SDK's own
go.modreaches every consumer's module graph whether they import thepackage or not — so btcec lives in a nested module, the same reason
toolsande2eare separate. The published SDK graph stays gRPC + protobuf.The alternative, hand-rolling curve math with
math/biginside an example,would have been worse than the dependency. An example teaches, and that one
would teach the wrong thing; a real Go wallet already has a curve library.
The BIP 352 derivation is shared, and tested from the other side
examples/internal/bip352is one implementation rather than a copy in eachexample, because the label arm is exactly what integrators get wrong: omit the
label tweak and you derive a key that looks fine and does not control the
output, so change becomes silently unspendable.
Its tests play the sender, reaching the same output key by a genuinely
different route — the shared secret from the other side of the ECDH (
a·B_scanrather than
b_scan·T), and the output key by point addition rather than thescalar shortcut the receiver takes. A test that re-derived the receiver's way
would agree with any bug it had.
Perturbation: 10 cases, 10 caught — after a fix to the test itself. The
first version computed the label tweak by calling the function under test, so it
could not see that function keyed on the wrong secret (9/10). It now restates
BIP 352 §5 directly.
What the tests do not establish is stated in the file: they are algebra and
self-consistency checks, not BIP 352 known-answer vectors. The authoritative
check that this matches the deployed protocol is the node's own silent-payment
index matching real payments, which the E2E suite covers.
deposit_notifyThe README quickstart, verbatim and compiled. The most-copied code in the
repository had nothing checking it.
Two infrastructure fixes
examples/ande2e/were silently unlinted. The analyzers resolve./...against the main module and stop at a nested
go.mod, solint.shwas checkingone of what are now four modules —
e2esince the day it landed. Both are cleanonce actually linted.
e2eneeds its build tag passed, or every analyzerreports a clean run over zero files.
E2E deadlines — and a correction. I had planned to widen the tight 30s/60s
deadlines wholesale. Reading them showed two kinds that want opposite treatment:
recvMatching,awaitRW, a poll loop) returns theinstant its condition holds, so its deadline is paid only when the test was
going to fail anyway. Generous is free. Widened 30s → 60s, plus a fired-alarm
prune poll 10s → 45s.
collect, or a context deadline proving nothingre-fires) is paid in full on every run, because "nothing happened" can only
be established by waiting. Widening one slows every green run to buy
confidence in a claim already anchored by the positive wait ahead of it. Left
alone, with the reasoning now in
harness_test.goso the next flake does notget "fixed" by inflating the wrong kind.
Parity inventory (plan §3), walked item by item
Dial+WithBearerToken/WithTLS/WithTLSCAPem/WithMTLS/WithTLSServerName/WithKeepalive/WithoutKeepalive/WithGRPCDialOption;Category*consts;MaxSPLabelsPerTarget,MaxSPTargetsPerConnection. Complete.Subscribe/Stream.Recv,Watch→(*WatchHandle, *Stream), all eight watch kinds with Add/Remove, plusSetCategories,SetWatchOptions,SetCursor,SetWatchSet,Rescan,SendControl;SilentPaymentTarget.Validate/ScanPubkey;Stream.Cursor.Complete.
plan's enumeration omitted
PrefixMatched. The count is not what guaranteescompleteness — the protobuf-reflection test walking the
NodeEventoneofdescriptor is. All seven enums carry
Known().CursorStore/FileCursorStore/NoopCursorStore,Backoff,LagPolicy,ResilientSubscription,ResilientWatchwith thefull add/remove set,
SetCursor,Rescan,Reload→ReloadSummary,WatchSet,WatchSetLoader. Complete.ScripthashOf,PrefixOf,PrefixWatcher,FundingHit/SpendingHit/PrefixHitswithIsMatch/HasUnresolved,DisplayHex,DisplayHexUnreversed,ParseTxid,TxidFromDisplayHex. Complete.deposit_notify.Docs
clients/go/README.md— rewritten; deposit-notification quickstart first,byte order above the fold, surface-selection table, delivery guarantees.
docs/manual/src/go-sdk.md— new chapter, inSUMMARY.md. Includes theRust → Go mapping table, so "idiomatic" never silently means "missing".
CHANGELOG.md+docs/release-notes/0.5.0-pre.md— SDK and parity-harnessentries, plus a Highlights bullet.
docs/api/streaming.md— a "first-party clients" note in §1 and Gocounterparts where the spec named only the Rust SDK.
Verification
./lint.shclean across all four modulesgo test ./...(SDK) andgo build ./... && go test ./...(examples) greenAfter this merges: tag
clients/go/v0.1.0.🤖 Generated with Claude Code
https://claude.ai/code/session_01HZw7Sf8PZj3qPJ6orxDH17