{|
! Field !! Size [B] !! Description
|-
| <version> || 2 || Version, little endian, currently 0x01 0x00
|-
| <txid> || 32 || The transaction to prove
|-
| <nonce> || 6 || Random data
|}
The lock_time of the PoP must be set to 499999999 to prevent the PoP from being included in a block, should it appear on the bitcoin p2p network. This is also the reason for setting the sequence numbers to 0, since sequence number of ffffffff would make lock_time ineffective. This specification demands that all input sequence numbers are 0, not just one of them, which would be sufficient to make lock_time effective. This is for simplicity reasons.
An illustration of the PoP data structure and its original payment is shown below.
T
+------------------------------------------------+
|inputs | outputs |
| Value,Sequence | Value,Script |
+------------------------------------------------+
|input0 1,ffffffff | 0,pay to A |
|input1 3,ffffffff | 2,OP_RETURN |
|input2 4,ffffffff | 1,pay to B |
| | 4,pay to C |
+------------------------------------------------+
PoP(T)
+-------------------------------------------------------------+
| inputs | outputs |
| Value,Sequence | Value,Script |
+-------------------------------------------------------------+
|input0 1,00000000 | 0,OP_RETURN |
|input1 3,00000000 | |
|input2 4,00000000 | |
+-------------------------------------------------------------+
| lock_time=499999999 |
+-------------------------------------------------------------+
The PoP is signed using the same signing process that is used for bitcoin transactions.
The purpose of the nonce is to make it harder to use a stolen PoP; Once the PoP has reached the server, that PoP is useless since the server will generate a new nonce for every PoP request.
=== Process ===
# A proof of payment request is sent from the server to the wallet. The PoP request contains:
## a random nonce
## a destination where to send the PoP, for example a https URL
## data hinting the wallet which transaction to create a proof for. For example:
##* txid, if known by the server
##* PaymentRequest.PaymentDetails.merchant_data (in case of a BIP0070 payment)
##* amount, label, message or other information from a BIP0021 URI
# The wallet identifies a transaction T, if possible. Otherwise it asks the user to select among the ones that match the hints in 1.iii.
# The wallet creates an unsigned PoP (UPoP) for T, and asks the user to sign it.
# The user confirms
# The UPoP(T) is signed by the wallet, creating PoP(T).
# The PoP is sent to the destination in 1.ii.
# The server receiving the PoP validates it and responds with “valid” or “invalid”.
# The wallet displays the response in some way to the user.
'''Remarks:'''
* The method of transferring the PoP request at step 1 is not specified here. Instead that is specified in separate specifications, see BIP0121.
* The nonce must be randomly generated by the server for every new PoP request.
=== Validating a PoP ===
The server needs to validate the PoP and reply with "valid" or "invalid". That process is outlined below. If any step fails, the validation is aborted and "invalid" is returned:
# Check the format of the PoP. It must pass normal transaction checks, except that the inputs may already be spent.
# Check that lock_time is 499999999.
# Check that there is exactly one output. This output must have value 0 and conform to the OP_RETURN output format outlined above.
# Check that the nonce is the same as the one requested.
# Check that the inputs of the PoP are exactly the same as in transaction T, except that the sequence numbers must all be 0. The ordering of the inputs must also be the same as in T.
# Run the scripts of all the inputs. All scipts must return true.
# Check that the txid in the PoP output is the transaction you actually want proof for. If you don’t know exactly what transaction you want proof for, check that the transaction actually pays for the product/service you deliver.
# Return "valid".
== Security considerations ==
* Someone can intercept the PoP-request and change any parameter in it. These can be mitigated by using secure connections. Examples of tampered parameters:
** Pop destination - Stealing your PoP.
** label - Trick you to sign an unintended pop or set a label that your wallet doesn't have any record for, resulting in a broken service. Always check the PoP before signing.
** nonce - Your pop will not validate on server.
* Someone can steal a PoP, for example by tampering with the PoP request, and try to use the service hoping to get a matching nonce. Probability per try: 1/(2^48). The server should have a mechanism for detecting a brute force attack of this kind, or at least slow down the process by delaying the PoP request by some 100 ms or so.
* Even if a wallet has no funds it might still be valuable as a generator for PoPs. This makes it important to keep the security of the wallet after it has been emptied.
* Transaction malleability may cause the server to have another transaction id for a payment than the client's wallet. In that case the wallet will not be able to prove the transaction to the server. Wallets should not rely on the transaction id of the outgoing transaction. Instead it should listen for the transaction on the network and put that in its list of transactions.
== Reference implementation ==
[https://github.com/kallerosenbaum/poppoc PoP Demo server on GitHub]
[https://github.com/kallerosenbaum/wallet PoP-enabled Mycelium fork on GitHub]
== References ==
[https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki BIP0021]: URI Scheme
[https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki BIP0070]: Payment Protocol
[https://github.com/bitcoin/bips/blob/master/bip-0121.mediawiki BIP0121]: Proof of Payment URI scheme