mirror of
https://github.com/seigler/dash-docs
synced 2025-07-27 09:46:12 +00:00
Merge remote-tracking branch 'harding/generatingtxes'
This commit is contained in:
commit
b428cbde26
18 changed files with 2135 additions and 755 deletions
|
@ -127,14 +127,11 @@ parent key:
|
|||
parent private key:
|
||||
parent private and public keys: parent key
|
||||
parent public key:
|
||||
Payment message: pp payment
|
||||
payment protocol:
|
||||
"payment protocol's": payment protocol
|
||||
PaymentACK:
|
||||
PaymentDetails:
|
||||
PaymentRequest:
|
||||
PaymentRequests: paymentrequest
|
||||
'`payment_url`': pp payment url
|
||||
peer:
|
||||
peers: peer
|
||||
peer-to-peer network: network
|
||||
|
@ -160,7 +157,6 @@ recurrent rebilling:
|
|||
redeemScript:
|
||||
refund:
|
||||
refunds: refund
|
||||
'`refund_to`': pp refund to
|
||||
root certificate:
|
||||
root seed:
|
||||
RPCs: rpc
|
||||
|
@ -222,6 +218,8 @@ BIP21:
|
|||
BIP32:
|
||||
BIP39:
|
||||
BIP70:
|
||||
BIP71:
|
||||
BIP72:
|
||||
|
||||
## RPCs
|
||||
'`addmultisigaddress`': rpc addmultisigaddress
|
||||
|
|
455
_includes/example_payment_processing.md
Normal file
455
_includes/example_payment_processing.md
Normal file
|
@ -0,0 +1,455 @@
|
|||
## Payment Processing
|
||||
|
||||
### Payment Protocol
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
To request payment using the payment protocol, you use an extended (but
|
||||
backwards-compatible) `bitcoin:` URI. For example:
|
||||
|
||||
~~~
|
||||
bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN\
|
||||
?amount=0.10\
|
||||
&label=Example+Merchant\
|
||||
&message=Order+of+flowers+%26+chocolates\
|
||||
&r=https://example.com/pay.php/invoice%3Dda39a3ee
|
||||
~~~
|
||||
|
||||
The browser, QR code reader, or other program processing the URI opens
|
||||
the spender's Bitcoin wallet program on the URI. If the wallet program is
|
||||
aware of the payment protocol, it accesses the URL specified in the `r`
|
||||
parameter, which should provide it with a serialized PaymentRequest
|
||||
served with the [MIME][] type `application/bitcoin-paymentrequest`<!--noref-->.
|
||||
|
||||
**Resource:** Gavin Andresen's [Payment Request Generator][] generates
|
||||
custom example URIs and payment requests for use with testnet.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
#### PaymentRequest & PaymentDetails
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
The [PaymentRequest][]{:#term-paymentrequest}{:.term} is created with data structures built using
|
||||
Google's Protocol Buffers. BIP70 describes these data
|
||||
structures in the non-sequential way they're defined in the payment
|
||||
request protocol buffer code, but the text below will describe them in
|
||||
a more linear order using a simple (but functional) Python CGI
|
||||
program. (For brevity and clarity, many normal CGI best practices are
|
||||
not used in this program.)
|
||||
|
||||
The full sequence of events is illustrated below, starting with the
|
||||
spender clicking a `bitcoin:` URI or scanning a `bitcoin:` QR code.
|
||||
|
||||

|
||||
|
||||
For the script to use the protocol buffer, you will need a copy of
|
||||
Google's Protocol Buffer compiler (`protoc`), which is available in most
|
||||
modern Linux package managers and [directly from Google.][protobuf] Non-Google
|
||||
protocol buffer compilers are available for a variety of
|
||||
programming languages. You will also need a copy of the PaymentRequest
|
||||
[Protocol Buffer description][core paymentrequest.proto] from the Bitcoin Core source code.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
##### Initialization Code
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
With the Python code generated by `protoc`, we can start our simple
|
||||
CGI program.
|
||||
|
||||
{% highlight python %}
|
||||
#!/usr/bin/env python
|
||||
|
||||
## This is the code generated by protoc --python_out=./ paymentrequest.proto
|
||||
from paymentrequest_pb2 import *
|
||||
|
||||
## Load some functions
|
||||
from time import time
|
||||
from sys import stdout
|
||||
from OpenSSL.crypto import FILETYPE_PEM, load_privatekey, sign
|
||||
|
||||
## Copy three of the classes created by protoc into objects we can use
|
||||
details = PaymentDetails()
|
||||
request = PaymentRequest()
|
||||
x509 = X509Certificates()
|
||||
{% endhighlight %}
|
||||
|
||||
The startup code above is quite simple, requiring nothing but the epoch
|
||||
(Unix date) time function, the standard out file descriptor, a few
|
||||
functions from the OpenSSL library, and the data structures and
|
||||
functions created by `protoc`.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
##### Configuration Code
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
Next, we'll set configuration settings which will typically only change
|
||||
when the receiver wants to do something differently. The code pushes a
|
||||
few settings into the `request` (PaymentRequest) and `details`
|
||||
(PaymentDetails) objects. When we serialize them,
|
||||
[PaymentDetails][]{:#term-paymentdetails}{:.term} will be contained
|
||||
within the PaymentRequest.
|
||||
|
||||
{% highlight python %}
|
||||
## SSL Signature method
|
||||
request.pki_type = "x509+sha256" ## Default: none
|
||||
|
||||
## Mainnet or Testnet?
|
||||
details.network = "test" ## Default: main
|
||||
|
||||
## Postback URL
|
||||
details.payment_url = "https://example.com/pay.py"
|
||||
|
||||
## PaymentDetails version number
|
||||
request.payment_details_version = 1 ## Default: 1
|
||||
|
||||
## Certificate chain
|
||||
x509.certificate.append(file("/etc/apache2/example.com-cert.der", "r").read())
|
||||
#x509.certificate.append(file("/some/intermediate/cert.der", "r").read())
|
||||
|
||||
## Load private SSL key into memory for signing later
|
||||
priv_key = "/etc/apache2/example.com-key.pem"
|
||||
pw = "test" ## Key password
|
||||
private_key = load_privatekey(FILETYPE_PEM, file(priv_key, "r").read(), pw)
|
||||
{% endhighlight %}
|
||||
|
||||
Each line is described below.
|
||||
|
||||
{% highlight python %}
|
||||
request.pki_type = "x509+sha256" ## Default: none
|
||||
{% endhighlight %}
|
||||
|
||||
`pki_type`: (optional) tell the receiving wallet program what [Public-Key
|
||||
Infrastructure][PKI]{:#term-pki}{:.term} (PKI) type you're using to
|
||||
cryptographically sign your PaymentRequest so that it can't be modified
|
||||
by a man-in-the-middle attack.
|
||||
|
||||
If you don't want to sign the PaymentRequest, you can choose a
|
||||
[`pki_type`][pp pki type]{:#term-pp-pki-type}{:.term} of `none`
|
||||
(the default).
|
||||
|
||||
If you do choose the sign the PaymentRequest, you currently have two
|
||||
options defined by BIP70: `x509+sha1` and `x509+sha256`. Both options
|
||||
use the X.509 certificate system, the same system used for HTTP Secure
|
||||
(HTTPS). To use either option, you will need a certificate signed by a
|
||||
certificate authority or one of their intermediaries. (A self-signed
|
||||
certificate will not work.)
|
||||
|
||||
Each wallet program may choose which certificate authorities to trust,
|
||||
but it's likely that they'll trust whatever certificate authorities their
|
||||
operating system trusts. If the wallet program doesn't have a full
|
||||
operating system, as might be the case for small hardware wallets, BIP70
|
||||
suggests they use the [Mozilla Root Certificate Store][mozrootstore]. In
|
||||
general, if a certificate works in your web browser when you connect to
|
||||
your webserver, it will work for your PaymentRequests.
|
||||
|
||||
{% highlight python %}
|
||||
details.network = "test" ## Default: main
|
||||
{% endhighlight %}
|
||||
|
||||
`network`:<!--noref--> (optional) tell the spender's wallet program what Bitcoin network you're
|
||||
using; BIP70 defines "main" for mainnet (actual payments) and "test" for
|
||||
testnet (like mainnet, but fake satoshis are used). If the wallet
|
||||
program doesn't run on the network you indicate, it will reject the
|
||||
PaymentRequest.
|
||||
|
||||
{% highlight python %}
|
||||
details.payment_url = "https://example.com/pay.py"
|
||||
{% endhighlight %}
|
||||
|
||||
`payment_url`: (required) tell the spender's wallet program where to send the Payment
|
||||
message (described later). This can be a static URL, as in this example,
|
||||
or a variable URL such as `https://example.com/pay.py?invoice=123.`
|
||||
It should usually be an HTTPS address to prevent man-in-the-middle
|
||||
attacks from modifying the message.
|
||||
|
||||
{% highlight python %}
|
||||
request.payment_details_version = 1 ## Default: 1
|
||||
{% endhighlight %}
|
||||
|
||||
`payment_details_version`: (optional) tell the spender's wallet program what version of the
|
||||
PaymentDetails you're using. As of this writing, the only version is
|
||||
version 1.
|
||||
|
||||
{% highlight python %}
|
||||
## This is the pubkey/certificate corresponding to the private SSL key
|
||||
## that we'll use to sign:
|
||||
x509.certificate.append(file("/etc/apache2/example.com-cert.der", "r").read())
|
||||
{% endhighlight %}
|
||||
|
||||
`x509certificates`:<!--noref--> (required for signed PaymentRequests) you must
|
||||
provide the public SSL key/certificate corresponding to the private SSL
|
||||
key you'll use to sign the PaymentRequest. The certificate must be in
|
||||
ASN.1/DER format.
|
||||
|
||||
{% highlight python %}
|
||||
## If the pubkey/cert above didn't have the signature of a root
|
||||
## certificate authority, we'd then append the intermediate certificate
|
||||
## which signed it:
|
||||
#x509.certificate.append(file("/some/intermediate/cert.der", "r").read())
|
||||
{% endhighlight %}
|
||||
|
||||
You must also provide any intermediate certificates necessary to link
|
||||
your certificate to the root certificate of a certificate authority
|
||||
trusted by the spender's software, such as a certificate from the
|
||||
Mozilla root store.
|
||||
|
||||
The certificates must be provided in a specific order---the same order
|
||||
used by Apache's `SSLCertificateFile` directive and other server
|
||||
software. The figure below shows the [certificate chain][]{:#term-certificate-chain}{:.term} of the
|
||||
www.bitcoin.org X.509 certificate and how each certificate (except the
|
||||
root certificate) would be loaded into the [X509Certificates][]{:#term-x509certificates}{:.term} protocol
|
||||
buffer message.
|
||||
|
||||

|
||||
|
||||
To be specific, the first certificate provided must be the
|
||||
X.509 certificate corresponding to the private SSL key which will make the
|
||||
signature<!--noref-->, called the [leaf certificate][]{:#term-leaf-certificate}{:.term}. Any [intermediate
|
||||
certificates][intermediate certificate]{:#term-intermediate-certificate}{:.term} necessary to link that signed public SSL
|
||||
key to the [root
|
||||
certificate][]{:#term-root-certificate}{:.term} (the certificate authority) are attached separately, with each
|
||||
certificate in DER format bearing the signature<!--noref--> of the certificate that
|
||||
follows it all the way to (but not including) the root certificate.
|
||||
|
||||
{% highlight python %}
|
||||
priv_key = "/etc/apache2/example.com-key.pem"
|
||||
pw = "test" ## Key password
|
||||
private_key = load_privatekey(FILETYPE_PEM, file(priv_key, "r").read(), pw)
|
||||
{% endhighlight %}
|
||||
|
||||
(Required for signed PaymentRequests) you will need a private SSL key in
|
||||
a format your SSL library supports (DER format is not required). In this
|
||||
program, we'll load it from a PEM file. (Embedding your passphrase in
|
||||
your CGI code, as done here, is obviously a bad idea in real life.)
|
||||
|
||||
The private SSL key will not be transmitted with your request. We're
|
||||
only loading it into memory here so we can use it to sign the request
|
||||
later.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
##### Code Variables
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
Now let's look at the variables your CGI program will likely set for
|
||||
each payment.
|
||||
|
||||
{% highlight python %}
|
||||
## Amount of the request
|
||||
amount = 10000000 ## In satoshis
|
||||
|
||||
## P2PKH pubkey hash
|
||||
pubkey_hash = "2b14950b8d31620c6cc923c5408a701b1ec0a020"
|
||||
## P2PKH output script entered as hex and converted to binary
|
||||
# OP_DUP OP_HASH160 <push 20 bytes> <pubKey hash> OP_EQUALVERIFY OP_CHECKSIG
|
||||
# 76 a9 14 <pubKey hash> 88 ac
|
||||
hex_script = "76" + "a9" + "14" + pubkey_hash + "88" + "ac"
|
||||
serialized_script = hex_script.decode("hex")
|
||||
|
||||
## Load amount and script into PaymentDetails
|
||||
details.outputs.add(amount = amount, script = serialized_script)
|
||||
|
||||
## Memo to display to the spender
|
||||
details.memo = "Flowers & chocolates"
|
||||
|
||||
## Data which should be returned to you with the payment
|
||||
details.merchant_data = "Invoice #123"
|
||||
{% endhighlight python %}
|
||||
|
||||
Each line is described below.
|
||||
|
||||
{% highlight python %}
|
||||
amount = 10000000 ## In satoshis (=100 mBTC)
|
||||
{% endhighlight %}
|
||||
|
||||
`amount`: (optional) the [amount][pp amount]{:#term-pp-amount}{:.term} you want the spender to pay. You'll probably get
|
||||
this value from your shopping cart application or fiat-to-BTC exchange
|
||||
rate conversion tool. If you leave the amount blank, the wallet
|
||||
program will prompt the spender how much to pay (which can be useful
|
||||
for donations).
|
||||
|
||||
{% highlight python %}
|
||||
pubkey_hash = "2b14950b8d31620c6cc923c5408a701b1ec0a020"
|
||||
# OP_DUP OP_HASH160 <push 20 bytes> <pubKey hash> OP_EQUALVERIFY OP_CHECKSIG
|
||||
# 76 a9 14 <pubKey hash> 88 ac
|
||||
hex_script = "76" + "a9" + "14" + pubkey_hash + "88" + "ac"
|
||||
serialized_script = hex_script.decode("hex")
|
||||
{% endhighlight %}
|
||||
|
||||
`script`: (required) You must specify the output script you want the spender to
|
||||
pay---any valid script is acceptable. In this example, we'll request
|
||||
payment to a P2PKH output script.
|
||||
|
||||
First we get a pubkey hash. The hash above is the hash form of the
|
||||
address used in the URI examples throughout this section,
|
||||
mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN.
|
||||
|
||||
Next, we plug that hash into the standard P2PKH output script using hex,
|
||||
as illustrated by the code comments.
|
||||
|
||||
Finally, we convert the output script from hex into its serialized form.
|
||||
|
||||
{% highlight python %}
|
||||
details.outputs.add(amount = amount, script = serialized_script)
|
||||
{% endhighlight %}
|
||||
|
||||
`outputs`:<!--noref--> (required) add the output script and (optional) amount to the
|
||||
PaymentDetails outputs<!--noref--> array.
|
||||
|
||||
It's possible to specify multiple [`scripts`][pp
|
||||
script]{:#term-pp-script}{:.term} and `amounts` as part of a merge
|
||||
avoidance strategy, described later in the [Merge Avoidance
|
||||
subsection][]. However, effective merge avoidance is not possible under
|
||||
the base BIP70 rules in which the spender pays each `script` the exact
|
||||
amount specified by its paired `amount`. If the amounts are omitted from
|
||||
all `amount`/`script` pairs, the spender will be prompted to choose an
|
||||
amount to pay.
|
||||
|
||||
{% highlight python %}
|
||||
details.memo = "Flowers & chocolates"
|
||||
{% endhighlight %}
|
||||
|
||||
`memo`: (optional) add a memo which will be displayed to the spender as
|
||||
plain UTF-8 text. Embedded HTML or other markup will not be processed.
|
||||
|
||||
{% highlight python %}
|
||||
details.merchant_data = "Invoice #123"
|
||||
{% endhighlight %}
|
||||
|
||||
`merchant_data`: (optional) add arbitrary data which should be sent back to the
|
||||
receiver when the invoice is paid. You can use this to track your
|
||||
invoices, although you can more reliably track payments by generating a
|
||||
unique address for each payment and then tracking when it gets paid.
|
||||
|
||||
The [`memo`][pp memo]{:#term-pp-memo}{:.term} field and the [`merchant_data`][pp merchant data]{:#term-pp-merchant-data}{:.term} field can be arbitrarily long,
|
||||
but if you make them too long, you'll run into the 50,000 byte limit on
|
||||
the entire PaymentRequest, which includes the often several kilobytes
|
||||
given over to storing the certificate chain. As will be described in a
|
||||
later subsection, the `memo` field can be used by the spender after
|
||||
payment as part of a cryptographically-proven receipt.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
##### Derivable Data
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
Next, let's look at some information your CGI program can
|
||||
automatically derive.
|
||||
|
||||
{% highlight python %}
|
||||
## Request creation time
|
||||
details.time = int(time()) ## Current epoch (Unix) time
|
||||
|
||||
## Request expiration time
|
||||
details.expires = int(time()) + 60 * 10 ## 10 minutes from now
|
||||
|
||||
## PaymentDetails is complete; serialize it and store it in PaymentRequest
|
||||
request.serialized_payment_details = details.SerializeToString()
|
||||
|
||||
## Serialized certificate chain
|
||||
request.pki_data = x509.SerializeToString()
|
||||
|
||||
## Initialize signature field so we can sign the full PaymentRequest
|
||||
request.signature = ""
|
||||
|
||||
## Sign PaymentRequest
|
||||
request.signature = sign(private_key, request.SerializeToString(), "sha256")
|
||||
{% endhighlight %}
|
||||
|
||||
Each line is described below.
|
||||
|
||||
{% highlight python %}
|
||||
details.time = int(time()) ## Current epoch (Unix) time
|
||||
{% endhighlight %}
|
||||
|
||||
`time`: (required) PaymentRequests must indicate when they were created
|
||||
in number of seconds elapsed since 1970-01-01T00:00 UTC (Unix
|
||||
epoch time format).
|
||||
|
||||
{% highlight python %}
|
||||
details.expires = int(time()) + 60 * 10 ## 10 minutes from now
|
||||
{% endhighlight %}
|
||||
|
||||
`expires`: (optional) the PaymentRequest may also set an [`expires`][pp
|
||||
expires]{:#term-pp-expires}{:.term} time after
|
||||
which they're no longer valid. You probably want to give receivers
|
||||
the ability to configure the expiration time delta; here we used the
|
||||
reasonable choice of 10 minutes. If this request is tied to an order
|
||||
total based on a fiat-to-satoshis exchange rate, you probably want to
|
||||
base this on a delta from the time you got the exchange rate.
|
||||
|
||||
{% highlight python %}
|
||||
request.serialized_payment_details = details.SerializeToString()
|
||||
{% endhighlight %}
|
||||
|
||||
`serialized_payment_details`: (required) we've now set everything we need to create the
|
||||
PaymentDetails, so we'll use the SerializeToString function from the
|
||||
protocol buffer code to store the PaymentDetails in the appropriate
|
||||
field of the PaymentRequest.
|
||||
|
||||
{% highlight python %}
|
||||
request.pki_data = x509.SerializeToString()
|
||||
{% endhighlight %}
|
||||
|
||||
`pki_data`: (required for signed PaymentRequests) serialize the certificate chain
|
||||
[PKI data][pp PKI data]{:#term-pp-pki-data}{:.term} and store it in the
|
||||
PaymentRequest
|
||||
|
||||
{% highlight python %}
|
||||
request.signature = ""
|
||||
{% endhighlight %}
|
||||
|
||||
We've filled out everything in the PaymentRequest except the signature,
|
||||
but before we sign it, we have to initialize the signature field by
|
||||
setting it to a zero-byte placeholder.
|
||||
|
||||
{% highlight python %}
|
||||
request.signature = sign(private_key, request.SerializeToString(), "sha256")
|
||||
{% endhighlight %}
|
||||
|
||||
`signature`:<!--noref--> (required for signed PaymentRequests) now we
|
||||
make the [signature][ssl signature]{:#term-ssl-signature}{:.term} by
|
||||
signing the completed and serialized PaymentRequest. We'll use the
|
||||
private key we stored in memory in the configuration section and the
|
||||
same hashing formula we specified in `pki_type` (sha256 in this case)
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
##### Output Code
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
Now that we have PaymentRequest all filled out, we can serialize it and
|
||||
send it along with the HTTP headers, as shown in the code below.
|
||||
|
||||
{% highlight python %}
|
||||
print "Content-Type: application/bitcoin-paymentrequest"
|
||||
print "Content-Transfer-Encoding: binary"
|
||||
print ""
|
||||
{% endhighlight %}
|
||||
|
||||
(Required) BIP71 defines the content types for PaymentRequests,
|
||||
Payments, and PaymentACKs.
|
||||
|
||||
{% highlight python %}
|
||||
file.write(stdout, request.SerializeToString())
|
||||
{% endhighlight %}
|
||||
|
||||
`request`: (required) now, to finish, we just dump out the serialized
|
||||
PaymentRequest (which contains the serialized PaymentDetails). The
|
||||
serialized data is in binary, so we can't use Python's print()
|
||||
because it would add an extraneous newline.
|
||||
|
||||
The following screenshot shows how the authenticated PaymentDetails
|
||||
created by the program above appears in the GUI from Bitcoin Core 0.9.
|
||||
|
||||

|
||||
|
||||
{% endautocrossref %}
|
1270
_includes/example_transactions.md
Normal file
1270
_includes/example_transactions.md
Normal file
File diff suppressed because it is too large
Load diff
7
_includes/fragment_reviews_needed.md
Normal file
7
_includes/fragment_reviews_needed.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
<!--Temporary disclaimer BEGIN-->
|
||||
<div id="develdocdisclaimer" class="develdocdisclaimer"><div>
|
||||
<b>BETA</b>: This documentation has been written recently and still needs more reviews to ensure all content is covered correctly and accurately; if you find a mistake, please <a href="https://github.com/bitcoin/bitcoin.org/issues/new" onmouseover="updateIssue(event);">report an issue</a> on GitHub. <a href="#" onclick="disclaimerClose(event);">Click here</a> to close this disclaimer.
|
||||
<a class="develdocdisclaimerclose" href="#" onclick="disclaimerClose(event);">X</a>
|
||||
</div></div>
|
||||
<script>disclaimerAutoClose();</script>
|
||||
<!--Temporary disclaimer END-->
|
|
@ -150,8 +150,6 @@ or [millibits][]{:#term-millibits}{:.term} (mBTC). Choosing between BTC and mBTC
|
|||
but other software also lets its users select denomination amounts from
|
||||
some or all of the following options:
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
| Bitcoins | Unit (Abbreviation) |
|
||||
|-------------|---------------------|
|
||||
| 1.0 | bitcoin (BTC) |
|
||||
|
@ -160,20 +158,8 @@ some or all of the following options:
|
|||
| 0.000001 | microbit (uBTC) |
|
||||
| 0.00000001 | [satoshi][]{:#term-satoshi}{:.term} |
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
Because of the widespread popularity of BTC and mBTC, it may be more
|
||||
useful to specify the amount in both denominations when the text is
|
||||
meant to be copied and pasted. For example:
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
~~~
|
||||
Pay: mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN
|
||||
Amount: 100 BTC (100000 mBTC)
|
||||
You must pay by: 2014-04-01 at 23:00 UTC
|
||||
~~~
|
||||
|
||||
#### bitcoin: URI
|
||||
|
||||
{% autocrossref %}
|
||||
|
@ -191,38 +177,23 @@ bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN?amount=100
|
|||
|
||||
{% autocrossref %}
|
||||
|
||||
Only the address is required, and if it is the only thing specified,
|
||||
wallets will pre-fill a payment request with it and let the spender enter
|
||||
an amount.
|
||||
Only the address is required, and if it is the only thing
|
||||
specified, wallets will pre-fill a payment request with it and let
|
||||
the spender enter an amount. The amount specified is always in
|
||||
decimal bitcoins (BTC).
|
||||
|
||||
The amount specified is always in decimal bitcoins (BTC), although requests
|
||||
only for whole bitcoins (as in the example above), may omit the decimal
|
||||
point. The amount field must not contain any commas. Fractional bitcoins
|
||||
may be specified with or without a leading zero; for example, either of
|
||||
the URIs below requesting one millibit are valid:
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
~~~
|
||||
bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN?amount=.001
|
||||
bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN?amount=0.001
|
||||
~~~
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
Two other parameters are widely supported. The [`label`][label]{:#term-label}{:.term} parameter is
|
||||
generally used to provide wallet software with the recipient's name. The
|
||||
[`message`][message]{:#term-message}{:.term} parameter is generally used to describe the payment request to
|
||||
the spender. Both the label and the message are commonly stored by the
|
||||
spender's wallet software---but they are never added to the actual
|
||||
transaction, so other Bitcoin users cannot see them. Both the label and
|
||||
the message must be [URI encoded][].
|
||||
Two other parameters are widely supported. The
|
||||
[`label`][label]{:#term-label}{:.term} parameter is generally used to
|
||||
provide wallet software with the recipient's name. The
|
||||
[`message`][message]{:#term-message}{:.term} parameter is generally used
|
||||
to describe the payment request to the spender. Both the label and the
|
||||
message are commonly stored by the spender's wallet software---but they
|
||||
are never added to the actual transaction, so other Bitcoin users cannot
|
||||
see them. Both the label and the message must be [URI encoded][].
|
||||
|
||||
All four parameters used together, with appropriate URI encoding, can be
|
||||
seen in the line-wrapped example below.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
~~~
|
||||
bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN\
|
||||
?amount=0.10\
|
||||
|
@ -230,32 +201,6 @@ bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN\
|
|||
&message=Order+of+flowers+%26+chocolates
|
||||
~~~
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
The URI above could be encoded in HTML as follows, providing compatibility
|
||||
with wallet software which can't accept URI links and allowing you to
|
||||
specify an expiration date to the spender.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
~~~
|
||||
<a href="bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN\
|
||||
?amount=0.10\
|
||||
&label=Example+Merchant\
|
||||
&message=Order+of+flowers+%26+chocolates"
|
||||
>Order flowers & chocolate using Bitcoin</a>
|
||||
(Pay 0.10 BTC [100 mBTC] to mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN by 2014-04-01 at 23:00 UTC)
|
||||
~~~
|
||||
|
||||
Which produces:
|
||||
|
||||
> <a href="bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN?amount=0.10&label=Example+Merchant&message=Order+of+flowers+%26+chocolates">Order flowers & chocolates using Bitcoin</a> (Pay 0.10 BTC [100 mBTC] to mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN by 2014-04-01 at 23:00 UTC)
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
Some payment processors use Javascript to display countdown timers
|
||||
indicating the number of minutes and seconds until the offer expires.
|
||||
|
||||
The URI scheme can be extended, as will be seen in the payment protocol
|
||||
section below, with both new optional and required parameters. As of this
|
||||
writing, the only widely-used parameter besides the four described above
|
||||
|
@ -276,27 +221,14 @@ images, or in videos. Most mobile Bitcoin wallet apps, and some desktop
|
|||
wallets, support scanning QR codes to pre-fill their payment screens.
|
||||
|
||||
The figure below shows the same `bitcoin:` URI code encoded as four
|
||||
different [Bitcoin QR codes][URI QR code]{:#term-uri-qr-code}{:.term} at different error correction levels (described
|
||||
below the image). The QR code can include the `label` and `message`
|
||||
different [Bitcoin QR codes][URI QR code]{:#term-uri-qr-code}{:.term} at four
|
||||
different error correction levels. The QR code can include the `label` and `message`
|
||||
parameters---and any other optional parameters---but they were
|
||||
omitted here to keep the QR code small and easy to scan with unsteady
|
||||
or low-resolution mobile cameras.
|
||||
|
||||

|
||||
|
||||
QR encoders offer four possible levels of error correction:
|
||||
|
||||
1. Low: corrects up to 7% damage
|
||||
|
||||
2. Medium: corrects up to 15% damage but results in approximately 8%
|
||||
larger images over low-level damage correction.
|
||||
|
||||
3. Quartile: corrects corrects up to 25% damage but results in
|
||||
approximately 20% larger images over low-level damage correction.
|
||||
|
||||
4. High: corrects up to 30% damage but results in approximately 26%
|
||||
larger images over low-level damage correction.
|
||||
|
||||
The error correction is combined with a checksum to ensure the Bitcoin QR code
|
||||
cannot be successfully decoded with data missing or accidentally altered,
|
||||
so your applications should choose the appropriate level of error
|
||||
|
@ -332,662 +264,143 @@ as "www.bitcoin.org".
|
|||
To request payment using the payment protocol, you use an extended (but
|
||||
backwards-compatible) `bitcoin:` URI. For example:
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
~~~
|
||||
bitcoin:mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN\
|
||||
?amount=0.10\
|
||||
&label=Example+Merchant\
|
||||
&message=Order+of+flowers+%26+chocolates\
|
||||
&r=http://example.com/pay.php/invoice%3Dda39a3ee
|
||||
&r=https://example.com/pay/mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN
|
||||
~~~
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
None of the parameters provided above, except `r`, are required for the
|
||||
payment protocol---but your applications may include them for backwards
|
||||
compatibility with wallet programs which don't yet handle the payment
|
||||
protocol.
|
||||
|
||||
The [`r`][r]{:#term-r-parameter}{:.term} parameter tells payment-protocol-aware wallet programs to ignore
|
||||
the other parameters and fetch a PaymentRequest from the URL provided. If the
|
||||
request will be signed, which is recommended but not required, it can be
|
||||
fetched from an HTTP server---although fetching it from an HTTPS server
|
||||
would still be preferable.
|
||||
|
||||
the other parameters and fetch a PaymentRequest from the URL provided.
|
||||
The browser, QR code reader, or other program processing the URI opens
|
||||
the spender's Bitcoin wallet program on the URI. If the wallet program is
|
||||
aware of the payment protocol, it accesses the URL specified in the `r`
|
||||
parameter, which should provide it with a serialized PaymentRequest
|
||||
served with the [MIME][] type `application/bitcoin-paymentrequest`<!--noref-->.
|
||||
|
||||
**Resource:** Gavin Andresen's [Payment Request Generator][] generates
|
||||
custom example URIs and payment requests for use with testnet.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
##### PaymentRequest & PaymentDetails
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
The [PaymentRequest][]{:#term-paymentrequest}{:.term} is created with data structures built using
|
||||
Google's Protocol Buffers. BIP70 describes these data
|
||||
structures in the non-sequential way they're defined in the payment
|
||||
request protocol buffer code, but the text below will describe them in
|
||||
a more linear order using a simple (but functional) Python CGI
|
||||
program. (For brevity and clarity, many normal CGI best practices are
|
||||
not used in this program.)
|
||||
|
||||
The full sequence of events is illustrated below, starting with the
|
||||
spender clicking a `bitcoin:` URI or scanning a `bitcoin:` QR code.
|
||||
the spender's Bitcoin wallet program on the URI.
|
||||
|
||||

|
||||
|
||||
For the script to use the protocol buffer, you will need a copy of
|
||||
Google's Protocol Buffer compiler (`protoc`), which is available in most
|
||||
modern Linux package managers and [directly from Google.][protobuf] Non-Google
|
||||
protocol buffer compilers are available for a variety of
|
||||
programming languages. You will also need a copy of the PaymentRequest
|
||||
[Protocol Buffer description][core paymentrequest.proto] from the Bitcoin Core source code.
|
||||
|
||||
###### Initialization Code
|
||||
|
||||
With the Python code generated by `protoc`, we can start our simple
|
||||
CGI program.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
#!/usr/bin/env python
|
||||
|
||||
## This is the code generated by protoc --python_out=./ paymentrequest.proto
|
||||
from paymentrequest_pb2 import *
|
||||
|
||||
## Load some functions
|
||||
from time import time
|
||||
from sys import stdout
|
||||
from OpenSSL.crypto import FILETYPE_PEM, load_privatekey, sign
|
||||
|
||||
## Copy three of the classes created by protoc into objects we can use
|
||||
details = PaymentDetails()
|
||||
request = PaymentRequest()
|
||||
x509 = X509Certificates()
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
The startup code above is quite simple, requiring nothing but the epoch
|
||||
(Unix date) time function, the standard out file descriptor, a few
|
||||
functions from the OpenSSL library, and the data structures and
|
||||
functions created by `protoc`.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
###### Configuration Code
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
Next, we'll set configuration settings which will typically only change
|
||||
when the receiver wants to do something differently. The code pushes a
|
||||
few settings into the `request` (PaymentRequest) and `details`
|
||||
(PaymentDetails) objects. When we serialize them,
|
||||
[PaymentDetails][]{:#term-paymentdetails}{:.term} will be contained
|
||||
within the PaymentRequest.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
## SSL Signature method
|
||||
request.pki_type = "x509+sha256" ## Default: none
|
||||
|
||||
## Mainnet or Testnet?
|
||||
details.network = "test" ## Default: main
|
||||
|
||||
## Postback URL
|
||||
details.payment_url = "https://example.com/pay.py"
|
||||
|
||||
## PaymentDetails version number
|
||||
request.payment_details_version = 1 ## Default: 1
|
||||
|
||||
## Certificate chain
|
||||
x509.certificate.append(file("/etc/apache2/example.com-cert.der", "r").read())
|
||||
#x509.certificate.append(file("/some/intermediate/cert.der", "r").read())
|
||||
|
||||
## Load private SSL key into memory for signing later
|
||||
priv_key = "/etc/apache2/example.com-key.pem"
|
||||
pw = "test" ## Key password
|
||||
private_key = load_privatekey(FILETYPE_PEM, file(priv_key, "r").read(), pw)
|
||||
{% endhighlight %}
|
||||
|
||||
Each line is described below.
|
||||
|
||||
{% highlight python %}
|
||||
request.pki_type = "x509+sha256" ## Default: none
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`pki_type`: (optional) tell the receiving wallet program what [Public-Key
|
||||
Infrastructure][PKI]{:#term-pki}{:.term} (PKI) type you're using to
|
||||
cryptographically sign your PaymentRequest so that it can't be modified
|
||||
by a man-in-the-middle attack.
|
||||
|
||||
If you don't want to sign the PaymentRequest, you can choose a
|
||||
[`pki_type`][pp pki type]{:#term-pp-pki-type}{:.term} of `none`
|
||||
(the default).
|
||||
|
||||
If you do choose the sign the PaymentRequest, you currently have two
|
||||
options defined by BIP70: `x509+sha1` and `x509+sha256`. Both options
|
||||
use the X.509 certificate system, the same system used for HTTP Secure
|
||||
(HTTPS). To use either option, you will need a certificate signed by a
|
||||
certificate authority or one of their intermediaries. (A self-signed
|
||||
certificate will not work.)
|
||||
|
||||
Each wallet program may choose which certificate authorities to trust,
|
||||
but it's likely that they'll trust whatever certificate authorities their
|
||||
operating system trusts. If the wallet program doesn't have a full
|
||||
operating system, as might be the case for small hardware wallets, BIP70
|
||||
suggests they use the [Mozilla Root Certificate Store][mozrootstore]. In
|
||||
general, if a certificate works in your web browser when you connect to
|
||||
your webserver, it will work for your PaymentRequests.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
details.network = "test" ## Default: main
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`network`:<!--noref--> (optional) tell the spender's wallet program what Bitcoin network you're
|
||||
using; BIP70 defines "main" for mainnet (actual payments) and "test" for
|
||||
testnet (like mainnet, but fake satoshis are used). If the wallet
|
||||
program doesn't run on the network you indicate, it will reject the
|
||||
PaymentRequest.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
details.payment_url = "https://example.com/pay.py"
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`payment_url`: (required) tell the spender's wallet program where to send the Payment
|
||||
message (described later). This can be a static URL, as in this example,
|
||||
or a variable URL such as `https://example.com/pay.py?invoice=123.`
|
||||
It should usually be an HTTPS address to prevent man-in-the-middle
|
||||
attacks from modifying the message.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
request.payment_details_version = 1 ## Default: 1
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`payment_details_version`: (optional) tell the spender's wallet program what version of the
|
||||
PaymentDetails you're using. As of this writing, the only version is
|
||||
version 1.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
## This is the pubkey/certificate corresponding to the private SSL key
|
||||
## that we'll use to sign:
|
||||
x509.certificate.append(file("/etc/apache2/example.com-cert.der", "r").read())
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`x509certificates`:<!--noref--> (required for signed PaymentRequests) you must
|
||||
provide the public SSL key/certificate corresponding to the private SSL
|
||||
key you'll use to sign the PaymentRequest. The certificate must be in
|
||||
ASN.1/DER format.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
## If the pubkey/cert above didn't have the signature of a root
|
||||
## certificate authority, we'd then append the intermediate certificate
|
||||
## which signed it:
|
||||
#x509.certificate.append(file("/some/intermediate/cert.der", "r").read())
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
You must also provide any intermediate certificates necessary to link
|
||||
your certificate to the root certificate of a certificate authority
|
||||
trusted by the spender's software, such as a certificate from the
|
||||
Mozilla root store.
|
||||
|
||||
The certificates must be provided in a specific order---the same order
|
||||
used by Apache's `SSLCertificateFile` directive and other server
|
||||
software. The figure below shows the [certificate chain][]{:#term-certificate-chain}{:.term} of the
|
||||
www.bitcoin.org X.509 certificate and how each certificate (except the
|
||||
root certificate) would be loaded into the [X509Certificates][]{:#term-x509certificates}{:.term} protocol
|
||||
buffer message.
|
||||
|
||||

|
||||
|
||||
To be specific, the first certificate provided must be the
|
||||
X.509 certificate corresponding to the private SSL key which will make the
|
||||
signature<!--noref-->, called the [leaf certificate][]{:#term-leaf-certificate}{:.term}. Any [intermediate
|
||||
certificates][intermediate certificate]{:#term-intermediate-certificate}{:.term} necessary to link that signed public SSL
|
||||
key to the [root
|
||||
certificate][]{:#term-root-certificate}{:.term} (the certificate authority) are attached separately, with each
|
||||
certificate in DER format bearing the signature<!--noref--> of the certificate that
|
||||
follows it all the way to (but not including) the root certificate.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
priv_key = "/etc/apache2/example.com-key.pem"
|
||||
pw = "test" ## Key password
|
||||
private_key = load_privatekey(FILETYPE_PEM, file(priv_key, "r").read(), pw)
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
(Required for signed PaymentRequests) you will need a private SSL key in
|
||||
a format your SSL library supports (DER format is not required). In this
|
||||
program, we'll load it from a PEM file. (Embedding your passphrase in
|
||||
your CGI code, as done here, is obviously a bad idea in real life.)
|
||||
|
||||
The private SSL key will not be transmitted with your request. We're
|
||||
only loading it into memory here so we can use it to sign the request
|
||||
later.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
###### Code Variables
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
Now let's look at the variables your CGI program will likely set for
|
||||
each payment.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
## Amount of the request
|
||||
amount = 10000000 ## In satoshis
|
||||
|
||||
## P2PKH pubkey hash
|
||||
pubkey_hash = "2b14950b8d31620c6cc923c5408a701b1ec0a020"
|
||||
## P2PKH output script entered as hex and converted to binary
|
||||
# OP_DUP OP_HASH160 <push 20 bytes> <pubKey hash> OP_EQUALVERIFY OP_CHECKSIG
|
||||
# 76 a9 14 <pubKey hash> 88 ac
|
||||
hex_script = "76" + "a9" + "14" + pubkey_hash + "88" + "ac"
|
||||
serialized_script = hex_script.decode("hex")
|
||||
|
||||
## Load amount and script into PaymentDetails
|
||||
details.outputs.add(amount = amount, script = serialized_script)
|
||||
|
||||
## Memo to display to the spender
|
||||
details.memo = "Flowers & chocolates"
|
||||
|
||||
## Data which should be returned to you with the payment
|
||||
details.merchant_data = "Invoice #123"
|
||||
{% endhighlight python %}
|
||||
|
||||
Each line is described below.
|
||||
|
||||
{% highlight python %}
|
||||
amount = 10000000 ## In satoshis (=100 mBTC)
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`amount`: (optional) the [amount][pp amount]{:#term-pp-amount}{:.term} you want the spender to pay. You'll probably get
|
||||
this value from your shopping cart application or fiat-to-BTC exchange
|
||||
rate conversion tool. If you leave the amount blank, the wallet
|
||||
program will prompt the spender how much to pay (which can be useful
|
||||
for donations).
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
pubkey_hash = "2b14950b8d31620c6cc923c5408a701b1ec0a020"
|
||||
# OP_DUP OP_HASH160 <push 20 bytes> <pubKey hash> OP_EQUALVERIFY OP_CHECKSIG
|
||||
# 76 a9 14 <pubKey hash> 88 ac
|
||||
hex_script = "76" + "a9" + "14" + pubkey_hash + "88" + "ac"
|
||||
serialized_script = hex_script.decode("hex")
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`script`: (required) You must specify the output script you want the spender to
|
||||
pay---any valid script is acceptable. In this example, we'll request
|
||||
payment to a P2PKH output script.
|
||||
|
||||
First we get a pubkey hash. The hash above is the hash form of the
|
||||
address used in the URI examples throughout this section,
|
||||
mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN.
|
||||
|
||||
Next, we plug that hash into the standard P2PKH output script using hex,
|
||||
as illustrated by the code comments.
|
||||
|
||||
Finally, we convert the output script from hex into its serialized form.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
details.outputs.add(amount = amount, script = serialized_script)
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`outputs`:<!--noref--> (required) add the output script and (optional) amount to the
|
||||
PaymentDetails outputs<!--noref--> array.
|
||||
|
||||
It's possible to specify multiple [`scripts`][pp
|
||||
script]{:#term-pp-script}{:.term} and `amounts` as part of a merge
|
||||
avoidance strategy, described later in the [Merge Avoidance
|
||||
subsection][]. However, effective merge avoidance is not possible under
|
||||
the base BIP70 rules in which the spender pays each `script` the exact
|
||||
amount specified by its paired `amount`. If the amounts are omitted from
|
||||
all `amount`/`script` pairs, the spender will be prompted to choose an
|
||||
amount to pay.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
details.memo = "Flowers & chocolates"
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`memo`: (optional) add a memo which will be displayed to the spender as
|
||||
plain UTF-8 text. Embedded HTML or other markup will not be processed.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
details.merchant_data = "Invoice #123"
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`merchant_data`: (optional) add arbitrary data which should be sent back to the
|
||||
receiver when the invoice is paid. You can use this to track your
|
||||
invoices, although you can more reliably track payments by generating a
|
||||
unique address for each payment and then tracking when it gets paid.
|
||||
|
||||
The [`memo`][pp memo]{:#term-pp-memo}{:.term} field and the [`merchant_data`][pp merchant data]{:#term-pp-merchant-data}{:.term} field can be arbitrarily long,
|
||||
but if you make them too long, you'll run into the 50,000 byte limit on
|
||||
the entire PaymentRequest, which includes the often several kilobytes
|
||||
given over to storing the certificate chain. As will be described in a
|
||||
later subsection, the `memo` field can be used by the spender after
|
||||
payment as part of a cryptographically-proven receipt.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
###### Derivable Data
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
Next, let's look at some information your CGI program can
|
||||
automatically derive.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
## Request creation time
|
||||
details.time = int(time()) ## Current epoch (Unix) time
|
||||
|
||||
## Request expiration time
|
||||
details.expires = int(time()) + 60 * 10 ## 10 minutes from now
|
||||
|
||||
## PaymentDetails is complete; serialize it and store it in PaymentRequest
|
||||
request.serialized_payment_details = details.SerializeToString()
|
||||
|
||||
## Serialized certificate chain
|
||||
request.pki_data = x509.SerializeToString()
|
||||
|
||||
## Initialize signature field so we can sign the full PaymentRequest
|
||||
request.signature = ""
|
||||
|
||||
## Sign PaymentRequest
|
||||
request.signature = sign(private_key, request.SerializeToString(), "sha256")
|
||||
{% endhighlight %}
|
||||
|
||||
Each line is described below.
|
||||
|
||||
{% highlight python %}
|
||||
details.time = int(time()) ## Current epoch (Unix) time
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`time`: (required) PaymentRequests must indicate when they were created
|
||||
in number of seconds elapsed since 1970-01-01T00:00 UTC (Unix
|
||||
epoch time format).
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
details.expires = int(time()) + 60 * 10 ## 10 minutes from now
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`expires`: (optional) the PaymentRequest may also set an [`expires`][pp
|
||||
expires]{:#term-pp-expires}{:.term} time after
|
||||
which they're no longer valid. You probably want to give receivers
|
||||
the ability to configure the expiration time delta; here we used the
|
||||
reasonable choice of 10 minutes. If this request is tied to an order
|
||||
total based on a fiat-to-satoshis exchange rate, you probably want to
|
||||
base this on a delta from the time you got the exchange rate.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
request.serialized_payment_details = details.SerializeToString()
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`serialized_payment_details`: (required) we've now set everything we need to create the
|
||||
PaymentDetails, so we'll use the SerializeToString function from the
|
||||
protocol buffer code to store the PaymentDetails in the appropriate
|
||||
field of the PaymentRequest.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
request.pki_data = x509.SerializeToString()
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`pki_data`: (required for signed PaymentRequests) serialize the certificate chain
|
||||
[PKI data][pp PKI data]{:#term-pp-pki-data}{:.term} and store it in the
|
||||
PaymentRequest
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
request.signature = ""
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
We've filled out everything in the PaymentRequest except the signature,
|
||||
but before we sign it, we have to initialize the signature field by
|
||||
setting it to a zero-byte placeholder.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
request.signature = sign(private_key, request.SerializeToString(), "sha256")
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`signature`:<!--noref--> (required for signed PaymentRequests) now we
|
||||
make the [signature][ssl signature]{:#term-ssl-signature}{:.term} by
|
||||
signing the completed and serialized PaymentRequest. We'll use the
|
||||
private key we stored in memory in the configuration section and the
|
||||
same hashing formula we specified in `pki_type` (sha256 in this case)
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
###### Output Code
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
Now that we have PaymentRequest all filled out, we can serialize it and
|
||||
send it along with the HTTP headers, as shown in the code below.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
print "Content-Type: application/bitcoin-paymentrequest"
|
||||
print "Content-Transfer-Encoding: binary"
|
||||
print ""
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
(Required) BIP71 defines the content types for PaymentRequests,
|
||||
Payments, and PaymentACKs.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
{% highlight python %}
|
||||
file.write(stdout, request.SerializeToString())
|
||||
{% endhighlight %}
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
`request`: (required) now, to finish, we just dump out the serialized
|
||||
PaymentRequest (which contains the serialized PaymentDetails). The
|
||||
serialized data is in binary, so we can't use Python's print()
|
||||
because it would add an extraneous newline.
|
||||
|
||||
The following screenshot shows how the authenticated PaymentDetails
|
||||
created by the program above appears in the GUI from Bitcoin Core 0.9.
|
||||
The Payment Protocol is described in depth in BIP70, BIP71, and BIP72.
|
||||
An example CGI program and description of all the parameters which can
|
||||
be used in the Payment Protocol is provided in the Developer Examples
|
||||
[Payment Protocol][devex payment protocol] subsection. In this
|
||||
subsection, we will briefly describe in story format how the Payment
|
||||
Protocol is typically used.
|
||||
|
||||
Charlie, the client, is shopping on a website run by Bob, the
|
||||
businessman. Charlie adds a few items to his shopping cart and clicks
|
||||
the "Checkout With Bitcoin" button.
|
||||
|
||||
Bob's server automatically adds the following information to its
|
||||
invoice database:
|
||||
|
||||
* The details of Charlie's order, including items ordered and
|
||||
shipping address.
|
||||
|
||||
* An order total in satoshis, perhaps created by converting prices in
|
||||
fiat to prices in satoshis.
|
||||
|
||||
* An expiration time when that total will no longer be acceptable.
|
||||
|
||||
* An output script to which Charlie should send payment. Typically this
|
||||
will be a P2PKH or P2SH output script containing a unique (never
|
||||
before used) public key.
|
||||
|
||||
After adding all that information to the database, Bob's server displays
|
||||
a `bitcoin:` URI for Charlie to click to pay.
|
||||
|
||||
Charlie clicks on the `bitcoin:` URI in his browser. His browser's URI
|
||||
handler sends the URI to his wallet program. The wallet is aware of the
|
||||
Payment Protocol, so it parses the `r` parameter and sends an HTTP GET
|
||||
to that URL looking for a PaymentRequest message.
|
||||
|
||||
The PaymentRequest message returned may include private information, such as Charlie's
|
||||
mailing address, but the wallet must be able to access it without using prior
|
||||
authentication, such as HTTP cookies, so a publicly-accessible HTTPS URL
|
||||
with a guess-resistant part is typically used. The
|
||||
unique public key created for the payment request can be used to create
|
||||
a unique identifier. This is why, in the example URI above, the PaymentRequest
|
||||
URL contains the P2PKH address:
|
||||
`https://example.com/pay/mjSk1Ny9spzU2fouzYgLqGUD8U41iR35QN`
|
||||
|
||||
After receiving the HTTP GET to the URL above, the
|
||||
PaymentRequest-generating CGI program on Bob's webserver takes the
|
||||
unique identifier from the URL and looks up the corresponding details in
|
||||
the database. It then creates a PaymentDetails message with the
|
||||
following information:
|
||||
|
||||
* The amount of the order in satoshis and the output script to be paid.
|
||||
|
||||
* A memo containing the list of items ordered, so Charlie knows what
|
||||
he's paying for. It may also include Charlie's mailing address so he can
|
||||
double-check it.
|
||||
|
||||
* The time the PaymentDetails message was created plus the time
|
||||
it expires.
|
||||
|
||||
* A URL to which Charlie's wallet should send its completed transaction.
|
||||
|
||||
That PaymentDetails message is put inside a PaymentRequest message.
|
||||
The payment request lets Bob's server sign the entire Request with the
|
||||
server's X.509 SSL certificate. (The Payment Protocol has been designed
|
||||
to allow other signing methods in the future.) Bob's server sends the
|
||||
payment request to Charlie's wallet in the reply to the HTTP GET.
|
||||
|
||||

|
||||
|
||||
{% endautocrossref %}
|
||||
Charlie's wallet receives the PaymentRequest message, checks its signature, and
|
||||
then displays the details from the PaymentDetails message to Charlie. Charlie
|
||||
agrees to pay, so the wallet constructs a payment to the output script
|
||||
Bob's server provided. Unlike a traditional Bitcoin payment, Charlie's
|
||||
wallet doesn't necessarily automatically broadcast this payment to the
|
||||
network. Instead, the wallet constructs a Payment message and sends it to
|
||||
the URL provided in the PaymentDetails message as an HTTP POST. Among
|
||||
other things, the Payment message contains:
|
||||
|
||||
##### Payment
|
||||
* The signed transaction in which Charlie pays Bob.
|
||||
|
||||
{% autocrossref %}
|
||||
* An optional memo Charlie can send to Bob. (There's no guarantee that
|
||||
Bob will read it.)
|
||||
|
||||
If the spender declines to pay, the wallet program will not send any
|
||||
further messages to the receiver's server unless the spender clicks
|
||||
another URI pointing to that server. If the spender does decide to pay,
|
||||
the wallet program will create at least one transaction paying each of
|
||||
the outputs in the PaymentDetails section. The wallet may broadcast
|
||||
the transaction or transactions, as Bitcoin Core 0.9 does, but it
|
||||
doesn't need to.
|
||||
* A refund address (output script) which Bob can pay if he needs to
|
||||
return some or all of Charlie's satoshis.
|
||||
|
||||
Whether or not it broadcasts the transaction or transactions, the wallet
|
||||
program composes a reply to the PaymentRequest; the reply is called the
|
||||
Payment. [Payment][pp payment]{:#term-pp-payment}{:.term} contains four fields:
|
||||
Bob's server receives the Payment message, verifies the transaction pays
|
||||
the requested amount to the address provided, and then broadcasts the
|
||||
transaction to the network. It also replies to the HTTP POSTed Payment
|
||||
message with a PaymentACK message, which includes an optional memo
|
||||
from Bob's server thanking Charlie for his patronage and providing other
|
||||
information about the order, such as the expected arrival date.
|
||||
|
||||
* `merchant_data`: (optional) an exact copy of the
|
||||
`merchant_data` from the PaymentDetails. This is
|
||||
optional in the case that the PaymentDetails doesn't provide
|
||||
`merchant_data`. Receivers should be aware that malicious spenders can
|
||||
modify the merchant data before sending it back, so receivers may wish to
|
||||
cryptographically sign it before giving it to the spender and then
|
||||
validate it before relying on it.
|
||||
Charlie's wallet sees the PaymentACK and tells Charlie that the payment
|
||||
has been sent. The PaymentACK doesn't mean that Bob has verified
|
||||
Charlie's payment---see the Verifying Payment subsection below---but it does mean
|
||||
that Charlie can go do something else while the transaction gets confirmed.
|
||||
After Bob's server verifies from the block chain that Charlie's
|
||||
transaction has been suitably confirmed, it authorizes shipping
|
||||
Charlie's order.
|
||||
|
||||
* [`transactions`][pp transactions]{:#term-pp-transactions}{:.term}: (required) one or more signed transactions which pay the outputs
|
||||
specified in the PaymentDetails.
|
||||
In the case of a dispute, Charlie can generate a cryptographically-proven
|
||||
[receipt][]{:#term-receipt}{:.term} out of the various signed or
|
||||
otherwise-proven information.
|
||||
|
||||
<!-- BIP70 implies that refund_to is required (i.e. "one or more..."),
|
||||
but Mike Hearn implied on bitcoin-devel that it's optional (i.e. "wallets have
|
||||
to either never submit refund data, or always submit it").
|
||||
I'll use the BIP70 version here until I hear differently. -harding -->
|
||||
* The PaymentDetails message signed by Bob's webserver proves Charlie
|
||||
received an invoice to pay a specified output script for a specified
|
||||
number of satoshis for goods specified in the memo field.
|
||||
|
||||
* [`refund_to`][pp refund to]{:#term-pp-refund-to}{:.term}: (required) one or more output scripts to which the
|
||||
receiver can send a partial or complete refund. As of this writing, a
|
||||
proposal is gaining traction to expire refund output scripts after a
|
||||
certain amount of time (not defined yet) so spenders don't need to
|
||||
worry about receiving refunds to addresses they no longer monitor.
|
||||
* The Bitcoin block chain can prove that the output script specified by
|
||||
Bob was paid the specified number of satoshis.
|
||||
|
||||
* `memo`: (optional) a plain UTF-8 text memo sent to the receiver. It
|
||||
should not contain HTML or any other markup. Spenders should not depend
|
||||
on receivers reading their memos.
|
||||
|
||||
The Payment is sent to the [`payment_url`][pp payment
|
||||
url]{:#term-pp-payment-url}{:.term} provided in the PaymentDetails.
|
||||
The URL should be a HTTPS address to prevent a man-in-the-middle attack
|
||||
from modifying the spender's `refund_to` output scripts. When sending the
|
||||
Payment, the wallet program must set the following HTTP client headers:
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
~~~
|
||||
Content-Type: application/bitcoin-payment
|
||||
Accept: application/bitcoin-paymentack
|
||||
~~~
|
||||
|
||||
##### PaymentACK
|
||||
{% autocrossref %}
|
||||
|
||||
The receiver's CGI program at the `payment_url` receives the Payment message and
|
||||
decodes it using its Protocol Buffers code. The `transactions` are
|
||||
checked to see if they pay the output scripts the receiver requested in
|
||||
PaymentDetails and are then broadcast to the network (unless the network
|
||||
already has them).
|
||||
|
||||
The CGI program checks the `merchant_data` parameter if necessary and issues
|
||||
a [PaymentACK][]{:#term-paymentack}{:.term} (acknowledgment) with the following HTTP headers:
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
~~~
|
||||
Content-Type: application/bitcoin-paymentack
|
||||
Content-Transfer-Encoding: binary
|
||||
~~~
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
Then it sends another Protocol-Buffers-encoded message with one or two
|
||||
fields:
|
||||
|
||||
* `payment`: (required) A copy of the the entire Payment message (in
|
||||
serialized form) which is being acknowledged.
|
||||
|
||||
* `memo`: (optional) A plain UTF-8 text memo displayed to the spender
|
||||
informing them about the status of their payment. It should not
|
||||
contain HTML or any other markup. Receivers should not depend on
|
||||
spenders reading their memos.
|
||||
|
||||
The PaymentACK does not mean that the payment is final; it just means
|
||||
that everything seems to be correct. The payment is final once the
|
||||
payment transactions are block-chain confirmed to the receiver's
|
||||
satisfaction.
|
||||
|
||||
However, the spender's wallet program should indicate to the spender that
|
||||
the payment was accepted for processing so the spender can direct his or
|
||||
her attention elsewhere.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
##### Receipts
|
||||
|
||||
{% autocrossref %}
|
||||
|
||||
Unlike PaymentRequest, PaymentDetails, [Payment][pp payment]{:.auto-link}, and PaymentACK, there is
|
||||
no specific [receipt][]{:#term-receipt}{:.term} object. However, a cryptographically-verifiable
|
||||
receipt can be derived from a signed PaymentDetails and one or more confirmed
|
||||
transactions.
|
||||
|
||||
A signed PaymentDetails indicates what output scripts should be paid
|
||||
(`script`), how much they should be paid (`amount`), and by when
|
||||
(`expires`). The Bitcoin block chain indicates whether those outputs
|
||||
were paid the requested amount and can provide a rough idea of when the
|
||||
transactions were generated. Together, this information provides
|
||||
verifiable proof that the spender paid somebody with the
|
||||
receiver's private SSL key.
|
||||
If a refund needs to be issued, Bob's server can safely pay the
|
||||
refund-to output script provided by Charlie. (Note: a proposal has been
|
||||
discussed to give refund-to addresses an implicit expiration date so
|
||||
users and software don't need to worry about payments being sent to
|
||||
addresses which are no longer monitored.) See the Refunds section below
|
||||
for more details.
|
||||
|
||||
{% endautocrossref %}
|
||||
|
||||
|
|
|
@ -225,7 +225,7 @@ design these minimalist wallets:
|
|||
* Pre-populate a database with a number of public keys or addresses, and
|
||||
then distribute on request an output script or address using one of
|
||||
the database entries. To [avoid key reuse][devguide avoiding key
|
||||
resuse], webservers should keep track
|
||||
reuse], webservers should keep track
|
||||
of used keys and never run out of public keys. This can be made easier
|
||||
by using parent public keys as suggested in the next method.
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
[broadcast]: /en/developer-guide#transaction-broadcasting "Sending transactions or blocks to all other peers on the Bitcoin network (compare to privately transmitting to a single peer or partner"
|
||||
[broadcasts]: /en/developer-guide#transaction-broadcasting "Sending transactions or blocks to all other peers on the Bitcoin network (compare to privately transmitting to a single peer or partner"
|
||||
[broadcasting]: /en/developer-guide#transaction-broadcasting "Sending transactions or blocks to all other peers on the Bitcoin network (compare to privately transmitting to a single peer or partner)"
|
||||
[certificate chain]: /en/developer-guide#term-certificate-chain "A chain of certificates connecting a individual's leaf certificate to the certificate authority's root certificate"
|
||||
[certificate chain]: /en/developer-examples#term-certificate-chain "A chain of certificates connecting a individual's leaf certificate to the certificate authority's root certificate"
|
||||
[chain code]: /en/developer-guide#term-chain-code "In HD wallets, 256 bits of entropy added to the master public and private keys to help them generate secure child keys; the chain code is usually derived from a seed along with the master private key"
|
||||
[change address]: /en/developer-guide#term-change-output "An output used by a spender to send back to himself some of the satoshis from the inputs"
|
||||
[change output]: /en/developer-guide#term-change-output "An output used by a spender to send back to himself some of the satoshis from the inputs"
|
||||
|
@ -45,11 +45,11 @@
|
|||
[high-priority transactions]: /en/developer-guide#term-high-priority-transactions "Transactions which don't pay a transaction fee; only transactions spending long-idle outputs are eligible"
|
||||
[input]: /en/developer-guide#term-input "The input to a transaction linking to the output of a previous transaction which permits spending of satoshis"
|
||||
[inputs]: /en/developer-guide#term-input "The input to a transaction linking to the output of a previous transaction which permits spending of satoshis"
|
||||
[intermediate certificate]: /en/developer-guide#term-intermediate-certificate "A intermediate certificate authority certificate which helps connect a leaf (receiver) certificate to a root certificate authority"
|
||||
[intermediate certificate]: /en/developer-examples#term-intermediate-certificate "A intermediate certificate authority certificate which helps connect a leaf (receiver) certificate to a root certificate authority"
|
||||
[key index]: /en/developer-guide#term-key-index "An index number used in the HD wallet formula to generate child keys from a parent key"
|
||||
[key pair]: /en/developer-guide#term-key-pair "A private key and its derived public key"
|
||||
[label]: /en/developer-guide#term-label "The label parameter of a bitcoin: URI which provides the spender with the receiver's name (unauthenticated)"
|
||||
[leaf certificate]: /en/developer-guide#term-leaf-certificate "The end-node in a certificate chain; in the payment protocol, it is the certificate belonging to the receiver of satoshis"
|
||||
[leaf certificate]: /en/developer-examples#term-leaf-certificate "The end-node in a certificate chain; in the payment protocol, it is the certificate belonging to the receiver of satoshis"
|
||||
[locktime]: /en/developer-guide#term-locktime "Part of a transaction which indicates the earliest time or earliest block when that transaction can be added to the block chain"
|
||||
[long-term fork]: /en/developer-guide#term-long-term-fork "When a series of blocks have corresponding block heights, indicating a possibly serious problem"
|
||||
[mainnet]: /en/developer-guide#term-mainnet "The Bitcoin main network used to transfer satoshis (compare to testnet, the test network)"
|
||||
|
@ -90,29 +90,24 @@
|
|||
[parent private key]: /en/developer-guide#term-parent-private-key "A private key which has created child private keys"
|
||||
[parent public key]: /en/developer-guide#term-parent-public-key "A public key corresponding to a parent private key which has child private keys"
|
||||
[payment protocol]: /en/developer-guide#term-payment-protocol "The protocol defined in BIP70 which lets spenders get signed payment details from receivers"
|
||||
[PaymentACK]: /en/developer-guide#term-paymentack "The PaymentACK of the payment protocol which allows the receiver to indicate to the spender that the payment is being processed"
|
||||
[PaymentDetails]: /en/developer-guide#term-paymentdetails "The PaymentDetails of the payment protocol which allows the receiver to specify the payment details to the spender"
|
||||
[PaymentRequest]: /en/developer-guide#term-paymentrequest "The PaymentRequest of the payment protocol which contains and allows signing of the PaymentDetails"
|
||||
[PaymentRequests]: /en/developer-guide#term-paymentrequest "The PaymentRequest of the payment protocol which contains and allows signing of the PaymentDetails"
|
||||
[PaymentDetails]: /en/developer-examples#term-paymentdetails "The PaymentDetails of the payment protocol which allows the receiver to specify the payment details to the spender"
|
||||
[PaymentRequest]: /en/developer-examples#term-paymentrequest "The PaymentRequest of the payment protocol which contains and allows signing of the PaymentDetails"
|
||||
[PaymentRequests]: /en/developer-examples#term-paymentrequest "The PaymentRequest of the payment protocol which contains and allows signing of the PaymentDetails"
|
||||
[peer]: /en/developer-guide#term-peer "Peer on the P2P network who receives and broadcasts transactions and blocks"
|
||||
[peers]: /en/developer-guide#term-peer "Peers on the P2P network who receive and broadcast transactions and blocks"
|
||||
[PKI]: /en/developer-guide#term-pki "Public Key Infrastructure; usually meant to indicate the X.509 certificate system used for HTTP Secure (https)."
|
||||
[PKI]: /en/developer-examples#term-pki "Public Key Infrastructure; usually meant to indicate the X.509 certificate system used for HTTP Secure (https)."
|
||||
[point function]: /en/developer-guide#term-point-function "The ECDSA function used to create a public key from a private key"
|
||||
[private key]: /en/developer-guide#term-private-key "The private portion of a keypair which can create signatures which other people can verify using the public key"
|
||||
[private keys]: /en/developer-guide#term-private-key "The private portion of a keypair which can create signatures which other people can verify using the public key"
|
||||
[pubkey hash]: /en/developer-guide#term-pubkey-hash "The hash of a public key which can be included in a P2PKH output"
|
||||
[public key]: /en/developer-guide#term-public-key "The public portion of a keypair which can be safely distributed to other people so they can verify a signature created with the corresponding private key"
|
||||
[pp amount]: /en/developer-guide#term-pp-amount "Part of the Output part of the PaymentDetails part of a payment protocol where receivers can specify the amount of satoshis they want paid to a particular output script"
|
||||
[pp expires]: /en/developer-guide#term-pp-expires "The expires field of a PaymentDetails where the receiver tells the spender when the PaymentDetails expires"
|
||||
[pp memo]: /en/developer-guide#term-pp-memo "The memo fields of PaymentDetails, Payment, and PaymentACK which allow spenders and receivers to send each other memos"
|
||||
[pp merchant data]: /en/developer-guide#term-pp-merchant-data "The merchant_data part of PaymentDetails and Payment which allows the receiver to send arbitrary data to the spender in PaymentDetails and receive it back in Payments"
|
||||
[pp Payment]: /en/developer-guide#term-pp-payment "The Payment message of the PaymentProtocol which allows the spender to send payment details to the receiver"
|
||||
[pp PKI data]: /en/developer-guide#term-pp-pki-data "The pki_data field of a PaymentRequest which provides details such as certificates necessary to validate the request"
|
||||
[pp pki type]: /en/developer-guide#term-pp-pki-type "The PKI field of a PaymentRequest which tells spenders how to validate this request as being from a specific recipient"
|
||||
[pp refund to]: /en/developer-guide#term-pp-refund-to "The refund_to field of a Payment where the spender tells the receiver what outputs to send refunds to"
|
||||
[pp script]: /en/developer-guide#term-pp-script "The script field of a PaymentDetails where the receiver tells the spender what output scripts to pay"
|
||||
[pp transactions]: /en/developer-guide#term-pp-transactions "The transactions field of a Payment where the spender provides copies of signed transactions to the receiver"
|
||||
[pp payment url]: /en/developer-guide#term-pp-payment-url "The payment_url of the PaymentDetails which allows the receiver to specify where the sender should post payment"
|
||||
[pp amount]: /en/developer-examples#term-pp-amount "Part of the Output part of the PaymentDetails part of a payment protocol where receivers can specify the amount of satoshis they want paid to a particular output script"
|
||||
[pp expires]: /en/developer-examples#term-pp-expires "The expires field of a PaymentDetails where the receiver tells the spender when the PaymentDetails expires"
|
||||
[pp memo]: /en/developer-examples#term-pp-memo "The memo fields of PaymentDetails, Payment, and PaymentACK which allow spenders and receivers to send each other memos"
|
||||
[pp merchant data]: /en/developer-examples#term-pp-merchant-data "The merchant_data part of PaymentDetails and Payment which allows the receiver to send arbitrary data to the spender in PaymentDetails and receive it back in Payments"
|
||||
[pp PKI data]: /en/developer-examples#term-pp-pki-data "The pki_data field of a PaymentRequest which provides details such as certificates necessary to validate the request"
|
||||
[pp pki type]: /en/developer-examples#term-pp-pki-type "The PKI field of a PaymentRequest which tells spenders how to validate this request as being from a specific recipient"
|
||||
[pp script]: /en/developer-examples#term-pp-script "The script field of a PaymentDetails where the receiver tells the spender what output scripts to pay"
|
||||
[proof of work]: /en/developer-guide#term-proof-of-work "Proof that computationally-difficult work was performed which helps secure blocks against modification, protecting transaction history"
|
||||
[Pubkey]: /en/developer-guide#term-pubkey "A standard output script which specifies the full public key to match a signature; used in coinbase transactions"
|
||||
[r]: /en/developer-guide#term-r-parameter "The payment request parameter in a bitcoin: URI"
|
||||
|
@ -121,7 +116,7 @@
|
|||
[recurrent rebilling]: /en/developer-guide#rebilling-recurring-payments "Billing a spender on a regular schedule"
|
||||
[redeemScript]: /en/developer-guide#term-redeemscript "A script created by the recipient, hashed, and given to the spender for use in a P2SH output"
|
||||
[refund]: /en/developer-guide#issuing-refunds "A transaction which refunds some or all satoshis received in a previous transaction"
|
||||
[root certificate]: /en/developer-guide#term-root-certificate "A certificate belonging to a certificate authority (CA)"
|
||||
[root certificate]: /en/developer-examples#term-root-certificate "A certificate belonging to a certificate authority (CA)"
|
||||
[root seed]: /en/developer-guide#term-root-seed "A potentially-short value used as a seed to generate a master private key and master chain code for an HD wallet"
|
||||
[satoshi]: /en/developer-guide#term-satoshi "The smallest unit of Bitcoin value; 0.00000001 bitcoins. Also used generically for any value of bitcoins"
|
||||
[satoshis]: /en/developer-guide#term-satoshi "The smallest unit of Bitcoin value; 0.00000001 bitcoins. Also used generically for any value of bitcoins"
|
||||
|
@ -140,7 +135,7 @@
|
|||
[signature]: /en/developer-guide#term-signature "The result of combining a private key and some data in an ECDSA signature operation which allows anyone with the corresponding public key to verify the signature"
|
||||
[signature hash]: /en/developer-guide#term-signature-hash "A byte appended onto signatures generated in Bitcoin which allows the signer to specify what data was signed, allowing modification of the unsigned data"
|
||||
[spv]: /en/developer-guide#simplified-payment-verification-spv "A method for verifying particular transactions were included in blocks without downloading the entire contents of the block chain"
|
||||
[ssl signature]: /en/developer-guide#term-ssl-signature "Signatures created and recognized by major SSL implementations such as OpenSSL"
|
||||
[ssl signature]: /en/developer-examples#term-ssl-signature "Signatures created and recognized by major SSL implementations such as OpenSSL"
|
||||
[stack]: /en/developer-guide#term-stack "An evaluation stack used in Bitcoin's script language"
|
||||
[standard script]: /en/developer-guide#standard-transactions "An output script which matches the isStandard() patterns specified in Bitcoin Core---or a transaction containing only standard outputs. Only standard transactions are mined or broadcast by peers running the default Bitcoin Core software"
|
||||
[target]: /en/developer-guide#term-target "The threshold below which a block header hash must be in order for the block to be added to the block chain"
|
||||
|
@ -163,13 +158,15 @@
|
|||
[wallet]: /en/developer-guide#wallets "Software which stores private keys to allow users to spend and receive satoshis"
|
||||
[Wallet Import Format]: /en/developer-guide#term-wallet-import-format "A private key specially formatted to allow easy import into a wallet"
|
||||
[wallets]: /en/developer-guide#wallets "Software which stores private keys to allow users to spend and receive satoshis"
|
||||
[X509Certificates]: /en/developer-guide#term-x509certificates
|
||||
[X509Certificates]: /en/developer-examples#term-x509certificates
|
||||
|
||||
[BFGMiner]: https://github.com/luke-jr/bfgminer
|
||||
[BIP21]: https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki
|
||||
[BIP32]: https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
|
||||
[BIP39]: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
|
||||
[BIP70]: https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki
|
||||
[BIP71]: https://github.com/bitcoin/bips/blob/master/bip-0071.mediawiki
|
||||
[BIP72]: https://github.com/bitcoin/bips/blob/master/bip-0072.mediawiki
|
||||
[bitcoin-documentation mailing list]: https://groups.google.com/forum/#!forum/bitcoin-documentation
|
||||
[bitcoinpdf]: https://bitcoin.org/bitcoin.pdf
|
||||
[block170]: http://blockexplorer.com/block/00000000d1145790a8694403d4063f323d499e655c83426834d4ce2f8dd4a2ee
|
||||
|
@ -180,6 +177,8 @@
|
|||
[core paymentrequest.proto]: https://github.com/bitcoin/bitcoin/blob/master/src/qt/paymentrequest.proto
|
||||
[core script.h]: https://github.com/bitcoin/bitcoin/blob/master/src/script.h
|
||||
[DER]: https://en.wikipedia.org/wiki/Abstract_Syntax_Notation_One
|
||||
[devex payment protocol]: /en/developer-examples#payment-protocol
|
||||
[devguide]: /en/developer-guide
|
||||
[devguide avoiding key reuse]: /en/developer-guide#avoiding-key-reuse
|
||||
[devguide payment processing]: /en/developer-guide#payment-processing
|
||||
[devguide wallets]: /en/developer-guide#wallets
|
||||
|
|
|
@ -198,7 +198,6 @@ body{
|
|||
.docreference a{
|
||||
zoom:1;
|
||||
display:inline;
|
||||
width:200px;
|
||||
}
|
||||
|
||||
.downloadbox{
|
||||
|
|
|
@ -642,8 +642,9 @@ table td,table th{
|
|||
}
|
||||
.docreference a{
|
||||
display:inline-block;
|
||||
margin:0 15px 40px 15px;
|
||||
margin:0 0 40px 0;
|
||||
font-size:125%;
|
||||
width:210px;
|
||||
}
|
||||
.docreference img{
|
||||
display:block;
|
||||
|
@ -782,6 +783,21 @@ table td,table th{
|
|||
.toccontent a.term:visited code{
|
||||
color:#646464;
|
||||
}
|
||||
.toccontent .multicode{
|
||||
background-color:#f5f5f5;
|
||||
overflow-y:auto;
|
||||
padding:17px;
|
||||
border:1px solid #ccc;
|
||||
-webkit-border-radius:3px;
|
||||
-moz-border-radius:3px;
|
||||
border-radius:3px;
|
||||
}
|
||||
.toccontent .multicode pre{
|
||||
border:0px none;
|
||||
padding:0;
|
||||
margin:0;
|
||||
overflow-y:visible;
|
||||
}
|
||||
|
||||
.anchorAf{
|
||||
position:relative;
|
||||
|
|
|
@ -63,6 +63,8 @@ require 'yaml'
|
|||
(?![^\[]*\]) ## No subst if key inside [brackets]
|
||||
(?![^\{]*\}) ## No subst if key inside {braces}
|
||||
(?![^\s]*<!--noref-->) ## No subst if <!--noref--> after key
|
||||
(?![\S ]*<\/span>) ## No subst on a line with a close span. This
|
||||
## prevents matching in highlight blocks
|
||||
(?![^\(]*(\.svg|\.png)) ## No subst if key inside an image name. This
|
||||
## simple regex has the side effect that we can't
|
||||
## use .svg or .png in non-image base text; if that
|
||||
|
@ -71,7 +73,7 @@ require 'yaml'
|
|||
(?!\w) ## Don't match inside words
|
||||
/xmi, "[\\&][#{term[1]}]{:.auto-link}")
|
||||
}
|
||||
output.gsub!('<!--noref-->','') ## Remove all <!--noref--> comments
|
||||
output.gsub!(/<!--.*?-->/m,'') ## Remove all HTML comments
|
||||
|
||||
output
|
||||
end
|
||||
|
|
|
@ -10,8 +10,9 @@ title: "Developer Documentation - Bitcoin"
|
|||
<p class="summary">Find useful resources, guides and reference material for developers.</p>
|
||||
|
||||
<div class="docreference">
|
||||
<a href="/en/developer-guide"><img src="/img/main_ico_guide.svg" alt="icon"><span>Developer Guide</span><span>(How Bitcoin works)</span></a>
|
||||
<a href="/en/developer-guide"><img src="/img/main_ico_compass.svg" alt="icon"><span>Developer Guide</span><span>(How Bitcoin works)</span></a>
|
||||
<a href="/en/developer-reference"><img src="/img/main_ico_guide.svg" alt="icon"><span>Developer Reference</span><span>(Specifications and APIs)</span></a>
|
||||
<a href="/en/developer-examples"><img src="/img/main_ico_hash.svg" alt="icon"><span>Developer Examples</span><span>(Examples you can use)</span></a>
|
||||
</div>
|
||||
|
||||
<div class="resources">
|
||||
|
@ -23,6 +24,7 @@ title: "Developer Documentation - Bitcoin"
|
|||
<h2><img src="/img/ico_micro.svg" class="titleicon" alt="Icon">Transactions</h2>
|
||||
<p><a href="/en/developer-guide#transactions">Transactions Guide</a></p>
|
||||
<p><a href="/en/developer-reference#transactions">Transactions Reference</a></p>
|
||||
<p><a href="/en/developer-examples#transactions">Transaction Examples</a></p>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
@ -47,6 +49,7 @@ title: "Developer Documentation - Bitcoin"
|
|||
<div>
|
||||
<h2><img src="/img/ico_bill.svg" class="titleicon" alt="Icon">Payment Processing</h2>
|
||||
<p><a href="/en/developer-guide#payment-processing">Payment Processing Guide</a></p>
|
||||
<p><a href="/en/developer-examples#payment-processing">Payment Processing Examples</a></p>
|
||||
<div class="resourcesext">
|
||||
<p><a href="https://github.com/bitcoin/bips/blob/master/bip-0070.mediawiki">Payment Protocol</a> - BIP70</p>
|
||||
</div>
|
||||
|
|
34
en/developer-examples.md
Normal file
34
en/developer-examples.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
layout: base
|
||||
lang: en
|
||||
id: developer-examples
|
||||
title: "Developer Examples - Bitcoin"
|
||||
---
|
||||
|
||||
# Bitcoin Developer Examples
|
||||
|
||||
<p class="summary">Find examples of how to build programs using Bitcoin.</p>
|
||||
|
||||
<div markdown="1" id="toc" class="toc"><div markdown="1">
|
||||
|
||||
* Table of contents
|
||||
{:toc}
|
||||
|
||||
<ul class="goback"><li><a href="/en/developer-documentation">Return To Overview</a></li></ul>
|
||||
<ul class="reportissue"><li><a href="https://github.com/bitcoin/bitcoin.org/issues/new" onmouseover="updateIssue(event);">Report An Issue</a></li></ul>
|
||||
|
||||
</div></div>
|
||||
<div markdown="1" class="toccontent">
|
||||
|
||||
{% include fragment_reviews_needed.md %}
|
||||
|
||||
{% include example_transactions.md %}
|
||||
|
||||
{% include example_payment_processing.md %}
|
||||
|
||||
{% include references.md %}
|
||||
|
||||
</div>
|
||||
|
||||
<script>updateToc();</script>
|
||||
<script>addAnchorLinks();</script>
|
|
@ -21,18 +21,12 @@ title: "Developer Guide - Bitcoin"
|
|||
|
||||
<div markdown="1" class="toccontent">
|
||||
|
||||
<!--Temporary disclaimer BEGIN-->
|
||||
<div id="develdocdisclaimer" class="develdocdisclaimer"><div>
|
||||
<b>BETA</b>: This documentation has been written recently and still needs more reviews to ensure all content is covered correctly and accurately; if you find a mistake, please <a href="https://github.com/bitcoin/bitcoin.org/issues/new" onmouseover="updateIssue(event);">report an issue</a> on GitHub. <a href="#" onclick="disclaimerClose(event);">Click here</a> to close this disclaimer.
|
||||
<a class="develdocdisclaimerclose" href="#" onclick="disclaimerClose(event);">X</a>
|
||||
</div></div>
|
||||
<script>disclaimerAutoClose();</script>
|
||||
<!--Temporary disclaimer END-->
|
||||
|
||||
<!-- includes should be separated by an empty line to prevent a
|
||||
paragraph at the end of one file from breaking the heading at the start
|
||||
of the following file. -->
|
||||
|
||||
{% include fragment_reviews_needed.md %}
|
||||
|
||||
{% include guide_intro.md %}
|
||||
|
||||
{% include guide_block_chain.md %}
|
||||
|
|
|
@ -20,16 +20,12 @@ title: "Developer Reference - Bitcoin"
|
|||
</div></div>
|
||||
<div markdown="1" class="toccontent">
|
||||
|
||||
<!--Temporary disclaimer BEGIN-->
|
||||
<div id="develdocdisclaimer" class="develdocdisclaimer"><div>
|
||||
<b>BETA</b>: This documentation has been written recently and still needs more reviews to ensure all content is covered correctly and accurately; if you find a mistake, please <a href="https://github.com/bitcoin/bitcoin.org/issues/new" onmouseover="updateIssue(event);">report an issue</a> on GitHub. <a href="#" onclick="disclaimerClose(event);">Click here</a> to close this disclaimer.
|
||||
<a class="develdocdisclaimerclose" href="#" onclick="disclaimerClose(event);">X</a>
|
||||
</div></div>
|
||||
<script>disclaimerAutoClose();</script>
|
||||
<!--Temporary disclaimer END-->
|
||||
{% include fragment_reviews_needed.md %}
|
||||
|
||||
{% include ref_block_chain.md %}
|
||||
|
||||
{% include ref_transactions.md %}
|
||||
|
||||
{% include ref_wallets.md %}
|
||||
|
||||
## Bitcoin Core APIs
|
||||
|
|
BIN
img/main_ico_compass.png
Normal file
BIN
img/main_ico_compass.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
127
img/main_ico_compass.svg
Normal file
127
img/main_ico_compass.svg
Normal file
|
@ -0,0 +1,127 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xml:space="preserve"
|
||||
width="48"
|
||||
height="48"
|
||||
style="fill-rule:evenodd"
|
||||
viewBox="0 0 13.546666 13.546666"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
sodipodi:docname="Compass_rose_en_04p.svg"><metadata
|
||||
id="metadata34"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="800"
|
||||
id="namedview32"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="40.338701"
|
||||
inkscape:cy="32.568949"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" />
|
||||
<defs
|
||||
id="defs4">
|
||||
<style
|
||||
type="text/css"
|
||||
id="style6">
|
||||
|
||||
.str1 {stroke:#999999;stroke-width:0.0762}
|
||||
.str0 {stroke:#B2B2B2;stroke-width:0.0762}
|
||||
.fil0 {fill:none}
|
||||
.fil1 {fill:#9999FF}
|
||||
.fil2 {fill:#E5E5E5}
|
||||
.fil3 {fill:black;fill-rule:nonzero}
|
||||
|
||||
</style>
|
||||
</defs>
|
||||
|
||||
<circle
|
||||
d="M 76.5,45 C 76.5,62.39697 62.39697,76.5 45,76.5 27.60303,76.5 13.5,62.39697 13.5,45 13.5,27.60303 27.60303,13.5 45,13.5 62.39697,13.5 76.5,27.60303 76.5,45 z"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.28222221;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
sodipodi:ry="31.5"
|
||||
sodipodi:rx="31.5"
|
||||
sodipodi:cy="45"
|
||||
sodipodi:cx="45"
|
||||
id="circle10"
|
||||
r="31.5"
|
||||
cy="45"
|
||||
cx="45"
|
||||
class="fil0 str0"
|
||||
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||
id="_125488368"
|
||||
class="fil1 str1"
|
||||
points="45.5813,45.003 53.596,37.003 45.5813,10.003 "
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||
id="_125485752"
|
||||
class="fil2 str1"
|
||||
points="45.5813,45.003 37.5666,37.003 45.5813,10.003 "
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||
id="_125486160"
|
||||
class="fil1 str1"
|
||||
points="45.5813,45.003 53.5813,53.0177 80.5813,45.003 "
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||
id="_125485464"
|
||||
class="fil2 str1"
|
||||
points="45.5813,45.003 53.5813,36.9883 80.5813,45.003 "
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||
id="_125486136"
|
||||
class="fil1 str1"
|
||||
points="45.5813,45.003 37.5666,53.003 45.5813,80.003 "
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||
id="_125485440"
|
||||
class="fil2 str1"
|
||||
points="45.5813,45.003 53.596,53.003 45.5813,80.003 "
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||
id="_125488056"
|
||||
class="fil1 str1"
|
||||
points="45.5813,45.003 37.5813,36.9883 10.5813,45.003 "
|
||||
style="fill:#000000;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><polygon
|
||||
id="_125486208"
|
||||
class="fil2 str1"
|
||||
points="45.5813,45.003 37.5813,53.0177 10.5813,45.003 "
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.56444442;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
|
||||
transform="matrix(0.18698133,0,0,-0.18698133,-1.7495191,15.188054)" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.15696824px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 8.9329554,4.7290382 11.932577,1.7294216"
|
||||
id="path3804"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.16438642px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 8.8605813,8.8551168 12.004951,11.993504"
|
||||
id="path3804-4"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.16195713px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 1.659623,1.6832236 4.757519,4.7752362"
|
||||
id="path3804-4-5"
|
||||
inkscape:connector-curvature="0" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.15696824px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="M 1.7087612,11.924122 4.7083808,8.9244992"
|
||||
id="path3804-5"
|
||||
inkscape:connector-curvature="0" /></svg>
|
After Width: | Height: | Size: 5.5 KiB |
BIN
img/main_ico_hash.png
Normal file
BIN
img/main_ico_hash.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 915 B |
67
img/main_ico_hash.svg
Normal file
67
img/main_ico_hash.svg
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="48"
|
||||
height="48"
|
||||
id="svg3896"
|
||||
version="1.1"
|
||||
inkscape:version="0.48.3.1 r9886"
|
||||
sodipodi:docname="hashbang.svg">
|
||||
<defs
|
||||
id="defs3898" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="1"
|
||||
inkscape:cx="45.392857"
|
||||
inkscape:cy="37.178572"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
inkscape:window-width="1278"
|
||||
inkscape:window-height="778"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0" />
|
||||
<metadata
|
||||
id="metadata3901">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(0,-1004.3622)">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:67.09772491px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Droid Sans;-inkscape-font-specification:Droid Sans"
|
||||
x="2.3275659"
|
||||
y="1052.3116"
|
||||
id="text3904"
|
||||
sodipodi:linespacing="125%"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan3906"
|
||||
x="2.3275659"
|
||||
y="1052.3116">#</tspan></text>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
Loading…
Add table
Add a link
Reference in a new issue