Interacting with daemon¶
The module offers an interface to interact with Monero daemon. For the time being, the only
available method to connnect to a daemon is by JSON RPC commands but the module allows for
providing a custom backend. The initializer
accepts keywords including, but not limited to, host
, port
, user
, and password
.
In [1]: from monero.daemon import Daemon
In [2]: daemon = Daemon(port=28081)
In [3]: daemon.height()
Out[3]: 1099108
Also, the info()
method will return a dictionary with details about the current daemon status.
Connecting via proxy (or TOR)¶
Daemon
also accepts optional proxy_url
keyword. A prime example of use is to route
your traffic via TOR:
In [4]: daemon = Daemon(host='xmrag4hf5xlabmob.onion', proxy_url='socks5h://127.0.0.1:9050')
In [5]: daemon.height()
Out[5]: 1999790
Please refer to the docs of underlying requests library for more info on proxies.
Sending prepared transactions¶
The daemon connection may be used for two-step sending of transactions. For example, you may want to check the fee before broadcasting the transaction to the network.
To prepare a transaction, use transfer()
or transfer_multiple()
method of the wallet or
account, as described in the section about sending payments.
The only difference is that now you want to add the relay=False
argument.
In [6]: from monero.wallet import Wallet
In [7]: from monero.backends.jsonrpc import JSONRPCWallet
In [8]: wallet = Wallet(JSONRPCWallet(port=28088))
In [9]: wallet.balance()
Out[9]: Decimal('17.642325205670')
In [10]: txs = wallet.transfer('Bg1nUjsEx6UUByxr68o6gXcQRF58BpQyKauoZSo2HwubGErEnz9x6AS9o5ybmk3QmgeUpX3Msgm74QkwZKx2CeVWFrrZZqt', 10, relay=False)
Now the return value is a list of resulting transactions (usually just one) which may be inspected and validated.
In [11]: txs
Out[11]: [38964a0c8c3be041051464b413996ad8d696223dc34925d98156848ed76a3ae3]
In [12]: txs[0].fee
Out[12]: Decimal('0.003766080000')
If anything is not OK, just discard the transaction and create a new one. There’s no need to clean up anything in the wallet.
Once you have the transaction accepted, it’s time to post it to the daemon:
In [13]: result = daemon.send_transaction(txs[0])
In [14]: result
Out[14]:
{'double_spend': False,
'fee_too_low': False,
'invalid_input': False,
'invalid_output': False,
'low_mixin': False,
'not_rct': False,
'not_relayed': False,
'overspend': False,
'reason': '',
'status': 'OK',
'too_big': False}
No batching due to double spends¶
Warning
The workflow described above should not be used for preparing a batch of transactions to be sent later. The wallet doesn’t remember which inputs have been spent and will very likely use the same in the next transaction, resulting in a double spend and broadcast failure.
The following example shows such behavior:
In [15]: txs1 = wallet.transfer('BYSXsmmK44xdjNVMGprUW5Yau9tsc9SAMJrQsANjGgpk2RB83cvVhWjZAgYNwLgmhdPawATh5q1CTEoLGKZSeZqtThefV7D', 1, relay=False)
In [16]: txs2 = wallet.transfer('Bd5m5wTjWdYSaLBKe4i2avJjuFLYMEUKpiiE86F83NFiDXKE7QseSRvS7efTtJu5xHiHm5XmxgB2mfLu7NFrG7e3UTYRzEf', 2, relay=False)
In [17]: txs1, txs2
Out[17]:
([315901f250a1018e89e1fc2b3953bd5acfdfa759f843cf5a38306a2255de6d54],
[2bd978172226b486badc8a9dcbafb04acb4760c3f2a5794c694fee8575739c6e])
In [18]: daemon.send_transaction(txs1[0])
Out[18]:
{'double_spend': False,
'fee_too_low': False,
'invalid_input': False,
'invalid_output': False,
'low_mixin': False,
'not_rct': False,
'not_relayed': False,
'overspend': False,
'reason': '',
'status': 'OK',
'too_big': False}
In [19]: daemon.send_transaction(txs2[0])
---------------------------------------------------------------------------
TransactionBroadcastError Traceback (most recent call last)
<ipython-input-22-f24dc5d51c78> in <module>()
----> 1 daemon.send_transaction(txs2[0])
~/devel/monero-python/monero/daemon.py in send_transaction(self, tx, relay)
10
11 def send_transaction(self, tx, relay=True):
---> 12 return self._backend.send_transaction(tx.blob, relay=relay)
13
14 def mempool(self):
~/devel/monero-python/monero/backends/jsonrpc.py in send_transaction(self, blob, relay)
36 raise exceptions.TransactionBroadcastError(
37 "{status}: {reason}".format(**res),
---> 38 details=res)
39
40 def mempool(self):
TransactionBroadcastError: Failed: double spend
The second transaction failed because it used the same inputs as the previous one. The daemon checks all incoming transactions for possible double-spends and rejects them if such conflict is discovered.
Other RPC Commands¶
Any RPC commands not available in the Daemon class, are likely available in the JSONRPCDaemon class. The official Monero Daemon RPC Documentation can be found here <https://www.getmonero.org/resources/developer-guides/daemon-rpc.html>. At the time of writing, all the RPC commands from the documentation have been implemented in JSONRPCDaemon, with the exception of any .bin commands, /get_txpool_backlog, and /get_output_distribution. These methods share the same name as their corresponding RPC names, and unlike the Daemon methods, the methods in JSONRPCDaemon are designed to be lower-level. As such, the return values of these methods reflect the raw JSON objects returned by the daemon. An example:
[In 20]: from monero.backends.jsonrpc import JSONRPCDaemon
[In 21]: daemon = JSONRPCDaemon(host='192.168.0.50')
[In 22]: sync_info = daemon.sync_info()
[In 23]: sync_info['height']
[Out 23]: 2304844
[In 24]: daemon.get_bans()
[Out 24]:
{
"bans": [
{
"host": "145.239.118.5",
"ip": 91680657,
"seconds": 72260
},
{
"host": "146.59.156.116",
"ip": 1956395922,
"seconds": 69397
}
],
"status": "OK",
"untrusted": False
}