Created
September 23, 2011 16:19
-
-
Save sipa/1237788 to your computer and use it in GitHub Desktop.
Beyond IP transactions: towards a Bitcoin payment protocol
This file contains 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
Beyond IP Transactions: towards a payment protocol | |
================================================== | |
IP transactions were originally introduced as a first "out-of-band" protocol | |
for negotiating a transaction output's public key. Being inconvenient and | |
insecure, they became obsolete, and recent versions of bitcoin don't support | |
them anymore. | |
The result is that static bitcoin addresses have become the most common way of | |
defining requested payments. This may be fine for anonymous donations, but is not | |
ideal for regular payments: | |
* Static bitcoin addresses can be reused inadvertently: when unique addresses are | |
employed for each individual payment, nothing distinguishes them from e.g. static | |
personal addresses. | |
* Individual payments may be hard to track when a bitcoin address is reused for | |
multiple payments. People turn to techniques like requiring a specific subcent | |
amount for transaction identification. | |
* Related to this, it is hard to attach comments or notes on a particular | |
(real-life-payment) transaction, as there is no communication between the | |
payer and payee except for the (bitcoin) transaction being submitted through | |
the network. In short: unless you are sure you gave the address to only one | |
person, there is no way to know who paid you (the ability to do anonymous | |
payments is a feature, but it isn't always wanted). | |
* Refunds require extra manual communication with the sender, as one cannot assume | |
payments to the address(es) the input coins were last sent to, will arrive | |
correctly. | |
* One cannot be sure that the private key corresponding to an older static bitcoin | |
address still exists. The owner may have switched e-wallet services, or have a | |
crashed hard drive. | |
Satoshi already hinted towards secure IP transactions, which would be a | |
combination of an IP or hostname, and a normal bitcoin address. This address would | |
then only be used for authentication, and not in the txout of the generated | |
transaction. See here for more information: | |
https://bitcointalk.org/index.php?topic=158.msg1322#msg1322 | |
I think we can combine this idea with the various bitcoin-URI proposals made | |
already, and extend it to a real bitcoin payment protocol. | |
The general idea: | |
* Use a URI/address as an entry point for retrieving a signed payment descriptor | |
* The payment descriptor contains all information necessary for the client | |
to construct a transaction. | |
* The resulting transaction is submitted to a possibly separate payment | |
processor, which either results in a signed "payment accepted" notice, or | |
failure, in which case the transaction is cancelled. | |
1. URI SCHEME | |
------------- | |
I suggest the following URI format: | |
http[s]+btc://<host>[:<port>][/<path>[/<name>]][?<query>]#<address> | |
It corresponds to HTTP communication with the URL: | |
http[s]://<host>[:<port>][/<path>[/<name>]][?<query>] | |
However, each request contains an optional HTTP header: | |
X-Bitcoin-Authenticate-As: <address> | |
And each response on a request that had this header, contains: | |
X-Bitcoin-Signature: <signature> | |
Where <signature> is a message signature (see | |
https://github.com/bitcoin/bitcoin/pull/524) of the response data with | |
the requested address. | |
This provides an embedding of bitcoin-address-based authentication in HTTP. | |
2. PAYMENT DESCRIPTORS | |
---------------------- | |
Both http[s]:// and http[s]+btc:// URI's can be used to initiate payments, | |
given that a GET results in a succesful response with MIME type: | |
application/x-bitcoin-payment-description | |
This payment descriptor consists of a JSON-encoded object with the following fields: | |
{ | |
"script" : base64-encoded txout script that must be present in the created transaction. | |
"amount" : bitcoin amount to be sent (optional, in which case the client asks the | |
user). | |
"target" : another URI (http[s] or http[s]+btc) where the resulting transaction is | |
sent to, called the payment processor. The client may warn the user if no | |
authenticated service is used. | |
"maxfee" : maximum amount of txfee the payee is willing to pay himself. If amount is A, | |
and maxfee is M, a txout value of A-F is allowed if F<=fee and F<=M. This | |
field is optional and defaults to 0. | |
"before" : UNIX timestamp before which the payment must be submitted, as determined by the | |
payment processor. This allows freshly generated keys to be reused if no payment | |
arrives. | |
"note" : string with information for the user, to be shown by the client. Optional. | |
"_..." : any field prefixed with "_" is private information for the payment | |
processor. This can be used to encode an item id, an order numner, a | |
customer id, ... | |
} | |
The script can be anything. Typical scripts will probably be based on OP_CHECKSIG ( | |
with or without OP_HASH160), as normal send-to-address or send-to-pubkey transactions | |
do, but there is no such requirement. The script may use arbitrarily complex contract- | |
like scripts. The client does not need to care what this script contains. | |
3. PAYMENT PROCESSORS | |
--------------------- | |
Upon receiving a payment descriptor, the wallet client asks the user for confirmation, | |
showing the note, and possibly asking for an amount (eg. in the case of donation). | |
When this is acknowledged by the user, a full bitcoin transaction is constructed that | |
satisfies the conditions defined by the descriptor (correct txout script, correct | |
amount). | |
This transaction is not required to be broadcast through the bitcoin network, but | |
instead is sent to a payment processor. The payment processor is responsible for | |
getting the transaction included in the block chain. A POST to the payment | |
processor again contains a JSON object, this time with fields: | |
{ | |
"desc" : the original bitcoin payment description object | |
"tx" : base64-encoded transaction fulfilling the payment | |
"refund" : another bitcoin address (http[s][+btc] or static) a refund for | |
this transaction could be sent to. Optional. | |
} | |
When receiving such a payment, the payment processor does several checks: | |
* The transaction is valid and is no (obvious) double spend | |
* The transaction satisfies the requirements set by the descriptor provided | |
* The description is authentic (i.e., corresponds to a real payment requested | |
or accepted). If the payment processor is managed separately from the | |
descriptor provider (e.g., different company), this could be done by | |
requiring a separate signature in an "_auth" field, by a key that is known | |
to the payment processor. | |
* As an optional service to the payer, check whether no previous payment for | |
the given description was already accepted, or in the case the descriptor | |
is per-customer-account instead of per-item, that sufficient outstanding | |
debt remains. | |
Based on this, the payment processor replies (signed!): | |
{ | |
"desc" : again, the original payment description | |
"tx" : again, the transaction | |
"state" : the state the payment is in | |
"error" : human-readable error code (optional) | |
} | |
The state is either: | |
* "accepted" : the transaction is accepted as payment. This response, | |
including its signatures, can be stored by the client as proof that the | |
item was paid. | |
* "pending" : the transaction and description are fully valid, but the | |
payment is not yet included / confirmed enough to be accepted. | |
* "retry" : the transaction and description may or may not be valid, but | |
the transaction is not yet broadcast, and won't be broadcast without | |
resubmission. | |
* "rejected" : the transaction or description are not valid. The payment | |
processor promises not to broadcast the transaction. | |
If the state is "accepted" or "rejected", further retransmissions of the | |
same payment should result in the same state. If the state is "pending", | |
resubmissions should after a finite time result in "accepted". Only in | |
case a double-spend is detected in the block chain, is it allowed to | |
turn into "rejected". | |
4. ADVANTAGES | |
------------- | |
* Wallet clients do not need a full client or even up-to-date block chain. | |
A smartphone app with just some preloaded coins could very well | |
request payment descriptions, and submit payments. As an extension, | |
not-too-complex payment descriptions could be sent through QR codes, or | |
NFC. Furthermore, as payment descriptors are simple text files, they can | |
be communicated in other ways as well, such as as an e-mail attachment, | |
or encoded in a URL directly (e.g., data URI scheme). | |
* E-wallet services could provide a payment URI for donations or personal | |
payments, guaranteeing unique pubkeys for each payment, and without | |
the risk of sending coins into the void (an honest e-wallet provider | |
would stop responding to user-specific payment URI's if the account is | |
closed, for example). | |
A URI like https+btc://wallet.foo/payto/username?comment could be | |
used for a description that contains <comment> in a private field, which | |
causes it to remain linked to the transaction as it is sent around. | |
Such a URI could be stored inside a wallet client's address book. | |
* Transparently supports complex transactions (e.g., contracts), as the | |
txout script in payment descriptions is a black box. | |
* Customers can get rid of the burden of transaction fees: the merchant can | |
transparently offer to compensate those. | |
* Could be integrated with "offline" wallets, where the payment | |
description provider is separate from the entity able to spend the received | |
funds. The description provider knows the owner's public key P (corresponding | |
to private key p), and generates a unique random private key q for each | |
payment, returning q*P as public key in the txout script. For each confirmed | |
payment, the provider gives q to the owner, who can do payments using p*q as | |
private key (after recovering p from possibly offline secure storage). | |
Alternatively, a 2-out-of-2 multisig transaction can be used. | |
* Client applications can track payments instead of bitcoin transactions, | |
showing the user which payments are accepted/pending (as determined by the | |
receiver or payment processor). | |
5. EXAMPLE | |
---------- | |
* I buy an item on webshop.foo. It gives me the link | |
https+btc://webshop.foo/payment/order1234.btc#1KaL5oSnKYgNe9sQS9qy6dR2zso6WSAh3F | |
* My browser forwards the URI to my wallet application, which fetches the signed | |
payment descriptor: | |
{ | |
"script" : "AQ==", /* we're very stupid and ask you to do a spend-to-any */ | |
"amount" : "123.45", | |
"target" : "https+btc://processor.foo/process#185VeiuMcHaL7XTsvYqzQPkFYkafE52hXe", | |
"maxfee" : "0.01", | |
"note" : "Order 1234 on webshop.foo: 1x Radeon HD6990", | |
"_order" : 1234, | |
"_auth" : "KJAGJ41YT6M156NB4JHF" | |
} | |
* I confirm the transaction in my wallet application, which constructs a full bitcoin | |
transaction with the funds I have, potentially containing a change output to myself. | |
* The wallet application submits to processor.foo: | |
{ | |
"desc" : { | |
"script" : ..., | |
... | |
}, | |
"tx" : "ORvxwTNJIQ+cx0eI1PB+5QUdkqA5Q2ykK7YHAXrA53OrfC+RVB9X6DI+hMKmHxC40fp3YkF7oJmn", | |
"refund" : "19qL4o3nKKgKe8sUS9pq6dP2zso5WSQh3E", // my personal static address | |
} | |
* As the transaction is valid, doesn't seem to be a double spend, but is too large to | |
be cleared immediately, processor.foo answers: | |
{ | |
"desc" : ..., | |
"tx" : ..., | |
"state" : "pending" | |
} | |
* My wallet application retries after a few hours. In the mean time the payment processor | |
broadcast the transaction, and got it into the block chain with several confirmations. | |
I get: | |
{ | |
"desc" : ..., | |
"tx" : ..., | |
"state" : "accepted" | |
} | |
* In the mean time, the payment processor notifies the merchant that the payment for item | |
1234 has been cleared. | |
6. REMARKS | |
---------- | |
* What if the transmission to the payment processor fails after the transaction was | |
submitted? The client should keep retrying, but what if it has to give up? The | |
transaction may have been received succesfully, and broadcast. | |
* Maybe the transmission to the payment processor should be done encrypted | |
(ECIES+AES), so that the included transaction cannot be broadcast by anyone except | |
the payment processor (who has a reputation to lose if he doesn't obey the rules). | |
* Timestamps/nonces necessary to prevent replay attacks? | |
* If https is not used, or if no certificate checking is done, the entire scheme is | |
vulnerable to a man-in-the-middle attack. This can be prevented by integrating | |
the key passed in X-Bitcoin-Authenticate-As: in the key agreement protocol (either | |
SSL, or custom implementation). This does seem a minor issue however, as the payment | |
descriptor itself is signed by the pre-exchanged key anyway. | |
* When using https+btc:// links for both the payment descriptor and processor, the client | |
has a completely signed 'path' of messages (descriptor + signature that authorize a | |
specific key to declare payments accepted, and a signed acceptance object with that | |
key). However, everything depends on the authenticity of the static address part of the | |
original payment descriptor. These need to come from somewhere, and we don't want to | |
recreate a public key infrastructure that relies on a centralized root. | |
* This draft specifies the use of http (and http-btc) to retrieve the payment descriptors; as these are merely JSON-encoded text files, they can be communicated in a less automated way as well, such as |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
A bit of a high-level reasoning behind it I wrote on the bitcoin-dev mailing list:
What current addresses are, is a reference to a public key. The way they
are used is as a template for a transaction. If you do not need complex
transactions, this suffices indeed, given that all other negotiation about
the payment occurs out-of-band already (e.g., a webshop interface that
after clicking 'pay' gives you a freshly generated bitcoin address and
stores it so it can track your payment).
What I want to do is to standardize part of that out-of-band communication
inside a protocol. The first observation is that if you want a freshly
negotiated key each time, some form of bidirectional communication is
necessary anyway, and a static txout template does not suffice anymore.
If you're doing bidirectional communication, you are no longer limited
by the space constraints of something by-human-copy-pastable, and you can
just negotiate the txout directly, which transparently adds support for
anything that is possible through bitcoin scripts.
So far, the creation of transactions is "solved". However, by asking nodes
not to broadcast their transaction, but instead just send it back (we're
communicating with some other party already anyway, and this other party
is the one who cares about the tx being accepted), the receiver can track
it as well. Furthermore, by passing tags along, identification of
transactions becomes a lot easier. As a extra advantage, this makes the
requirements for a client easier as well (it doesn't need to be a p2p
node).
The third step is adding signatures to authenticate the whole process.
They are necessary to make sure the client is communicating with who he
thinks he is, but by using them for the submission of the transaction as
well, it gives the client a proof of payment acceptance too.
Summarized: addresses are a limited method for defining payments, and as
soon as you move to a protocol instead of a static template, a lot of
possibilities open up.