Skip to content

Instantly share code, notes, and snippets.

@sipa
Created September 23, 2011 16:19
Show Gist options
  • Save sipa/1237788 to your computer and use it in GitHub Desktop.
Save sipa/1237788 to your computer and use it in GitHub Desktop.
Beyond IP transactions: towards a Bitcoin payment protocol
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
@sipa
Copy link
Author

sipa commented Sep 24, 2011

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment