Skip to content
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

BIP 321: URI Scheme (Replace BIP 21 with a new BIP containing information about more modern usage of it) #1555

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
203 changes: 203 additions & 0 deletions bip-XXXX.mediawiki
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
<pre>
BIP: XXXX
Layer: Applications
Title: URI Scheme
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you want to use the exact same title?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea, I don't see why we'd change it.

Author: Matt Corallo <bip21@bluematt.me>
Comments-Summary: No comments yet.
Comments-URI: https://github.com/bitcoin/bips/wiki/Comments:BIP-XXXX
Status: Draft
Type: Standards Track
Created: 2024-07-13
License: BSD-2-Clause
</pre>
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing License header, and should probably have a Replaces header:

Suggested change
</pre>
License: ?
Replaces: 21
</pre>

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I cannot assign this document a license given a substantial portion was written by @luke-jr. I'm happy to assign my contributions whatever license he wants for his.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah apologies, BIP 20 is licensed.

Copy link
Member

Choose a reason for hiding this comment

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

If this Replaces: 21 then BIP21 could also be updated to Status: Replaced.


==Abstract==
This BIP proposes a URI scheme for describing Bitcoin payment receipt information.
Copy link
Contributor

Choose a reason for hiding this comment

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

"Invoice information" would make sense to me here, or "payment instructions", but receipt doesn’t make sense to me here.


Copy link
Contributor

Choose a reason for hiding this comment

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

Th Copyright section is missing. Judging from the header:

Suggested change
==Copyright==
This BIP is licensed under the BSD 2-clause license.

==Motivation==
The purpose of this URI scheme is to enable users to easily make payments by simply clicking links on webpages or scanning QR Codes.
TheBlueMatt marked this conversation as resolved.
Show resolved Hide resolved

This BIP is a modification of [[bip-0021.mediawiki|BIP 0021]] to add information about the modern usage of bitcoin: URIs (including standard query parameters and modern address types) as well as provide forward-looking guidance on how to incorporate new payment instructions. It further adds an optional extension to provide the payment initiator with proof of payment. BIP 21 was based on BIP 20, which was, in turn based off an earlier document by Nils Schneider.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
This BIP is a modification of [[bip-0021.mediawiki|BIP 0021]] to add information about the modern usage of bitcoin: URIs (including standard query parameters and modern address types) as well as provide forward-looking guidance on how to incorporate new payment instructions. It further adds an optional extension to provide the payment initiator with proof of payment. BIP 21 was based on BIP 20, which was, in turn based off an earlier document by Nils Schneider.
This BIP is a modification of [[bip-0021.mediawiki|BIP 0021]] to add information about the modern usage of bitcoin: URIs (including standard query parameters and modern address types) as well as provide forward-looking guidance on how to incorporate new payment instructions. It further adds an optional extension to provide the payment initiator with proof of payment. BIP 21 was based on BIP 20, which was, in turn based on an earlier document by Nils Schneider.


==Specification==

=== General rules for handling (important!) ===

Bitcoin clients MUST NOT act on URIs without getting the user's authorization.
They SHOULD require the user to manually approve each payment individually, though in some cases they MAY allow the user to automatically make this decision.

=== Operating system integration ===
Graphical bitcoin clients SHOULD register themselves as the handler for the "bitcoin:" URI scheme by default, if no other handler is already registered. If there is already a registered handler, they MAY prompt the user to change it once when they first run the client.

=== General Format ===

Bitcoin URIs follow the general format for URIs as set forth in RFC 3986. The path component consists of a bitcoin address, and the query component provides additional payment options.

Elements of the query component may contain characters outside the valid range. These must first be encoded according to UTF-8, and then each octet of the corresponding UTF-8 sequence must be percent-encoded as described in RFC 3986.

=== ABNF grammar ===

bitcoinurn = "bitcoin:" [ bitcoinaddress ] [ "?" bitcoinparams ]
bitcoinaddress = *base58 / *bech32 / *bech32m
bitcoinparams = bitcoinparam [ "&" bitcoinparams ]
bitcoinparam = [ amountparam / labelparam / messageparam / responseparam / otherparam / reqparam ]
amountparam = "amount=" *digit [ "." *digit ]
labelparam = "label=" *qchar
messageparam = "message=" *qchar
responseparam = [ "req-" ] "pop=" *qchar
otherparam = qchar *qchar [ "=" *qchar ]
reqparam = "req-" qchar *qchar [ "=" *qchar ]

Here, "qchar" corresponds to valid characters of an RFC 3986 URI query component, excluding the "=" and "&" characters, which this BIP takes as separators.

The scheme component ("bitcoin:") is case-insensitive, and implementations must accept any combination of uppercase and lowercase letters. The query parameter keys are also case-insensitive. Query parameter values and bitcoin address fields may be case-sensitive depending on their content.
Copy link
Contributor

Choose a reason for hiding this comment

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

It’s a good change, but please mention the case-insensitivity of query parameter keys in the Backwards Compatibility section, since BIP 21 specified query parameter keys to be case-sensitive.


=== Bitcoin Address ===

The bitcoinaddress body MUST be either a base58 P2SH or P2PKH address, bech32 Segwit version 0 address, bech32m Segwit address, or empty. Future address formats SHOULD instead be placed in query keys as optional payment instructions to provide backwards compatibility during upgrade cycles. After new address types are near-universally supported, or for recipients wishing to avoid a standard on-chain fallback, the bitcoinaddress part of the URI MAY be left empty.
Copy link
Contributor

Choose a reason for hiding this comment

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

[…] or for recipients wishing to avoid a standard on-chain fallback […]

Some of this last sentence repeats prior specification parts and the rest could probably be in Rationale

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This section describes normative behavior so we can't move it (otherwise its not clear you can set the address part empty), not sure which parts are redundant with elsewhere, aside from the ABNF grammar, but I prefer to have it in text as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

I’m not sure what I exactly meant back then. Either way, it’s not clear to me why this mentions "After new address types are near-universally supported". If the receiver wants to only offer specific optional payment methods even while they are not broadly supported, that’s up to the receiver.

Suggested change
The bitcoinaddress body MUST be either a base58 P2SH or P2PKH address, bech32 Segwit version 0 address, bech32m Segwit address, or empty. Future address formats SHOULD instead be placed in query keys as optional payment instructions to provide backwards compatibility during upgrade cycles. After new address types are near-universally supported, or for recipients wishing to avoid a standard on-chain fallback, the bitcoinaddress part of the URI MAY be left empty.
The bitcoinaddress body MUST be either a base58 P2SH or P2PKH address, bech32 Segwit version 0 address, bech32m Segwit address, or empty. Future address formats SHOULD instead be placed in query keys as optional payment instructions to provide backwards compatibility during upgrade cycles. The bitcoinaddress part of the URI MAY be left empty, if there is at least one optional payment instruction provided and the recipient does not want to provide a static on-chain payment method.


=== Query Keys ===

The following keys are defined generally and apply to any URI regardless of payment instructions:

*label: Label for that address (e.g. name of receiver)
*address: bitcoin address
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the "address" query key used for, if all base58, bech32, and bech32m addresses are permitted in the body, and new payment instructions using bech32[m] should use the HRP? Is the intent to provide a fall-back address from the set of addresses permitted in the body, in case the address in the body is not supported by the sender’s client?

Alternatively, should the body contain the type that is most likely to be supported by the client? Is there a way for the receiver to express which address type they’d prefer if multiple were provided?

If I wanted to express a preference for P2TR, have a P2WPKH fallback, and for people from the last decade also provide a P2PKH address, how would I do that?

bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?address=bc1qp2wpkhaddress?bc=bc1ppaytotaprootaddress

bitcoin:bc1ppaytotaprootaddress?address=175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?bc=bc1qp2wpkhaddress

*message: message that describes the transaction to the user ([[#Examples|see examples below]])
*pop: a URI which the Bitcoin Wallet may return to in order to provide the application which initiated the payment with proof that a payment was completed.

The following keys are currently defined for payment instructions of various forms:

*lightning: Lightning BOLT 11 invoices
*lno: Lightning BOLT12 offers
*pay: [[bip-0351.mediawiki|BIP 351 Private Payment addresses]]
*sp: [[bip-0352.mediawiki|BIP 352 Silent Payment addresses]]

New payment instructions using bech32 or bech32m encodings SHOULD reuse their address format's Human Readable Part as the parameter key.
TheBlueMatt marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

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

In the discussion on this PR, some reviewers were suggesting using bc1q and bc1p as parameter keys for native segwit payment instructions. In case that was meant to be proposed here, I wanted to point out that the human-readable part for all native segwit versions is bc.

If you meant to propose that bc1q, bc1p, bc1z, to be used, this could be described as:

Suggested change
New payment instructions using bech32 or bech32m encodings SHOULD reuse their address format's Human Readable Part as the parameter key.
New payment instructions using bech32 or bech32m encodings SHOULD reuse their address format's _human-readable part (HRP)_ as the parameter key. As all native segwit outputs share the same HRP, Bech32m addresses for future native segwit output types SHOULD use the four-character prefix of the address as the parameter key, e.g. `bc1p`, `bc1z`, `bc1r`, etc.


==== Transfer amount ====

If an amount is provided, it MUST be specified in decimal BTC.
All amounts MUST contain no commas and use a period (.) as the separating character to separate whole numbers and decimal fractions.
I.e. amount=50.00 or amount=50 is treated as 50 BTC, and amount=50,000.00 is invalid.

=== Proof of Payment ===

The URI MAY include a "pop" (or "req-pop") parameter whose value can be used to build a URI which the wallet application can, after payment completes, "open" to provide proof the payment was completed or other information about the payment.

The value of a "pop" (or "req-pop") parameter shall be a percent-encoded (per RFC 3986 section 2.1) URI prefix. The wallet application, if it supports providing payment information SHOULD percent-decode the provided URI once, append the query parameter key from which the payment instructions used were read, append a single =, and finally append the Payment Information to the resulting URI and open it with the default system handler for the URI. For payment instructions read from the body of the URI, "onchain" SHALL be used in place of the key.

A wallet MUST validate that the provided URI's scheme is not (case-insensitive) "http", "https", "file", "javascript", "mailto" or any other scheme which will open in a web browser prior to opening it.
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I want to get feedback on this line. My thinking here is that there may be some link to a bitcoin: URI in, eg, a social media site or some other context where the user might click it, pay, but then doesn't want the callback to open a random website that then will reveal the sender's IP. However, it does limit the utility somewhat. Specifically web apps will be unable to get callbacks directly without registering a URI handler.

I'm curious if/on what platforms registering a URI handler for a web app is annoying to deal with, and if the tradeoff here makes sense. I strongly dont want to introduce an IP leak because of this, but also there may be some platforms where we really have to.

Choose a reason for hiding this comment

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

If IP address leak is the only concern, "http" and "https" could be allowed with .onion and .i2p hostnames as exception.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, some wallets could implement that, but in general if wallets are going to implement by opening with the system-default handler its pretty unlikely that a onion or .i2p is gonna work, so not sure its even worth calling out.

Choose a reason for hiding this comment

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

Yes, system default handler then does not make much sense, but a lot of wallets already implement Tor and some also I2P and could handle http(s) themselves.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yea, I guess I just see no need for a remote call for these callbacks - the whole point of the callbacks is to keep things local, if you're going remote anyway the thing initiating the payment can just ask the recipient if they got it using HTTP...


If a wallet will not open the pop scheme (either because it does not support returning payment information for the selected payment method or because it uses a URI scheme which should not be opened) and the parameter was passed as a "req-pop" parameter, the wallet MUST NOT initiate payment.

For payments made using an on-chain transaction, the Payment Information shall be the full (including witness data) Bitcoin transaction as it was broadcasted to the Bitcoin network, encoded in hex.

For payments made using a BOLT 11 invoice (communicated via the `lightning` parameter), the Payment Information shall be the hex-encoded payment preimage.

Other payment schemes will define their own Payment Information format. This BIP may be updated from time to time with Payment Information formats for other payment schemes.

== Rationale ==

===Payment identifiers, not person identifiers===
Best practices are that a unique address should be used for every transaction on-chain.
Therefore, a URI which contains an on-chain payment address MUST NOT represent an exchange of personal information, but a one-time payment instruction. URIs which represent only reusable non-address-reusing payment instructions (like Lightning BOLT12 offers or Silent Payments) MAY be reused as a wallet sees fit.

==Forward compatibility==
Variables which are prefixed with a req- are considered required. If a client does not implement any variables which are prefixed with req-, it MUST consider the entire URI invalid. Any other variables which are not implemented, but which are not prefixed with a req-, can be safely ignored.
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you mean "query parameters" instead of variables here?


As future new address types should be added using query parameters rather than the `bitcoinaddress` field, URIs seamlessly support various payment instructions while senders only need to support legacy instructions. This allows old senders to pay newer recipients which offer more modern payment instruction formats.

==Backward compatibility==
As this BIP is written, several clients already implement a bitcoin: URI scheme similar to this one, however usually without the additional "req-" prefix requirement. Thus, it is recommended that additional variables prefixed with req- not be used in a mission-critical way until a grace period of 6 months from the finalization of this BIP has passed in order to allow client developers to release new versions, and users of old clients to upgrade.

Compared to BIP 21, this document describes standard query parameters containing payment instructions, allows bech32 and bech32m `bitcoinaddress` fields, and allows for URIs with an empty `bitcoinaddress` field. Use of bech32 and bech32m `bitcoinaddress` fields were long-since common practice in 2024, and the `lightning` query parameter storing BOLT 11 payment instructions became common practice in the year or three leading up to 2024. Inclusion of standard query parameters was added to provide guidance on query parameter usage going forward.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Compared to BIP 21, this document describes standard query parameters containing payment instructions, allows bech32 and bech32m `bitcoinaddress` fields, and allows for URIs with an empty `bitcoinaddress` field. Use of bech32 and bech32m `bitcoinaddress` fields were long-since common practice in 2024, and the `lightning` query parameter storing BOLT 11 payment instructions became common practice in the year or three leading up to 2024. Inclusion of standard query parameters was added to provide guidance on query parameter usage going forward.
Compared to BIP 21, this document describes standard query parameters containing payment instructions, allows bech32 and bech32m addresses in the `bitcoinaddress` field, and allows for URIs with an empty `bitcoinaddress` field. Use of bech32 and bech32m `bitcoinaddress` fields were long-since common practice in 2024, and the `lightning` query parameter storing BOLT 11 payment instructions became common practice in the year or three leading up to 2024. Inclusion of standard query parameters was added to provide guidance on query parameter usage going forward.


Additionally, this BIP describes the "pop" query parameter, which was unused and will be ignored by BIP 21 implementations.

Any existing BIP 21 implementation should automatically be fully compliant with this BIP, as the changes only describe existing practice or impact future address format inclusion.
Copy link
Contributor

Choose a reason for hiding this comment

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

As mentioned above, BIP 21 required query parameters to be case-sensitive

The rest of the URI is case-sensitive, including the query parameter keys.

while this proposal allows them to be any case. This should be mentioned in the Backward Compatibility section.


== Proof of Payment ==

On many mobile operating systems (especially, or any operating system more generally), applications may "open" a bitcoin: URI in order to initiate a payment with the user's default wallet application. These payment-initiating applications may wish to learn about the completed payment.

For payments completed on-chain, this is largely addressed by having the payment-initiating application monitor the blockchain for payment completion, however for other payment schemes (e.g. lightning), no such global ledger of transactions exists. In that case, proof of payment must be provided via some other mechanism.

== Appendix ==

=== Simpler syntax ===

This section is non-normative and does not cover all possible syntax.
Please see the ABNF grammar above for the normative syntax.

[foo] means optional, &lt;bar&gt; are placeholders

<nowiki>bitcoin:<address>[?amount=<amount>][?label=<label>][?message=<message>]</nowiki>
Copy link
Contributor

Choose a reason for hiding this comment

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

This section confuses me. It feels like I’m missing some context. Is this supposed to provide a simplified syntax for some part of the implementers? Who is supposed to use the simplified syntax? Under what circumstances should this syntax be used? What are the trade-offs?


=== Examples ===
TheBlueMatt marked this conversation as resolved.
Show resolved Hide resolved

==== URIs ====
Copy link
Contributor

@murchandamus murchandamus Nov 15, 2024

Choose a reason for hiding this comment

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

Perhaps it would be good to provide a couple more invalid examples. E.g. a common issue might be the reuse of a query parameter.

What is supposed to happen when e.g. two amounts are provided, or two addresses for the same key? Should that fail, use the first, or use the last value for the same query parameter key?


Just the address:
bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W

Address with name:
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
Address with name:
Address with recipient’s name as label:

bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?label=Luke-Jr

Request 20.30 BTC to "Luke-Jr":
bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=20.3&label=Luke-Jr

Request 50 BTC with message:
bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=50&label=Luke-Jr&message=Donation%20for%20project%20xyz

Request funds to be paid over lightning to a BOLT 11 invoice with a fallback to on-chain payments:
bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?lightning=lnbc420bogusinvoice

Request funds to be paid over lightning to a BOLT 11 invoice with no fallback:
bitcoin:?lightning=lnbc420bogusinvoice

Request funds to be paid over lightning to a BOLT 12 offer with no fallback:
bitcoin:?lno=lno1bogusoffer

Request funds to be paid over lightning to a BOLT 12 offer or silent payments address with no fallback:
bitcoin:?lno=lno1bogusoffer&sp=sp1qsilentpayment

Request funds to be paid to a silent payments address with no fallback:
bitcoin:?sp=sp1qsilentpayment

Request funds to be paid to a silent payments address with a fallback:
bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?sp=sp1qsilentpayment

Some future version that has variables which are (currently) not understood and required and thus invalid:
bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?req-somethingyoudontunderstand=50&req-somethingelseyoudontget=999

Some future version that has variables which are (currently) not understood but not required and thus valid:
bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?somethingyoudontunderstand=50&somethingelseyoudontget=999

Characters must be URI encoded properly.

==== Proof of Payment ====

If the original URI is
bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?pop=initiatingapp%3a
the wallet should perform the payment information callback by opening
initiatingapp:onchain=$HEX_ENCODED_TRANSACTION

If the original URI is
bitcoin:?lightning=lnbc420bogusinvoice&pop=callbackuri%3abody%3fpop=
the wallet should perform the proof-of-payment callback by opening
callbackuri:body?pop=lightning=$HEX_ENCODED_PAYMENT_PREIMAGE

If the original URI is
bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?lightning=lnbc420bogusinvoice&pop=app%3a%3f
and the wallet pays on-chain, it should perform the payment information callback by opening
app:?onchain=$HEX_ENCODED_TRANSACTION
but if the app pays using lightning, it should perform the proof-of-payment callback by opening
app:?lightning=$HEX_ENCODED_PAYMENT_PREIMAGE

If the original URI is
bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?pop=https%3aiwantyouripaddress.com
the wallet should make a payment as it normally would but MUST NOT interact with iwantyouripaddress.com

If the original URI is
bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?req-pop=https%3aevilwebsite.com
the wallet MUST NOT make a payment