-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Annotate the reference implementation for Audit #52
Conversation
MENTAL-MODEL.md
Outdated
let link = link | ||
.check_pj_supported() | ||
.map_err(|e| anyhow!("The provided URI doesn't support payjoin (BIP78): {}", e))?; | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Refactoring (see issue #19) to use a builder pattern may eliminate the need to call check_pj_supported
or expose bip21 at all.
MENTAL-MODEL.md
Outdated
.wallet_process_psbt(&psbt, None, None, None) | ||
.with_context(|| "Failed to process PSBT")? | ||
.psbt; | ||
let psbt = load_psbt_from_base64(psbt.as_bytes()) // SHOULD BE PROVIDED BY CRATE AS HELPER USING rust-bitcoin base64 feature |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The load_psbt_from_base64
helper method could be refactored to use the bitcoin::base64
feature. I imagine the rust-bitcoin crate would be open to Psbt::to_base64
and Psbt::from_base64
feature-gated helpers being added as well. see #30
MENTAL-MODEL.md
Outdated
payjoin::bitcoin::Amount::from_sat(10000), | ||
None, | ||
); | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the current flow works and it does not need to change to prove the concept, but it may also be improved with the builder pattern (#19). The builder might take the bip21, an optional amount that differs from that in the bip21, fee rate, a closure to fund and sign an Original PSBT. The result should be a request containing (original_psbt, optional_parameters)
and state in context.
MENTAL-MODEL.md
Outdated
|
||
Senders request a payjoin from the receiver with a payload containing the Original PSBT and optional parameters. They require a secure endpoint for authentication and message secrecy to prevent that transaction from being modified by a malicious third party during transit or being snooped on. Only https and .onion endpoints are spec-compatible payjoin endpoints. | ||
|
||
Avoiding the secure endpoint requirement is convenient for testing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(but again, Signal guidelines remind me of the folly of options). I wonder if there's a better way to set up a testing environment with a valid https cert with something like Nix.
MENTAL-MODEL.md
Outdated
log::debug!("Proposed psbt: {:#?}", psbt); | ||
``` | ||
|
||
Payjoin response errors (called [receiver's errors in spec](https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#receivers-well-known-errors)) come from a remote server and can be used to "maliciously to phish a non technical user." Only those listed as "well known" in the spec should be displayed with preset messages to prevent phishing. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Errors from process_response
may be improved by being separated into safe ReceiverError::WellKnown
standard error types that can be displayed in the UI and ReceiverError::DebugOnly
which "can only appear in debug logs." Separation would simplify an integration's error handling. (#53)
MENTAL-MODEL.md
Outdated
copy.find(|(k, _)| k.eq_ignore_ascii_case(key)).map(|(_, v)| v) | ||
} | ||
} | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reckon bindings will run into a problem with this Trait-based design. @thunderbiscuit, what do you think?
MENTAL-MODEL.md
Outdated
// in a payment processor where the sender could go offline, this is where you schedule to broadcast the original_tx | ||
let _to_broadcast_in_failure_case = proposal.get_transaction_to_schedule_broadcast(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The broadcast out of order! This should be after check 1 making sure it's broadcastable.
MENTAL-MODEL.md
Outdated
} | ||
``` | ||
|
||
Serious, in-depth research has gone into proper transaction construction. [Here's a good starting point from the JoinMarket repo](https://gist.github.com/AdamISZ/4551b947789d3216bacfcb7af25e029e#gistcomment-2796539). Using methods for coin selection not provided by this library may have dire implications for privacy. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Downstream projects may benefit to consume expose the selection algorithm inside try_preserving_privacy
as a static function. It may be easier easier and more lightweight to bind to and avoid dangerous instances of UIH in addition to the typestate method. Look at BDK Coin Selection for interface inspiration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MENTAL-MODEL.md
Outdated
Fees are applied to the augmented Payjoin Proposal PSBT using calculation factoring the receiver's own preferred feerate and the sender's fee-related [optional parameters](https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#optional-parameters). The current `apply_fee` method is primitive, disregarding PSBT fee estimation and only adding fees coming from the sender's budget. When more accurate tools are available to calculate a PSBT's fee-dependent weight (slightly more complicated than it sounds, but solved, just unimplemented in rust-bitcoin), this `apply_fee` should be improved. | ||
|
||
```rs | ||
let payjoin_proposal_psbt = payjoin.apply_fee(min_feerate_sat_per_vb: Some(1))?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current apply_fee
method is primitive, disregarding fee estimation based on PSBT contents and only adding fees coming from the sender's budget. When more accurate tools are available to calculate a PSBT's fee-dependent weight (slightly more complicated than it sounds, but solved, just unimplemented in rust-bitcoin), this apply_fee
should be improved.
MENTAL-MODEL.md
Outdated
log::debug!("Extracted PSBT: {:#?}", payjoin_proposal_psbt); | ||
// Sign payjoin psbt | ||
let payjoin_base64_string = | ||
base64::encode(bitcoin::consensus::serialize(&payjoin_proposal_psbt)); | ||
// `wallet_process_psbt` adds available utxo data and finalizes | ||
let payjoin_proposal_psbt = | ||
bitcoind.wallet_process_psbt(&payjoin_base64_string, sign: None, sighash_type: None, bip32derivs: Some(false))?.psbt; | ||
let payjoin_proposal_psbt = | ||
load_psbt_from_base64(payjoin_proposal_psbt.as_bytes()).context("Failed to parse PSBT")?; | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The signing algorithm could be passed as a closure, but may encounter runtime issues as mentioned previously. I.e., rust does not yet support async closures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While bitcoind's wallet_process_psbt
handles most everything you throw at it with grace, LND's PSBT signing gets very cranky when fed inputs belonging to other wallets and requires a complicated workaround of multiple gRPC calls to function properly. It might be good to note this in the documentation since it's such a pain in the kiester.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some suggestions. I kind of ran out of steam near the end there, it's a pretty lengthy document. Hopefully what I was able to provide was helpful.
MENTAL-MODEL.md
Outdated
@@ -0,0 +1,376 @@ | |||
# What is the Payjoin SDK and How does it work? | |||
|
|||
The Payjoin SDK/`rust-payjoin` is the most well-tested and flexible library for BIP 78 Payjoin and related privacy practices. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Payjoin SDK/`rust-payjoin` is the most well-tested and flexible library for BIP 78 Payjoin and related privacy practices. | |
The Payjoin SDK/[`rust-payjoin`](https://crates.io/crates/payjoin) is the most well-tested and flexible library for [BIP 78 Payjoin](https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki) and related privacy practices. |
MENTAL-MODEL.md
Outdated
|
||
The Payjoin SDK/`rust-payjoin` is the most well-tested and flexible library for BIP 78 Payjoin and related privacy practices. | ||
|
||
The primary crate, `payjoin`, is runtime-agnostic. Data persistence, chain interactions, and networking may be provided by custom implementations or copy the reference `payjoin-client` + bitcoind, `nolooking` + LND integration, or `bitmask-core` + BDK integrations. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The primary crate, `payjoin`, is runtime-agnostic. Data persistence, chain interactions, and networking may be provided by custom implementations or copy the reference `payjoin-client` + bitcoind, `nolooking` + LND integration, or `bitmask-core` + BDK integrations. | |
The primary crate, `payjoin`, is runtime-agnostic. Data persistence, chain interactions, and networking may be provided by custom implementations or copy the reference `payjoin-client` + bitcoind, `nolooking` + LND integration, or `bitmask-core` + BDK integrations, compatible within either desktop, mobile, or WASM contexts. |
MENTAL-MODEL.md
Outdated
|
||
### 1. Parse BIP21 as `payjoin::Uri` | ||
|
||
Start by parsing a valid BIP 21 uri having the `pj` parameter. This is the [`bip21`](https://crates.io/crates/bip21) crate under the hood. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Start by parsing a valid BIP 21 uri having the `pj` parameter. This is the [`bip21`](https://crates.io/crates/bip21) crate under the hood. | |
Start by parsing a valid [BIP 21](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki) uri having the `pj` parameter. This is the [`bip21`](https://crates.io/crates/bip21) crate under the hood. |
MENTAL-MODEL.md
Outdated
&outputs, | ||
None, // locktime | ||
Some(options), | ||
None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is the bitcoind
variable from? And what is the last option? (I realize what they are, but it helps to be more explicit about this in the docs)
MENTAL-MODEL.md
Outdated
I wrote this in the original docs, but I think it should be amended. | ||
|
||
In case the payjoin goes through but you still want to pay by default. This missing `payjoin-client` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wrote this in the original docs, but I think it should be amended. | |
In case the payjoin goes through but you still want to pay by default. This missing `payjoin-client` | |
This was written in the original docs, but it should be clarified: In case the payjoin goes through but you still want to pay by default. This is missing `payjoin-client`. |
This seems like an incomplete thought, but I did my best to piece it together. Definitely try to avoid documenting in the first-person throughout the rest of this doc.
MENTAL-MODEL.md
Outdated
// TODO display well-known errors and log::debug the rest | ||
let psbt = ctx.process_response(response).with_context(|| "Failed to process response")?; | ||
log::debug!("Proposed psbt: {:#?}", psbt); | ||
``` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Where is ctx from? Please try to make examples more complete.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ctx is the context returned from create_pj_request in step 4, so I've added a comment. Would it be better to have a complete send_payjoin method based on a file in examples rather than this breakdown, or something else?
I've moved this into rustdoc for The original audit doc can be found here: https://gist.github.com/DanGould/595865688cabcfec42eb0e2a36f65490 |
A high level, annotated "TODO" for integrating payjoin in your app. This info should make its way into the docs.rs documentation and payjoin.org. To start I've drafted plain ol' markdown so you can review and suggest edits to the doc directly.
It's a mental model of what the payjoin-client logic does and where/how the coin selection happens and the interplay with a wallet to understand how it could work with others like bdk.
This is a tool to try and figure out the bare-minimum shape of what you'd need as an API to use it on a mobile phone which has already access to e.g. bdk bindings
Thanks to @thunderbiscuit for making this request.
cc: @fiatjaf @kaloudis who also requested more detail on how to integrate the protocol
One of Kixunil's criteria for taking his original work into widespread production is getting an outside audit. This is a good start.