Skip to content

Make the C# package README user-facing - #1764

Merged
DanGould merged 5 commits into
payjoin:masterfrom
chavic:chavic/csharp-user-readme
Aug 4, 2026
Merged

Make the C# package README user-facing#1764
DanGould merged 5 commits into
payjoin:masterfrom
chavic:chavic/csharp-user-readme

Conversation

@chavic

@chavic chavic commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

The NuGet page renders README.md as the package front page, but most of its content (Development, Packaging, cross-compilation) is for contributors building the bindings, not for .NET developers consuming them. This corrects it.

Disclosure: co-authored by Claude Code.

Pull Request Checklist

@coveralls

coveralls commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 30827364559

Coverage remained the same at 86.57%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 16322
Covered Lines: 14130
Line Coverage: 86.57%
Coverage Strength: 338.12 hits per line

💛 - Coveralls

@chavic
chavic force-pushed the chavic/csharp-user-readme branch from 56777f8 to cb2bf4b Compare July 24, 2026 21:41
@chavic
chavic requested review from DanGould and benalleng and removed request for DanGould and benalleng July 25, 2026 05:34

@DanGould DanGould left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So I for C#, we've got a lot of these and there are basically three sections that need to be covered. The conceptual stuff, the C#-specifc context, and then code examples. In my opinion, the conceptual stuff should be lifted from a commonplace that way it can be edited across all packages at once. The C#-specifics are the meat of this PR, and any code needs to be checked against the compiler/runtime so that it never goes out of date.

Conceptual stuff

Because this is meant for developers, I would do my best to match the register of BIP77 itself. Concretely, the narrative in the first sentence is quite different from how that presents.

Payjoin lets the receiver of a Bitcoin payment contribute inputs to the sender's transaction, improving privacy and enabling batching in an ordinary-looking payment.

The BIP doesn't use "payment" and I have been avoiding that word everywhere because what payjoin does is quite different than lightning, which is better designed specifically for payments. I prefer to use "transfer" to describe funds moving. And the bip doesn't describe improvements to privacy, but rather preservation of privacy by by heuristic poisoning. This is a critical frame to maintain across our developer messaging until we have further concrete guarantees we can make about "improving privacy" because the first question thereafter is naturally "by how much" and we don't have a concrete answer. Satoshi's whitepaper privacy section being addressed is around the best payjoin can do on its own.

C#-specifics

.NET requirements, supported platforms, install, is all great for package consumers. Keep it

Code

Lastly, the examples. Writing them here creates same problem that we have in old payjoindevkit.org Rust articles. If they don't compile they get stale almost immediately. So I don't really think examples belong here. I think we should have pointers to a samples/ folder perhaps or otherwise XML Doc string comments that have IntelliSense that compiles in VS Code.

Alternatively I read that https://github.com/SimonCropp/MarkdownSnippets can be used to point to the README here and actually compile / test things by using #region but haven't tried.


The move to DEVELOPMENT.md and separation in general seems correct to me. cACK

@chavic

chavic commented Jul 27, 2026

Copy link
Copy Markdown
Collaborator Author

I'll look into incorporating this strategy

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good catch, I originally liked DEVELOPMENT.md for what this is, especially to make it distinct from policy, but it is probably better to stay consistent and then consider appling a wholesale change CONTRIBUTING -> DEVELOPMENT as a separate matter / commit / pr

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.

Agree with all of this.

Not relevant here but another pattern that reinforces the contributing verbiage are the contrib folders that hold the testing and linting scripts for each crate/ffi bindings.

@chavic

chavic commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

Just reworked the narrative and the examples moved to a samples/ folder that compiles as part of Payjoin.Tests, with the README pointing there. Opened #1772 for the shared conceptual text across packages.

@xstoicunicornx xstoicunicornx 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.

Again DEVELOPMENT.md should be CONTRIBUTING.md to align with established patterns in the repo (#1764 (comment))

Also, I am not familiar with csharp and its development flows but will do my best to review the content.

In general, is there anything in the samples that isn't already covered in the integration test? Even if there is maybe it would be better to incorporate those missing elements in the integration test instead?

Instead of creating standalone sample files that need to be maintained and have no indicator of when they are out of date it seems better to maintain an extremely well documented and straightforward integration test instead and point developers there to understand usage. Right now the integration test has no comments so would likely be hard to follow. Additionally, these piecemeal code samples are very minimal and focus on one specific aspect of the functionality but leave the developer to figure out how those pieces fit together themselves. The integration test shows how all the pieces fit together.

What do you think?

@chavic

chavic commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

Again DEVELOPMENT.md should be CONTRIBUTING.md to align with established patterns in the repo ((earlier comment))

......

I mostly agree; I know I would probably use the integration tests or CLI. I think the samples folder should break CI when it drifts, but it won't catch behavior drift; that’s a good point. At the same time, the integration tests may not be well structured for presenting... after adding comments to the integration tests, I think your suggestion is actually better

xstoicunicornx
xstoicunicornx previously approved these changes Jul 30, 2026

@xstoicunicornx xstoicunicornx 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.

ACK d7bc5f6

Documentation reads strictly better than before. Really like the commented integration test. Have some suggestions that I think could add clarity.

Disclaimer: I have not run all the csharp build commands myself so can't attest to all of their accuracy.

Comment thread payjoin-ffi/csharp/README.md Outdated
- `ref/net10.0/Payjoin.dll`
- `runtimes/any/lib/net10.0/Payjoin.dll`
- `runtimes/{rid}/native/{native-library}`
The usage reference is the commented walkthrough in [`IntegrationTests.cs`](https://github.com/payjoin/rust-payjoin/blob/master/payjoin-ffi/csharp/IntegrationTests.cs): `TestIntegrationV2ToV2` drives a complete payjoin from both sides, executed by CI on every change so it cannot go stale, and narrates each protocol step, from opening and persisting the session through the receiver checklist to signing the proposal.

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 would put this in its own section above Receiver a payjoin so it is immediately identifiable and not buried in text. Maybe could call the section End to end example or something similar.

Comment thread payjoin-ffi/csharp/README.md Outdated
```csharp
var uri = Payjoin.Url.Parse(
"bitcoin:12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX?amount=1&pj=https://example.com?ciao");
A sender session starts from a BIP 21 URI scanned from the receiver (`Payjoin.Uri.Parse(...).CheckPjSupported()`) and the wallet's signed PSBT, posts that original PSBT, and polls for the receiver's proposal through the same request/response flow. The sender half of the same walkthrough shows every step.

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.

Suggested change
A sender session starts from a BIP 21 URI scanned from the receiver (`Payjoin.Uri.Parse(...).CheckPjSupported()`) and the wallet's signed PSBT, posts that original PSBT, and polls for the receiver's proposal through the same request/response flow. The sender half of the same walkthrough shows every step.
A sender session starts from a BIP 21 URI scanned from the receiver (`Payjoin.Uri.Parse(...).CheckPjSupported()`) which is used to create and sign the original PSBT which is then posted back to the receiver. Then the sender polls for the receiver's payjoin proposal through a request/response flow. Once the sender receives the payjoin proposal it can sign and broadcast it to the network. The sender half of the same walkthrough shows every step.

Something like this that is a bit clearer might be better.

Comment thread payjoin-ffi/csharp/IntegrationTests.cs Outdated
/// usage reference the package README points at. <see cref="TestIntegrationV2ToV2"/>
/// drives a complete payjoin between two regtest wallets: the receiver opens a
/// session and produces a BIP 21 URI, the sender posts its original PSBT to an
/// untrusted directory, the receiver checks that original, contributes an input,

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.

Suggested change
/// untrusted directory, the receiver checks that original, contributes an input,
/// untrusted directory, the receiver fetches that original PSBT, contributes an input,

Comment thread payjoin-ffi/csharp/IntegrationTests.cs Outdated
/// drives a complete payjoin between two regtest wallets: the receiver opens a
/// session and produces a BIP 21 URI, the sender posts its original PSBT to an
/// untrusted directory, the receiver checks that original, contributes an input,
/// and posts a proposal back, and the sender signs and broadcasts the final

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.

Suggested change
/// and posts a proposal back, and the sender signs and broadcasts the final
/// and posts a payjoin proposal back, and the sender signs and broadcasts the final

Comment thread payjoin-ffi/csharp/IntegrationTests.cs Outdated
Comment on lines +16 to +17
/// through the directory, encapsulated in OHTTP so the directory cannot link
/// client identity to session content.

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.

Suggested change
/// through the directory, encapsulated in OHTTP so the directory cannot link
/// client identity to session content.
/// through the directory, encapsulated in OHTTP so the directory cannot observe
/// session content. Additionally all requests to the directory are routed through an
/// OHTTP relay to prevent the directory from learning the receiver and sender identities.

Just wanna be careful here because the directory can link the sender and receiver requests because they have the same mailbox id, and being unable to link this to the client identity is not due to OHTTP encapsulation but due to intermediary OHTTP relay.

Comment thread payjoin-ffi/csharp/IntegrationTests.cs Outdated
Comment on lines +268 to +269
// own check, and Save yields the next state. A receiver that skips a check
// does not typecheck.

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.

Suggested change
// own check, and Save yields the next state. A receiver that skips a check
// does not typecheck.
// own check, and Save yields the next state.

Maybe last sentence isn't necessary? I wasn't too sure what it meant myself.

Comment thread payjoin-ffi/csharp/IntegrationTests.cs Outdated
/// <summary>
/// Check 4: find which outputs of the original pay the receiver. These are
/// the outputs the payjoin is allowed to substitute or amend; everything
/// else belongs to the sender and stays untouched.

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.

Suggested change
/// else belongs to the sender and stays untouched.
/// else belongs to the sender and stays untouched. This check also ensures that at least
/// one output actually pays the receiver.

}

/// <summary>
/// The heart of the payjoin: the receiver contributes its own input. 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.

Love this phrasing :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I actually have some minor reservations about this phrasing, explained in earlier comment, but am OK with it for now here in an explanatory test comment.

xstoicunicornx
xstoicunicornx previously approved these changes Aug 3, 2026

@xstoicunicornx xstoicunicornx 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.

ACK 5c2db25

Updates look good. Looks like there is a merge conflict that needs to be addressed though?

chavic added 5 commits August 3, 2026 17:14
Replace the README's inline code with prose and pointers to a samples/
folder that builds as part of Payjoin.Tests, so the examples are
checked by the compiler on every change instead of going stale on the
package page. Align the narrative with BIP 77's register while here:
funds move in transfers rather than payments, and payjoin preserves
privacy by poisoning the common-input-ownership heuristic rather than
promising an improvement it cannot quantify.
The dart and javascript bindings document their build and test flow in
a CONTRIBUTING.md, as does the repository root. Follow that pattern
instead of introducing a new name.
Replace the standalone samples with narrative comments in
IntegrationTests.cs and point the README there. The samples only
compiled, so they proved syntax and not behavior, and each one showed
a fragment while leaving the composition to the reader. The
integration test runs on CI on every change and contains the complete
flow, so comments on it document usage that is executed rather than
merely compiled: the walkthrough narrates session bootstrap and
persistence, the sender's original PSBT, each receiver check and why
it exists, input contribution, the fee range, and finalization.
Give the end-to-end example its own README section, complete the
sender narrative through signing and broadcast, separate the two
privacy mechanisms correctly (OHTTP encapsulation hides content from
the directory, the relay hides client identity; the directory can
still link one session's requests through the shared mailbox), drop
an unclear sentence, and note that output identification also
guarantees at least one output pays the receiver.

@xstoicunicornx xstoicunicornx 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.

re-ACK 3fd2ec4

@DanGould DanGould left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

utACK 3fd2ec4 with conceptual commentary for the future

The NuGet package is still prepared as a preview while the C# API stabilizes. The
first release-ready package layout targets .NET 10 and ships a managed
`Payjoin.dll` plus RID-specific native `payjoin_ffi` libraries.
Payjoin lets the receiver of a Bitcoin transfer contribute inputs to the sender's transaction. The result looks like any other transaction, which preserves privacy by poisoning the common-input-ownership heuristic that chain surveillance depends on, and it lets the receiver batch its own operations into the same transaction. These bindings implement both [BIP 78](https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki) (synchronous payjoin) and [BIP 77](https://github.com/bitcoin/bips/blob/master/bip-0077.md) (asynchronous payjoin, where sender and receiver exchange the transaction through an untrusted directory and never need to be online at the same time), and ship with native libraries for every supported platform, so no Rust toolchain is required.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I prefer the BIP77 abstract's first sentence

Payjoin lets Bitcoin senders and receivers interact to make batched transactions.

Because the interaction is the core that lest both cooperate to choose inputs and outputs. Yes, past discussion, and most privacy consequences have been discussed in the context of input-contribution but I think that's a limited view. Just a pin to track for later.

}

/// <summary>
/// The heart of the payjoin: the receiver contributes its own input. The

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I actually have some minor reservations about this phrasing, explained in earlier comment, but am OK with it for now here in an explanatory test comment.

@DanGould
DanGould merged commit cefe1ca into payjoin:master Aug 4, 2026
35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants