mirror of
https://git.wownero.com/lza_menace/wownero-python.git
synced 2024-08-15 03:25:25 +00:00
Add quick start tutorial and docs on wallet, address
This commit is contained in:
parent
9c05dfe4b1
commit
ea6d4264a7
5 changed files with 355 additions and 8 deletions
|
@ -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
|
.. automodule:: monero.address
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
.. automodule:: monero.numbers
|
|
||||||
:members:
|
|
||||||
|
|
5
docs/source/backends.rst
Normal file
5
docs/source/backends.rst
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
JSON RPC backend
|
||||||
|
================
|
||||||
|
|
||||||
|
.. automodule:: monero.backends.jsonrpc
|
||||||
|
:members:
|
|
@ -1,22 +1,31 @@
|
||||||
Python module for Monero -- docs
|
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::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 1
|
||||||
:caption: Contents:
|
:caption: Contents:
|
||||||
|
|
||||||
|
quickstart
|
||||||
wallet
|
wallet
|
||||||
address
|
address
|
||||||
daemon
|
daemon
|
||||||
|
backends
|
||||||
exceptions
|
exceptions
|
||||||
license
|
license
|
||||||
authors
|
authors
|
||||||
|
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
==================
|
------------------
|
||||||
|
|
||||||
* :ref:`genindex`
|
* :ref:`genindex`
|
||||||
* :ref:`modindex`
|
* :ref:`modindex`
|
||||||
|
|
79
docs/source/quickstart.rst
Normal file
79
docs/source/quickstart.rst
Normal 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>`.
|
|
@ -1,6 +1,98 @@
|
||||||
Using wallet and accounts
|
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
|
.. automodule:: monero.wallet
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue