Add quick start tutorial and docs on wallet, address

This commit is contained in:
Michał Sałaban 2018-01-18 02:59:09 +01:00
parent 9c05dfe4b1
commit ea6d4264a7
5 changed files with 355 additions and 8 deletions

View File

@ -1,8 +1,170 @@
Addresses, amounts and payment IDs
==================================
Addresses and payment IDs
=========================
In Monero v0.11.x the wallet had only one address. This is changing now. A
concept of **subaddress** has been introduced.
The first, original address of the wallet is usually known as the *master
address*. All others are just *subaddresses*, even if they represent a separate
account within the wallet.
Monero addresses are base58-encoded strings. You may disassemble each of them
using the excellent `address analysis tool`_ from *luigi1111*.
While the ordinary string representation is perfectly valid to use, you may
want to use validation and other features provided by the ``monero.address``
package.
.. _`address analysis tool`: https://xmr.llcoins.net/addresstests.html
Address validation and instatination
------------------------------------
The function ``monero.address.address()`` will recognize and validate Monero
address, returning an instance that provides additional functionality.
The following example uses addresses from the wallet :doc:`we have generated in
the previous chapter <wallet>`.
Let's start with the master address:
.. code-block:: python
In [1]: from monero.address import address
In [2]: a = address('A2GmyHHJ9jtUhPiwoAbR2tXU9LJu2U6fJjcsv3rxgkVRWU6tEYcn6C1NBc7wqCv5V7NW3zeYuzKf6RGGgZTFTpVC4QxAiAX')
In [3]: a.is_testnet()
Out[3]: True
In [4]: a.get_spend_key()
Out[4]: 'f0481b63cb937fa5960529247ebf6db627ff1b0bb88de9feccc3c504c16aa4b0'
In [5]: a.get_view_key()
Out[5]: '2c5ba76d22e48a7ea4ddabea3cce66808ba0cc91265371910f893962e977af1e'
In [6]: type(a)
Out[6]: monero.address.Address
We may use a subaddress too:
.. code-block:: python
In [7]: b = address('BenuGf8eyVhjZwdcxEJY1MHrUfqHjPvE3d7Pi4XY5vQz53VnVpB38bCBsf8AS5rJuZhuYrqdG9URc2eFoCNPwLXtLENT4R7')
In [8]: b.is_testnet()
Out[8]: True
In [9]: b.get_spend_key()
Out[9]: 'ae7e136f46f618fe7f4a6b323ed60864c20070bf110978d7e3868686d5677318'
In [10]: b.get_view_key()
Out[10]: '2bf801cdaf3a8b41020098a6d5e194f48fa62129fe9d8f09d19fee9260665baa'
In [11]: type(b)
Out[11]: monero.address.SubAddress
These two classes, ``Address`` and ``SubAddress`` have similar functionality
but one significant difference. Only the former may form *integrated address*.
Payment IDs and integrated addresses
------------------------------------
Each Monero transaction may carry a **payment ID**. It is a 64 or 256-bit long
number that carries additional information between parties. For example, a
merchant can generate a payment ID for each order, or an exchange can assign
one to each user, so they would know what is the purpose of incoming payment.
A short, 64-bit payment ID can be integrated into an address, creating, well...
an **integrated address**.
.. code-block:: python
In [12]: ia = a.with_payment_id(0xfeedbadbeef)
In [13]: ia
Out[13]: ABySz66nm1QUhPiwoAbR2tXU9LJu2U6fJjcsv3rxgkVRWU6tEYcn6C1NBc7wqCv5V7NW3zeYuzKf6RGGgZTFTpVC623BT1ptXvVU2GjR1B
In [14]: ia.get_base_address()
Out[14]: A2GmyHHJ9jtUhPiwoAbR2tXU9LJu2U6fJjcsv3rxgkVRWU6tEYcn6C1NBc7wqCv5V7NW3zeYuzKf6RGGgZTFTpVC4QxAiAX
In [15]: ia.get_base_address() == a
Out[15]: True
In [16]: ia.get_payment_id()
Out[16]: 00000feedbadbeef
Since subaddresses have been introduced, merchants may generate a separate
address for each order, user or any other object they expect the payments
coming to. Therefore, it has been decided that `subaddresses cannot generate
integrated addresses`_.
.. _`subaddresses cannot generate integrated addresses`: https://monero.stackexchange.com/questions/6606/how-to-make-an-integrated-address-based-on-a-subaddress
.. code-block:: python
In [17]: b.with_payment_id(0xfeedbadbeef)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-23-5a5811a6962a> in <module>()
----> 1 b.with_payment_id(0xfeedbadbeef)
~/devel/monero-python/monero/address.py in with_payment_id(self, _)
99
100 def with_payment_id(self, _):
--> 101 raise TypeError("SubAddress cannot be integrated with payment ID")
102
103
TypeError: SubAddress cannot be integrated with payment ID
The ``monero.numbers.PaymentID`` class validates payment IDs. It accepts both
integer and hexadecimal string representations.
.. code-block:: python
In [18]: from monero.numbers import PaymentID
In [19]: p1 = PaymentID(0xfeedbadbeef)
In [20]: p2 = PaymentID('feedbadbeef')
In [21]: p1 == p2
Out[21]: True
In [22]: p1.is_short()
Out[22]: True
In [23]: p3 = PaymentID('1234567890abcdef0')
In [24]: p3
Out[24]: 000000000000000000000000000000000000000000000001234567890abcdef0
In [25]: p3.is_short()
Out[25]: False
Long payment IDs cannot be integrated:
.. code-block:: python
In [26]: a.with_payment_id(p3)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-31-7098746f0b69> in <module>()
----> 1 a.with_payment_id(p3)
~/devel/monero-python/monero/address.py in with_payment_id(self, payment_id)
73 payment_id = numbers.PaymentID(payment_id)
74 if not payment_id.is_short():
---> 75 raise TypeError("Payment ID {0} has more than 64 bits and cannot be integrated".format(payment_id))
76 prefix = 54 if self.is_testnet() else 19
77 data = bytearray([prefix]) + self._decoded[1:65] + struct.pack('>Q', int(payment_id))
TypeError: Payment ID 000000000000000000000000000000000000000000000001234567890abcdef0 has more than 64 bits and cannot be integrated
API reference
=============
.. automodule:: monero.address
:members:
.. automodule:: monero.numbers
:members:

5
docs/source/backends.rst Normal file
View File

@ -0,0 +1,5 @@
JSON RPC backend
================
.. automodule:: monero.backends.jsonrpc
:members:

View File

@ -1,22 +1,31 @@
Python module for Monero -- docs
================================================
Here Monero meets Python.
Welcome to the documentation for the `monero` Python module.
The aim of this project is to offer a set of tools for interacting with Monero
cryptocurrency in Python. It provides higher level classes representing objects
from the Monero environment, like wallets, accounts, addresses, transactions.
Currently it operates over JSON RPC protocol, however other backends are
planned as well.
.. toctree::
:maxdepth: 2
:maxdepth: 1
:caption: Contents:
quickstart
wallet
address
daemon
backends
exceptions
license
authors
Indices and tables
==================
------------------
* :ref:`genindex`
* :ref:`modindex`

View File

@ -0,0 +1,79 @@
Quick start
===========
This quick start tutorial will guide you through the first steps of connecting
to the Monero wallet. We assume you:
* have basic knowledge of Monero concepts of the wallet and daemon,
* know how to use CLI (*command line interface*),
* have experience with Python.
Connect to testnet for your own safety
--------------------------------------
The testnet is another Monero network where worthless coins circulate and
where, as the name suggests, all tests are supposed to be run. It's also a
place for early deployment of future features of the currency itself. You may
read `a brief explanation at stackexchange`_.
**Please run all tests on testnet.** The code presented in these docs will
perform the requested operations right away, without asking for confirmation.
This is live code, not a wallet application that makes sure the user has not
made a mistake. **Running on the live net, if you make a mistake, you may lose
money.**
.. _a brief explanation at stackexchange: https://monero.stackexchange.com/questions/1591/what-is-the-monero-testnet-how-can-i-participate-in-it
Start the daemon and create a wallet
------------------------------------
In order to connect to the testnet network you need to start the daemon:
.. code-block:: shell
$ monerod --testnet
If you haven't used testnet before, it will begin downloading the blockchain,
exactly like it does on the live network. In January 2018 the testnet
blockchain was slightly over 2 GiB. It may take some time to get it.
You may however create a wallet in the meantime:
.. code-block:: shell
$ monero-wallet-cli --testnet --generate-new-wallet testwallet
For now you may leave the password empty (testnet coins are worthless).
Start the RPC server
--------------------
The RPC server is a small utility that will operate on the wallet, exposing
a JSON RPC interface. Start it by typing:
.. code-block:: shell
$ monero-wallet-rpc --testnet --wallet-file testwallet --password "" --rpc-bind-port 28088 --disable-rpc-login
Now everything is ready to start using Python.
Connect to the wallet
---------------------
.. code-block:: python
In [1]: from monero.wallet import Wallet
In [2]: from monero.backends.jsonrpc import JSONRPCWallet
In [3]: w = Wallet(JSONRPCWallet(port=28088))
In [4]: w.get_address()
Out[4]: A2GmyHHJ9jtUhPiwoAbR2tXU9LJu2U6fJjcsv3rxgkVRWU6tEYcn6C1NBc7wqCv5V7NW3zeYuzKf6RGGgZTFTpVC4QxAiAX
In [5]: w.get_balance()
Out[5]: Decimal('0E-12')
Congratulations! You have connected to the wallet. You may now proceed to the
next part, which will tell you about :doc:`interaction with wallet and accounts <wallet>`.

View File

@ -1,6 +1,98 @@
Using wallet and accounts
=========================
The wallet, up to Monero 'Helium Hydra' (0.11.x) release, had only single
address and no concept of accounts. This will change with the next version
which is planned to be published in March 2018 and already is available for
testing.
The wallet
----------
The following example shows how to create and retrieve wallet's accounts and
addresses:
.. code-block:: python
In [1]: from monero.wallet import Wallet
In [2]: from monero.backends.jsonrpc import JSONRPCWallet
In [3]: w = Wallet(JSONRPCWallet(port=28088))
In [4]: w.get_address()
Out[4]: A2GmyHHJ9jtUhPiwoAbR2tXU9LJu2U6fJjcsv3rxgkVRWU6tEYcn6C1NBc7wqCv5V7NW3zeYuzKf6RGGgZTFTpVC4QxAiAX
Accounts and subaddresses
-------------------------
The following part may look strange if you are still using v0.11.x, because the
concept of multiple accounts and subaddresses didn't exist back then.
The accounts are stored in wallet's ``accounts`` attribute, which is a list.
Regardless of the version, **the wallet by default operates on its account of
index 0**, which makes it consistent with the behavior of the CLI wallet
client. On v0.11 the following code will work, even though it doesn't make much
sense.
.. code-block:: python
In [5]: len(w.accounts)
Out[5]: 1
In [6]: w.accounts[0]
Out[6]: <monero.account.Account at 0x7f78992d6898>
In [7]: w.accounts[0].get_address()
Out[7]: A2GmyHHJ9jtUhPiwoAbR2tXU9LJu2U6fJjcsv3rxgkVRWU6tEYcn6C1NBc7wqCv5V7NW3zeYuzKf6RGGgZTFTpVC4QxAiAX
In [8]: w.get_addresses()
Out[8]: [A2GmyHHJ9jtUhPiwoAbR2tXU9LJu2U6fJjcsv3rxgkVRWU6tEYcn6C1NBc7wqCv5V7NW3zeYuzKf6RGGgZTFTpVC4QxAiAX]
Creating accounts and addresses
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Every wallet can have separate accounts and each account can have numerous
addresses. The ``Wallet.new_account()`` and ``Account.new_address()`` will
create new instances.
(This snippet, will fail on Monero v0.11.x)
.. code-block:: python
In [9]: w.new_address()
Out[9]: BenuGf8eyVhjZwdcxEJY1MHrUfqHjPvE3d7Pi4XY5vQz53VnVpB38bCBsf8AS5rJuZhuYrqdG9URc2eFoCNPwLXtLENT4R7
In [10]: w.get_addresses()
Out[10]:
[A2GmyHHJ9jtUhPiwoAbR2tXU9LJu2U6fJjcsv3rxgkVRWU6tEYcn6C1NBc7wqCv5V7NW3zeYuzKf6RGGgZTFTpVC4QxAiAX,
BenuGf8eyVhjZwdcxEJY1MHrUfqHjPvE3d7Pi4XY5vQz53VnVpB38bCBsf8AS5rJuZhuYrqdG9URc2eFoCNPwLXtLENT4R7]
In [11]: w.new_account()
Out[11]: <monero.account.Account at 0x7f7894dffbe0>
In [12]: len(w.accounts)
Out[12]: 2
In [13]: w.accounts[1].get_address()
Out[13]: Bhd3PRVCnq5T5jjNey2hDSM8DxUgFpNjLUrKAa2iYVhYX71RuCGTekDKZKXoJPAGL763kEXaDSAsvDYb8bV77YT7Jo19GKY
In [14]: w.accounts[1].new_address()
Out[14]: Bbz5uCtnn3Gaj1YAizaHw1FPeJ6T7kk7uQoeY48SWjezEAyrWScozLxYbqGxsV5L6VJkvw5VwECAuLVJKQtHpA3GFXJNPYu
In [15]: w.accounts[1].get_addresses()
Out[15]:
[Bhd3PRVCnq5T5jjNey2hDSM8DxUgFpNjLUrKAa2iYVhYX71RuCGTekDKZKXoJPAGL763kEXaDSAsvDYb8bV77YT7Jo19GKY,
Bbz5uCtnn3Gaj1YAizaHw1FPeJ6T7kk7uQoeY48SWjezEAyrWScozLxYbqGxsV5L6VJkvw5VwECAuLVJKQtHpA3GFXJNPYu]
As mentioned above, the wallet by default operates on the first account, so
``w.new_address()`` is equivalent to ``w.accounts[0].new_address()``.
In the next chapter we will :doc:`learn about addresses <address>`.
.. automodule:: monero.wallet
:members: