import six
import json
import requests
[docs]class RPCException(Exception):
""" Base class for RPC errors """
[docs]class Client(object):
"""
Nano (RaiBlocks) node RPC client
:param host: RPC server host, defaults to `'http://localhost:7076'`
:param session: optional :py:class:`requests.Session` session to use for this client
>>> from nano.rpc import Client
>>> rpc = Client('http://localhost:7076')
>>> rpc.version()
{
'rpc_version': 1,
'store_version': 10,
'node_vendor': 'RaiBlocks 9.0'
}
"""
def __init__(self, host='http://localhost:7076', session=None):
"""
Initialize the Nano (RaiBlocks) RPC client
:param host: location of the RPC server eg. http://localhost:7076
:type host: str
:param session: optional `requests` session to use for this client
:type host: :py:class:`requests.Session`
"""
if not session:
session = requests.Session()
self.session = session
self.host = host
[docs] def call(self, action, params=None):
"""
Makes an RPC call to the server and returns the json response
:param action: RPC method to call
:type action: str
:param params: Dict of arguments to send with RPC call
:type params: dict
:raises: :py:exc:`nano.rpc.RPCException`
:raises: :py:exc:`requests.exceptions.RequestException`
>>> rpc.call(
... action='account_balance',
... params={
... 'account': xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3'
... })
{'balance': '325586539664609129644855132177',
'pending': '2309370940000000000000000000000000'}
"""
params = params or {}
params['action'] = action
resp = self.session.post(self.host, json=params)
result = resp.json()
if 'error' in result:
raise RPCException(result['error'])
return result
def _process_value(self, value, type):
"""
Process a value that will be sent to backend
:param value: the value to return
:param type: hint for what sort of value this is
:type type: str
"""
if not isinstance(value, six.string_types + (list,)):
value = json.dumps(value)
return value
[docs] @doc_metadata(categories=['account'])
def account_balance(self, account):
"""
Returns how many RAW is owned and how many have not yet been received
by **account**
:param account: Account id to return balance of
:type account: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.account_balance(
... account="xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000"
... )
{
"balance": 10000,
"pending": 10000
}
"""
account = self._process_value(account, 'account')
payload = {
"account": account,
}
resp = self.call('account_balance', payload)
for k, v in resp.items():
resp[k] = int(v)
return resp
[docs] @doc_metadata(categories=['account'])
def accounts_balances(self, accounts):
"""
Returns how many RAW is owned and how many have not yet been received
by **accounts** list
:param accounts: list of accounts to return balances for
:type accounts: list of str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.accounts_balances(
... accounts=[
... "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
... "xrb_3i1aq1cchnmbn9x5rsbap8b15akfh7wj7pwskuzi7ahz8oq6cobd99d4r3b7"
... ]
... )
{
"xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000": {
"balance": 10000,
"pending": 10000
},
"xrb_3i1aq1cchnmbn9x5rsbap8b15akfh7wj7pwskuzi7ahz8oq6cobd99d4r3b7": {
"balance": 10000000,
"pending": 0
}
}
"""
accounts = self._process_value(accounts, 'list')
payload = {
"accounts": accounts,
}
resp = self.call('accounts_balances', payload)
accounts_balances = resp.get('balances') or {}
for account, balances in accounts_balances.items():
for k in balances:
balances[k] = int(balances[k])
return accounts_balances
[docs] @doc_metadata(categories=['account'])
def account_block_count(self, account):
"""
Get number of blocks for a specific **account**
:param account: Account to get number of blocks for
:type account: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.account_block_count(account="xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3")
19
"""
account = self._process_value(account, 'account')
payload = {
"account": account,
}
resp = self.call('account_block_count', payload)
return int(resp['block_count'])
[docs] @doc_metadata(categories=['wallet', 'account'])
def accounts_create(self, wallet, count, work=True):
"""
Creates new accounts, insert next deterministic keys in **wallet** up
to **count**
.. enable_control required
.. version 8.0 required
:param wallet: Wallet to create new accounts in
:type wallet: str
:param count: Number of accounts to create
:type count: int
:param work: If false, disables work generation after creating account
:type work: bool
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.accounts_create(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... count=2
... )
[
"xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"xrb_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3s00000000"
]
"""
wallet = self._process_value(wallet, 'wallet')
count = self._process_value(count, 'int')
payload = {
"wallet": wallet,
"count": count,
}
if not work:
payload['work'] = self._process_value(work, 'strbool')
resp = self.call('accounts_create', payload)
return resp.get('accounts') or []
[docs] @doc_metadata(categories=['account'])
def accounts_frontiers(self, accounts):
"""
Returns a list of pairs of account and block hash representing the
head block for **accounts** list
:param accounts: Accounts to return frontier blocks for
:type accounts: list of str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.accounts_frontiers(
... accounts=[
... "xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3",
... "xrb_3i1aq1cchnmbn9x5rsbap8b15akfh7wj7pwskuzi7ahz8oq6cobd99d4r3b7"
... ]
... )
{
"xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3":
"791AF413173EEE674A6FCF633B5DFC0F3C33F397F0DA08E987D9E0741D40D81A",
"xrb_3i1aq1cchnmbn9x5rsbap8b15akfh7wj7pwskuzi7ahz8oq6cobd99d4r3b7":
"6A32397F4E95AF025DE29D9BF1ACE864D5404362258E06489FABDBA9DCCC046F"
}
"""
accounts = self._process_value(accounts, 'list')
payload = {
"accounts": accounts,
}
resp = self.call('accounts_frontiers', payload)
return resp.get('frontiers') or {}
[docs] @doc_metadata(categories=['account'])
def account_info(self, account, representative=False, weight=False,
pending=False):
"""
Returns frontier, open block, change representative block, balance,
last modified timestamp from local database & block count for
**account**
:param account: Account to return info for
:type account: str
:param representative: if True, also returns the representative block
:type representative: bool
:param weight: if True, also returns the voting weight
:type weight: bool
:param pending: if True, also returns the pending balance
:type pending: bool
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.account_info(
... account="xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3"
... )
{
"frontier": "FF84533A571D953A596EA401FD41743AC85D04F406E76FDE4408EAED50B473C5",
"open_block": "991CF190094C00F0B68E2E5F75F6BEE95A2E0BD93CEAA4A6734DB9F19B728948",
"representative_block": "991CF190094C00F0B68E2E5F75F6BEE95A2E0BD93CEAA4A6734DB9F19B728948",
"balance": "235580100176034320859259343606608761791",
"modified_timestamp": "1501793775",
"block_count": "33"
}
"""
account = self._process_value(account, 'account')
payload = {
"account": account,
}
if representative:
payload['representative'] = self._process_value(representative, 'strbool')
if weight:
payload['weight'] = self._process_value(weight, 'strbool')
if pending:
payload['pending'] = self._process_value(pending, 'strbool')
resp = self.call('account_info', payload)
for key in ('modified_timestamp', 'block_count', 'balance', 'pending', 'weight'):
if key in resp:
resp[key] = int(resp[key])
return resp
[docs] @doc_metadata(categories=['wallet', 'account'])
def account_create(self, wallet, work=True):
"""
Creates a new account, insert next deterministic key in **wallet**
.. enable_control required
:param wallet: Wallet to insert new account into
:type wallet: str
:param work: If false, disables work generation after creating account
:type work: bool
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.account_create(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
"xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000"
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
if not work:
payload['work'] = self._process_value(work, 'strbool')
resp = self.call('account_create', payload)
return resp['account']
[docs] @doc_metadata(categories=['account'])
def account_get(self, key):
"""
Get account number for the **public key**
:param key: Public key to get account for
:type key: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.account_get(
... key="3068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039"
... )
"xrb_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx"
"""
key = self._process_value(key, 'publickey')
payload = {
"key": key,
}
resp = self.call('account_get', payload)
return resp['account']
[docs] @doc_metadata(categories=['account'])
def account_history(self, account, count):
"""
Reports send/receive information for a **account**
:param account: Account to get send/receive information for
:type account: str
:param count: number of blocks to return
:type count: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.account_history(
... account="xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
... count=1
... )
[
{
"hash": "000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
"type": "receive",
"account": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"amount": 100000000000000000000000000000000
}
]
"""
account = self._process_value(account, 'account')
count = self._process_value(count, 'int')
payload = {
"account": account,
"count": count,
}
resp = self.call('account_history', payload)
history = resp.get('history') or []
for entry in history:
entry['amount'] = int(entry['amount'])
return history
[docs] @doc_metadata(categories=['wallet', 'account'])
def account_list(self, wallet):
"""
Lists all the accounts inside **wallet**
:param wallet: Wallet to get account list for
:type wallet: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.account_list(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
[
"xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000"
]
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
resp = self.call('account_list', payload)
return resp.get('accounts') or []
[docs] @doc_metadata(categories=['wallet', 'account'])
def account_move(self, source, wallet, accounts):
"""
Moves **accounts** from **source** to **wallet**
.. enable_control required
:param source: wallet to move accounts from
:type source: str
:param wallet: wallet to move accounts to
:type wallet: str
:param accounts: accounts to move
:type accounts: list of str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.account_move(
... source="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... accounts=[
... "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000"
... ]
... )
True
"""
wallet = self._process_value(wallet, 'wallet')
source = self._process_value(source, 'wallet')
accounts = self._process_value(accounts, 'list')
payload = {
"wallet": wallet,
"source": source,
"accounts": accounts,
}
resp = self.call('account_move', payload)
return resp['moved'] == '1'
[docs] @doc_metadata(categories=['account'])
def accounts_pending(self, accounts, count=None, threshold=None, source=False):
"""
Returns a list of block hashes which have not yet been received by
these **accounts**
:param accounts: Accounts to return list of block hashes for
:type accounts: list of str
:param count: Max number of blocks to returns
:type count: int
:param threshold: Minimum amount in raw per block
:type threshold: int
:param source: if True returns the source as well
:type source: bool
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.accounts_pending(
... accounts=[
... "xrb_1111111111111111111111111111111111111111111111111117353trpda",
... "xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3"
... ],
... count=1
... )
{
"xrb_1111111111111111111111111111111111111111111111111117353trpda": [
"142A538F36833D1CC78B94E11C766F75818F8B940771335C6C1B8AB880C5BB1D"
],
"xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3": [
"4C1FEEF0BEA7F50BE35489A1233FE002B212DEA554B55B1B470D78BD8F210C74"
]
}
"""
payload = {
"accounts": accounts,
}
accounts = self._process_value(accounts, 'list')
if count is not None:
payload['count'] = self._process_value(count, 'int')
if threshold is not None:
payload['threshold'] = self._process_value(threshold, 'int')
if source:
payload['source'] = self._process_value(source, 'strbool')
resp = self.call('accounts_pending', payload)
blocks = resp.get('blocks') or {}
for account, data in blocks.items():
if isinstance(data, list): # list of block hashes, no change needed
continue
if not data:
blocks[account] = [] # convert a "" response to []
continue
for key, value in data.items():
if isinstance(value, six.string_types): # amount
data[key] = int(value)
elif isinstance(value, dict): # dict with "amount" and "source"
for key in ('amount',):
if key in value:
value[key] = int(value[key])
return blocks
[docs] @doc_metadata(categories=['account'])
def account_key(self, account):
"""
Get the public key for **account**
:param account: Account to get public key for
:type account: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.account_key(
... account="xrb_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx"
... )
"3068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039"
"""
account = self._process_value(account, 'account')
payload = {
"account": account,
}
resp = self.call('account_key', payload)
return resp['key']
[docs] @doc_metadata(categories=['account', 'wallet'])
def account_remove(self, wallet, account):
"""
Remove **account** from **wallet**
.. enable_control required
:param wallet: Wallet to remove account from
:type wallet: str
:param account: Account to remove
:type account: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.account_remove(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... account="xrb_39a73oy5ungrhxy5z5oao1xso4zo7dmgpjd4u74xcrx3r1w6rtazuouw6qfi"
... )
True
"""
wallet = self._process_value(wallet, 'wallet')
account = self._process_value(account, 'account')
payload = {
"wallet": wallet,
"account": account,
}
resp = self.call('account_remove', payload)
return resp['removed'] == '1'
[docs] @doc_metadata(categories=['account'])
def account_representative(self, account):
"""
Returns the representative for **account**
:param account: Account to get representative for
:type account: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.account_representative(
... account="xrb_39a73oy5ungrhxy5z5oao1xso4zo7dmgpjd4u74xcrx3r1w6rtazuouw6qfi"
)
"xrb_16u1uufyoig8777y6r8iqjtrw8sg8maqrm36zzcm95jmbd9i9aj5i8abr8u5"
"""
account = self._process_value(account, 'account')
payload = {
"account": account,
}
resp = self.call('account_representative', payload)
return resp['representative']
[docs] @doc_metadata(categories=['wallet', 'account'])
def account_representative_set(self, wallet, account, representative, work=None):
"""
Sets the representative for **account** in **wallet**
.. enable_control required
:param wallet: Wallet to use for account
:type wallet: str
:param account: Account to set representative for
:type account: str
:param representative: Representative to set to
:type representative: str
:param work: If set, is used as the work for the block
:type work: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.account_representative_set(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... account="xrb_39a73oy5ungrhxy5z5oao1xso4zo7dmgpjd4u74xcrx3r1w6rtazuouw6qfi",
... representative="xrb_16u1uufyoig8777y6r8iqjtrw8sg8maqrm36zzcm95jmbd9i9aj5i8abr8u5"
... )
"000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
"""
wallet = self._process_value(wallet, 'wallet')
account = self._process_value(account, 'account')
representative = self._process_value(representative, 'account')
payload = {
"wallet": wallet,
"account": account,
"representative": representative,
}
if work is not None:
payload['work'] = self._process_value(work, 'work')
resp = self.call('account_representative_set', payload)
return resp['block']
[docs] @doc_metadata(categories=['account'])
def account_weight(self, account):
"""
Returns the voting weight for **account**
:param account: Account to get voting weight for
:type account: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.account_weight(
... account="xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000"
... )
10000
"""
account = self._process_value(account, 'account')
payload = {
"account": account,
}
resp = self.call('account_weight', payload)
return int(resp['weight'])
[docs] @doc_metadata(categories=['global'])
def available_supply(self):
"""
Returns how many rai are in the public supply
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.available_supply()
10000
"""
resp = self.call('available_supply')
return int(resp['available'])
[docs] @doc_metadata(categories=['block'])
def block(self, hash):
"""
Retrieves a json representation of **block**
:param hash: Hash of block to return representation for
:type hash: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.block(
... hash="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
{
"account": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"work": "0000000000000000",
"source": "FA5B51D063BADDF345EFD7EF0D3C5FB115C85B1EF4CDE89D8B7DF3EAF60A04A4",
"representative": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"signature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"type": "open"
}
"""
hash = self._process_value(hash, 'block')
payload = {
"hash": hash,
}
resp = self.call('block', payload)
return json.loads(resp['contents'])
[docs] @doc_metadata(categories=['block'])
def blocks(self, hashes):
"""
Retrieves a json representations of **blocks**
:param hashes: List of block hashes to return
:type hashes: list of str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.blocks(
... hashes=["000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"]
... )
{
"000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F": {
"account": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"work": "0000000000000000",
"source": "FA5B51D063BADDF345EFD7EF0D3C5FB115C85B1EF4CDE89D8B7DF3EAF60A04A4",
"representative": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"signature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"type": "open"
}
}
"""
hashes = self._process_value(hashes, 'list')
payload = {
"hashes": hashes,
}
resp = self.call('blocks', payload)
blocks = resp.get('blocks') or {}
for k, v in blocks.items():
blocks[k] = json.loads(v)
return blocks
[docs] @doc_metadata(categories=['block'])
def blocks_info(self, hashes, pending=False, source=False):
"""
Retrieves a json representations of **blocks** with transaction
**amount** & block **account**
:param hashes: List of block hashes to return info for
:type hashes: list of str
:param pending: If true, returns pending amount as well
:type pending: bool
:param source: If true, returns source account as well
:type source: bool
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.blocks_info(hashes=["000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"])
{
"000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F": {
"block_account": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"amount": "1000000000000000000000000000000",
"contents": {
"account": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"work": "0000000000000000",
"source": "FA5B51D063BADDF345EFD7EF0D3C5FB115C85B1EF4CDE89D8B7DF3EAF60A04A4",
"representative": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"signature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"type": "open"
}
}
}
"""
hashes = self._process_value(hashes, 'list')
payload = {
"hashes": hashes,
}
if pending:
payload['pending'] = self._process_value(pending, 'strbool')
if source:
payload['source'] = self._process_value(source, 'strbool')
resp = self.call('blocks_info', payload)
blocks = resp.get('blocks') or {}
for block, data in blocks.items():
data['contents'] = json.loads(data['contents'])
for key in ('amount', 'pending'):
if key in data:
data[key] = int(data[key])
return blocks
[docs] @doc_metadata(categories=['account', 'block'])
def block_account(self, hash):
"""
Returns the account containing block
:param hash: Hash of the block to return account for
:type hash: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.block_account(
... hash="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
"xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000"
"""
hash = self._process_value(hash, 'block')
payload = {
"hash": hash,
}
resp = self.call('block_account', payload)
return resp['account']
[docs] @doc_metadata(categories=['global', 'block'])
def block_count(self):
"""
Reports the number of blocks in the ledger and unchecked synchronizing
blocks
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.block_count()
{
"count": 1000,
"unchecked": 10
}
"""
resp = self.call('block_count')
for k, v in resp.items():
resp[k] = int(v)
return resp
[docs] @doc_metadata(categories=['global', 'block'])
def block_count_type(self):
"""
Reports the number of blocks in the ledger by type (send, receive,
open, change)
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.block_count_type()
{
"send": 1000,
"receive": 900,
"open": 100,
"change": 50
}
"""
resp = self.call('block_count_type')
for k, v in resp.items():
resp[k] = int(v)
return resp
[docs] @doc_metadata(categories=['block'])
def block_create(self,
type,
account,
wallet=None,
representative=None,
key=None,
destination=None,
amount=None,
balance=None,
previous=None,
source=None,
work=None):
"""
Creates a json representations of new block based on input data &
signed with private key or account in **wallet** for offline signing
.. enable_control required
.. version 8.1 required
:param type: Type of block to create one of **open**, **receive**,
**change**, **send**
:type type: str
:param account: Account for the signed block
:type account: str
:param wallet: Wallet to use
:type wallet: str
:param representative: Representative account for **open** and
**change** blocks
:type representative: str
:param key: Private key to use to open account for **open** blocks
:type key: str
:param destination: Destination account for **send** blocks
:type destination: str
:param amount: Amount in raw for **send** blocks
:type amount: int
:param balance: Balance in raw of account for **send** blocks
:type balance: int
:param previous: Previous block hash for **receive**, **send**
and **change** blocks
:type previous: str
:param source: Source block for **open** and **receive** blocks
:type source: str
:param work: Work value to use for block from external source
:type work: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.block_create(
... type="open",
... account="xrb_3kdbxitaj7f6mrir6miiwtw4muhcc58e6tn5st6rfaxsdnb7gr4roudwn951",
... source="19D3D919475DEED4696B5D13018151D1AF88B2BD3BCFF048B45031C1F36D1858",
... representative="xrb_1hza3f7wiiqa7ig3jczyxj5yo86yegcmqk3criaz838j91sxcckpfhbhhra1",
... key="0000000000000000000000000000000000000000000000000000000000000001"
... )
{
"block": {
"account": "xrb_3kdbxitaj7f6mrir6miiwtw4muhcc58e6tn5st6rfaxsdnb7gr4roudwn951",
"representative": "xrb_1hza3f7wiiqa7ig3jczyxj5yo86yegcmqk3criaz838j91sxcckpfhbhhra1",
"signature": "5974324F8CC42DA56F62FC212A17886BDCB18DE363D04DA84EEDC99CB4A33919D14A2CF9DE9D534FAA6D0B91D01F0622205D898293525E692586C84F2DCF9208",
"source": "19D3D919475DEED4696B5D13018151D1AF88B2BD3BCFF048B45031C1F36D1858",
"type": "open",
"work": "4ec76c9bda2325ed"
},
"hash": "F47B23107E5F34B2CE06F562B5C435DF72A533251CB414C51B2B62A8F63A00E4"
}
>>> rpc.block_create(
... type="receive",
... account="xrb_3kdbxitaj7f6mrir6miiwtw4muhcc58e6tn5st6rfaxsdnb7gr4roudwn951",
... previous="F47B23107E5F34B2CE06F562B5C435DF72A533251CB414C51B2B62A8F63A00E4",
... source="19D3D919475DEED4696B5D13018151D1AF88B2BD3BCFF048B45031C1F36D1858",
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... )
{
"block": {
"previous": "F47B23107E5F34B2CE06F562B5C435DF72A533251CB414C51B2B62A8F63A00E4",
"signature": "A13FD22527771667D5DFF33D69787D734836A3561D8A490C1F4917A05D77EA09860461D5FBFC99246A4EAB5627F119AD477598E22EE021C4711FACF4F3C80D0E",
"source": "19D3D919475DEED4696B5D13018151D1AF88B2BD3BCFF048B45031C1F36D1858",
"type": "receive",
"work": "6acb5dd43a38d76a"
},
"hash": "314BA8D9057678C1F53371C2DB3026C1FAC01EC8E7802FD9A2E8130FC523429E"
}
>>> rpc.block_create(
... type="send",
... account="xrb_3kdbxitaj7f6mrir6miiwtw4muhcc58e6tn5st6rfaxsdnb7gr4roudwn951",
... amount=10000000000000000000000000000000,
... balance=20000000000000000000000000000000,
... destination="xrb_18gmu6engqhgtjnppqam181o5nfhj4sdtgyhy36dan3jr9spt84rzwmktafc",
... previous="314BA8D9057678C1F53371C2DB3026C1FAC01EC8E7802FD9A2E8130FC523429E",
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... work="478563b2d9facfd4",
... )
{
"block": {
"balance": "0000007E37BE2022C0914B2680000000",
"destination": "xrb_18gmu6engqhgtjnppqam181o5nfhj4sdtgyhy36dan3jr9spt84rzwmktafc",
"previous": "314BA8D9057678C1F53371C2DB3026C1FAC01EC8E7802FD9A2E8130FC523429E",
"signature": "F19CA177EFA8692C8CBF7478CE3213F56E4A85DF760DA7A9E69141849831F8FD79BA9ED89CEC807B690FB4AA42D5008F9DBA7115E63C935401F1F0EFA547BC00",
"type": "send",
"work": "478563b2d9facfd4"
},
"hash": "F958305C0FF0551421D4ABEDCCF302079D020A0A3833E33F185E2B0415D4567A"
}
>>> rpc.block_create(
... type="change",
... account="xrb_3kdbxitaj7f6mrir6miiwtw4muhcc58e6tn5st6rfaxsdnb7gr4roudwn951",
... representative="xrb_18gmu6engqhgtjnppqam181o5nfhj4sdtgyhy36dan3jr9spt84rzwmktafc",
... previous="F958305C0FF0551421D4ABEDCCF302079D020A0A3833E33F185E2B0415D4567A",
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... )
{
"block": {
"previous": "F958305C0FF0551421D4ABEDCCF302079D020A0A3833E33F185E2B0415D4567A",
"representative": "xrb_18gmu6engqhgtjnppqam181o5nfhj4sdtgyhy36dan3jr9spt84rzwmktafc",
"signature": "98B4D56881D9A88B170A6B2976AE21900C26A27F0E2C338D93FDED56183B73D19AA5BEB48E43FCBB8FF8293FDD368CEF50600FECEFD490A0855ED702ED209E04",
"type": "change",
"work": "55e5b7a83edc3f4f"
},
"hash": "654FA425CEBFC9E7726089E4EDE7A105462D93DBC915FFB70B50909920A7D286"
}
"""
payload = {
"type": self._process_value(type, 'blocktype'),
"account": self._process_value(account, 'account'),
}
if representative is not None:
payload['representative'] = self._process_value(representative, 'account')
if key is not None:
payload['key'] = self._process_value(key, 'privatekey')
if source is not None:
payload['source'] = self._process_value(source, 'block')
if destination is not None:
payload['destination'] = self._process_value(destination, 'account')
if amount is not None:
payload['amount'] = self._process_value(amount, 'int')
if balance is not None:
payload['balance'] = self._process_value(balance, 'int')
if previous is not None:
payload['previous'] = self._process_value(previous, 'block')
if wallet is not None:
payload['wallet'] = self._process_value(wallet, 'wallet')
if work is not None:
payload['work'] = self._process_value(work, 'work')
resp = self.call('block_create', payload)
resp['block'] = json.loads(resp['block'])
return resp
[docs] @doc_metadata(categories=['node'])
def bootstrap(self, address, port):
"""
Initialize bootstrap to specific **IP address** and **port**
:param address: Ip address to bootstrap
:type address: str
:param port: Port to bootstrap
:type port: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.bootstrap(address="::ffff:138.201.94.249", port="7075")
True
"""
address = self._process_value(address, 'ipaddr')
port = self._process_value(port, 'int')
payload = {
"address": address,
"port": port,
}
resp = self.call('bootstrap', payload)
return 'success' in resp
[docs] @doc_metadata(categories=['node'])
def bootstrap_any(self):
"""
Initialize multi-connection bootstrap to random peers
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.bootstrap_any()
True
"""
resp = self.call('bootstrap_any')
return 'success' in resp
[docs] @doc_metadata(categories=['block'])
def chain(self, block, count):
"""
Returns a list of block hashes in the account chain starting at
**block** up to **count**
:param block: Block hash to start at
:type block: str
:param count: Number of blocks to return up to
:type count: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.chain(
... block="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... count=1
... )
[
"000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
]
"""
block = self._process_value(block, 'block')
count = self._process_value(count, 'int')
payload = {
"block": block,
"count": count,
}
resp = self.call('chain', payload)
return resp.get('blocks') or []
[docs] @doc_metadata(categories=['account'])
def delegators(self, account):
"""
Returns a list of pairs of delegator names given **account** a
representative and its balance
.. version 8.0 required
:param account: Account to return delegators for
:type account: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.delegators(
... account="xrb_1111111111111111111111111111111111111111111111111117353trpda"
... )
{
"xrb_13bqhi1cdqq8yb9szneoc38qk899d58i5rcrgdk5mkdm86hekpoez3zxw5sd":
"500000000000000000000000000000000000",
"xrb_17k6ug685154an8gri9whhe5kb5z1mf5w6y39gokc1657sh95fegm8ht1zpn":
"961647970820730000000000000000000000"
}
"""
account = self._process_value(account, 'account')
payload = {
"account": account,
}
resp = self.call('delegators', payload)
delegators = resp.get('delegators') or {}
for k, v in delegators.items():
delegators[k] = int(v)
return delegators
[docs] @doc_metadata(categories=['account'])
def delegators_count(self, account):
"""
Get number of delegators for a specific representative **account**
.. version 8.0 required
:param account: Account to get number of delegators for
:type account: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.delegators_count(
... account="xrb_1111111111111111111111111111111111111111111111111117353trpda"
... )
2
"""
account = self._process_value(account, 'account')
payload = {
"account": account,
}
resp = self.call('delegators_count', payload)
return int(resp['count'])
[docs] @doc_metadata(categories=['utility'])
def deterministic_key(self, seed, index):
"""
Derive deterministic keypair from **seed** based on **index**
:param seed: Seed used to get keypair
:type seed: str
:param index: Index of the generated keypair
:type index: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.deterministic_key(
... seed="0000000000000000000000000000000000000000000000000000000000000000",
... index=0
... )
{
"private": "9F0E444C69F77A49BD0BE89DB92C38FE713E0963165CCA12FAF5712D7657120F",
"public": "C008B814A7D269A1FA3C6528B19201A24D797912DB9996FF02A1FF356E45552B",
"account": "xrb_3i1aq1cchnmbn9x5rsbap8b15akfh7wj7pwskuzi7ahz8oq6cobd99d4r3b7"
}
"""
seed = self._process_value(seed, 'seed')
index = self._process_value(index, 'int')
payload = {
"seed": seed,
"index": index,
}
resp = self.call('deterministic_key', payload)
return resp
[docs] @doc_metadata(categories=['account'])
def frontiers(self, account, count):
"""
Returns a list of pairs of account and block hash representing the
head block starting at **account** up to **count**
:param account: Account to get frontier blocks for
:type account: str
:param count: Max amount to return
:type count: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.frontiers(
... account="xrb_1111111111111111111111111111111111111111111111111111hifc8npp",
... count=1
... )
{
"xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000":
"000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
}
"""
account = self._process_value(account, 'account')
count = self._process_value(count, 'int')
payload = {
"account": account,
"count": count,
}
resp = self.call('frontiers', payload)
return resp.get('frontiers') or {}
[docs] @doc_metadata(categories=['global'])
def frontier_count(self):
"""
Reports the number of accounts in the ledger
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.frontier_count()
1000
"""
resp = self.call('frontier_count')
return int(resp['count'])
[docs] @doc_metadata(categories=['block'])
def history(self, hash, count):
"""
Reports send/receive information for a chain of blocks
:param hash: Hash of block to receive history for
:type hash: str
:param count: Max number of blocks to return
:type count: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.history(
... hash="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... count=1
... )
[
{
"hash": "000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
"type": "receive",
"account": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"amount": "100000000000000000000000000000000"
}
]
"""
hash = self._process_value(hash, 'block')
count = self._process_value(count, 'int')
payload = {
"hash": hash,
"count": count,
}
resp = self.call('history', payload)
history = resp.get('history') or []
for entry in history:
entry['amount'] = int(entry['amount'])
return history
[docs] @doc_metadata(categories=['utility'])
def mrai_from_raw(self, amount):
"""
Divide a raw amount down by the Mrai ratio.
:param amount: Amount in raw to convert to Mrai
:type amount: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.mrai_from_raw(amount=1000000000000000000000000000000)
1
"""
amount = self._process_value(amount, 'int')
payload = {
"amount": amount,
}
resp = self.call('mrai_from_raw', payload)
return int(resp['amount'])
[docs] @doc_metadata(categories=['utility'])
def mrai_to_raw(self, amount):
"""
Multiply an Mrai amount by the Mrai ratio.
:param amount: Amount in Mrai to convert to raw
:type amount: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.mrai_to_raw(amount=1)
1000000000000000000000000000000
"""
amount = self._process_value(amount, 'int')
payload = {
"amount": amount,
}
resp = self.call('mrai_to_raw', payload)
return int(resp['amount'])
[docs] @doc_metadata(categories=['utility'])
def krai_from_raw(self, amount):
"""
Divide a raw amount down by the krai ratio.
:param amount: Amount in raw to convert to krai
:type amount: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.krai_from_raw(amount=1000000000000000000000000000)
1
"""
amount = self._process_value(amount, 'int')
payload = {
"amount": amount,
}
resp = self.call('krai_from_raw', payload)
return int(resp['amount'])
[docs] @doc_metadata(categories=['utility'])
def krai_to_raw(self, amount):
"""
Multiply an krai amount by the krai ratio.
:param amount: Amount in krai to convert to raw
:type amount: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.krai_to_raw(amount=1)
1000000000000000000000000000
"""
amount = self._process_value(amount, 'int')
payload = {
"amount": amount,
}
resp = self.call('krai_to_raw', payload)
return int(resp['amount'])
[docs] @doc_metadata(categories=['utility'])
def rai_from_raw(self, amount):
"""
Divide a raw amount down by the rai ratio.
:param amount: Amount in raw to convert to rai
:type amount: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.rai_from_raw(amount=1000000000000000000000000)
1
"""
amount = self._process_value(amount, 'int')
payload = {
"amount": amount,
}
resp = self.call('rai_from_raw', payload)
return int(resp['amount'])
[docs] @doc_metadata(categories=['utility'])
def rai_to_raw(self, amount):
"""
Multiply an rai amount by the rai ratio.
:param amount: Amount in rai to convert to raw
:type amount: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.rai_to_raw(amount=1)
1000000000000000000000000
"""
amount = self._process_value(amount, 'int')
payload = {
"amount": amount,
}
resp = self.call('rai_to_raw', payload)
return int(resp['amount'])
[docs] @doc_metadata(categories=['utility'])
def key_create(self):
"""
Generates an **adhoc random keypair**
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.key_create()
{
"private": "781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3",
"public": "3068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039",
"account": "xrb_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx"
}
"""
resp = self.call('key_create')
return resp
[docs] @doc_metadata(categories=['utility'])
def key_expand(self, key):
"""
Derive public key and account number from **private key**
:param key: Private key to generate account and public key of
:type key: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.key_expand(
key="781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3"
)
{
"private": "781186FB9EF17DB6E3D1056550D9FAE5D5BBADA6A6BC370E4CBB938B1DC71DA3",
"public": "3068BB1CA04525BB0E416C485FE6A67FD52540227D267CC8B6E8DA958A7FA039",
"account": "xrb_1e5aqegc1jb7qe964u4adzmcezyo6o146zb8hm6dft8tkp79za3sxwjym5rx"
}
"""
key = self._process_value(key, 'privatekey')
payload = {
"key": key,
}
resp = self.call('key_expand', payload)
return resp
[docs] @doc_metadata(categories=['node'])
def keepalive(self, address, port):
"""
Tells the node to send a keepalive packet to **address**:**port**
.. enable_control required
:param address: IP address of node to send keepalive packet to
:type address: str
:param port: Port of node to send keepalive packet to
:type port: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.keepalive(address="::ffff:192.168.1.1", port=1024)
True
"""
address = self._process_value(address, 'ipaddr')
port = self._process_value(port, 'int')
payload = {
"address": address,
"port": port,
}
resp = self.call('keepalive', payload)
return resp == {}
[docs] @doc_metadata(categories=['account'])
def ledger(self, account, count=None, representative=False, weight=False,
pending=False, sorting=False):
"""
Returns frontier, open block, change representative block, balance,
last modified timestamp from local database & block count starting at
**account** up to **count**
.. enable_control required
.. version 8.0 required
:param account: Account to return blocks for
:type account: str
:param count: Max number of blocks to return
:type count: int
:param representative: If true, returns the representative as well
:type representative: bool
:param weight: If true, returns the voting weight as well
:type weight: bool
:param pending: If true, returns the pending amount as well
:type pending: bool
:param sorting: If true, sorts the response by balance
:type sorting: bool
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.ledger(
... account="xrb_1111111111111111111111111111111111111111111111111111hifc8npp",
... count=1
... )
{
"xrb_11119gbh8hb4hj1duf7fdtfyf5s75okzxdgupgpgm1bj78ex3kgy7frt3s9n": {
"frontier": "E71AF3E9DD86BBD8B4620EFA63E065B34D358CFC091ACB4E103B965F95783321",
"open_block": "643B77F1ECEFBDBE1CC909872964C1DBBE23A6149BD3CEF2B50B76044659B60F",
"representative_block": "643B77F1ECEFBDBE1CC909872964C1DBBE23A6149BD3CEF2B50B76044659B60F",
"balance": 0,
"modified_timestamp": 1511476234,
"block_count": 2
}
}
"""
account = self._process_value(account, 'account')
payload = {
"account": account,
}
if count is not None:
payload['count'] = self._process_value(count, 'int')
if sorting:
payload['sorting'] = self._process_value(sorting, 'strbool')
if representative:
payload['representative'] = self._process_value(representative, 'strbool')
if weight:
payload['weight'] = self._process_value(weight, 'strbool')
if pending:
payload['pending'] = self._process_value(pending, 'strbool')
resp = self.call('ledger', payload)
accounts = resp.get('accounts') or {}
int_keys = (
'balance', 'modified_timestamp', 'block_count', 'weight', 'pending'
)
for account, frontier in accounts.items():
for key in int_keys:
if key in frontier:
frontier[key] = int(frontier[key])
return accounts
[docs] @doc_metadata(categories=['wallet'])
def payment_begin(self, wallet):
"""
Begin a new payment session. Searches wallet for an account that's
marked as available and has a 0 balance. If one is found, the account
number is returned and is marked as unavailable. If no account is
found, a new account is created, placed in the wallet, and returned.
:param wallet: Wallet to begin payment in
:type wallet: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.payment_begin(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
"xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000"
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
resp = self.call('payment_begin', payload)
return resp['account']
# NOTE(dan): Server RPC is broken here - it *should* return an
# 'error' for 'No wallet found', but it returns 'status' instead
# https://github.com/clemahieu/nano/blob/e9592e5/rai/node/rpc.cpp#L2238
[docs] @doc_metadata(categories=['wallet'])
def payment_init(self, wallet):
"""
Marks all accounts in wallet as available for being used as a payment
session.
:param wallet: Wallet to init payment in
:type wallet: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.payment_init(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
True
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
resp = self.call('payment_init', payload)
return resp['status'] == 'Ready'
[docs] @doc_metadata(categories=['wallet'])
def payment_end(self, account, wallet):
"""
End a payment session. Marks the account as available for use in a
payment session.
:param account: Account to mark available
:type account: str
:param wallet: Wallet to end payment session for
:type wallet: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.payment_end(
... account="xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
... wallet="FFFD1BAEC8EC20814BBB9059B393051AAA8380F9B5A2E6B2489A277D81789EEE"
... )
True
"""
account = self._process_value(account, 'account')
wallet = self._process_value(wallet, 'wallet')
payload = {
"account": account,
"wallet": wallet,
}
resp = self.call('payment_end', payload)
return resp == {}
[docs] @doc_metadata(categories=['account'])
def payment_wait(self, account, amount, timeout):
"""
Wait for payment of **amount** to arrive in **account** or until **timeout**
milliseconds have elapsed.
:param account: Account to wait for payment
:type account: str
:param amount: Amount in raw of funds to wait for payment to arrive
:type amount: int
:param timeout: Timeout in milliseconds to wait for
:type timeout: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.payment_wait(
... account="xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
... amount=1,
... timeout=1000
... )
True
"""
account = self._process_value(account, 'account')
amount = self._process_value(amount, 'int')
timeout = self._process_value(timeout, 'int')
payload = {
"account": account,
"amount": amount,
"timeout": timeout,
}
resp = self.call('payment_wait', payload)
return resp['status'] == 'success'
[docs] @doc_metadata(categories=['block'])
def process(self, block):
"""
Publish **block** to the network
:param block: Block to publish
:type block: dict or json
:raises: :py:exc:`nano.rpc.RPCException`
>>> block = {
"account": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"work": "0000000000000000",
"source": "FA5B51D063BADDF345EFD7EF0D3C5FB115C85B1EF4CDE89D8B7DF3EAF60A04A4",
"representative": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"signature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"type": "open"
}
>>> rpc.process(block=block)
"42A723D2B60462BF7C9A003FE9A70057D3A6355CA5F1D0A57581000000000000"
>>> rpc.process(json.dumps(block))
"42A723D2B60462BF7C9A003FE9A70057D3A6355CA5F1D0A57581000000000000"
"""
if isinstance(block, dict):
block = json.dumps(block, sort_keys=True)
payload = {
"block": block,
}
resp = self.call('process', payload)
return resp['hash']
[docs] @doc_metadata(categories=['wallet', 'account', 'block'])
def receive(self, wallet, account, block, work=None):
"""
Receive pending **block** for **account** in **wallet**
.. enable_control required
:param wallet: Wallet of account to receive block for
:type wallet: str
:param account: Account to receive block for
:type account: str
:param block: Block hash to receive
:type block: str
:param work: If set, uses this work for the receive block
:type work: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.receive(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... account="xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
... block="53EAA25CE28FA0E6D55EA9704B32604A736966255948594D55CBB05267CECD48",
... work="12041e830ad10de1"
... )
"EE5286AB32F580AB65FD84A69E107C69FBEB571DEC4D99297E19E3FA5529547B"
"""
wallet = self._process_value(wallet, 'wallet')
account = self._process_value(account, 'account')
block = self._process_value(block, 'block')
payload = {
"wallet": wallet,
"account": account,
"block": block,
}
if work:
payload['work'] = self._process_value(work, 'work')
resp = self.call('receive', payload)
return resp['block']
[docs] @doc_metadata(categories=['node'])
def receive_minimum(self):
"""
Returns receive minimum for node
.. enable_control required
.. version 8.0 required
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.receive_minimum()
1000000000000000000000000
"""
resp = self.call('receive_minimum')
return int(resp['amount'])
[docs] @doc_metadata(categories=['node'])
def receive_minimum_set(self, amount):
"""
Set **amount** as new receive minimum for node until restart
.. enable_control required
.. version 8.0 required
:param amount: Amount in raw to set as minimum to receive
:type amount: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.receive_minimum_set(amount=1000000000000000000000000000000)
True
"""
amount = self._process_value(amount, 'int')
payload = {
"amount": amount,
}
resp = self.call('receive_minimum_set', payload)
return 'success' in resp
[docs] @doc_metadata(categories=['global'])
def representatives(self, count=None, sorting=False):
"""
Returns a list of pairs of representative and its voting weight
:param count: Max amount of representatives to return
:type count: int
:param sorting: If true, sorts by weight
:type sorting: bool
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.representatives()
{
"xrb_1111111111111111111111111111111111111111111111111117353trpda":
3822372327060170000000000000000000000,
"xrb_1111111111111111111111111111111111111111111111111awsq94gtecn":
30999999999999999999999999000000,
"xrb_114nk4rwjctu6n6tr6g6ps61g1w3hdpjxfas4xj1tq6i8jyomc5d858xr1xi":
0
}
"""
payload = {}
if count is not None:
payload['count'] = self._process_value(count, 'int')
if sorting:
payload['sorting'] = self._process_value(sorting, 'strbool')
resp = self.call('representatives', payload)
representatives = resp.get('representatives') or {}
for k, v in representatives.items():
representatives[k] = int(v)
return representatives
[docs] @doc_metadata(categories=['node', 'block'])
def unchecked(self, count=None):
"""
Returns a list of pairs of unchecked synchronizing block hash and its
json representation up to **count**
.. version 8.0 required
:param count: Max amount of unchecked blocks to return
:type count: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.unchecked(count=1)
{
"000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F": {
"account": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"work": "0000000000000000",
"source": "FA5B51D063BADDF345EFD7EF0D3C5FB115C85B1EF4CDE89D8B7DF3EAF60A04A4",
"representative": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"signature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"type": "open"
}
}
"""
payload = {}
if count is not None:
payload["count"] = self._process_value(count, 'int')
resp = self.call('unchecked', payload)
blocks = resp.get('blocks') or {}
for block, block_json in blocks.items():
blocks[block] = json.loads(block_json)
return blocks
[docs] @doc_metadata(categories=['node', 'block'])
def unchecked_clear(self):
"""
Clear unchecked synchronizing blocks
.. enable_control required
.. version 8.0 required
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.unchecked_clear()
True
"""
resp = self.call('unchecked_clear')
return 'success' in resp
[docs] @doc_metadata(categories=['node', 'block'])
def unchecked_get(self, hash):
"""
Retrieves a json representation of unchecked synchronizing block by
**hash**
.. version 8.0 required
:param hash: Hash of unchecked block to get
:type hash: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.unchecked_get(
... hash="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
{
"account": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"work": "0000000000000000",
"source": "FA5B51D063BADDF345EFD7EF0D3C5FB115C85B1EF4CDE89D8B7DF3EAF60A04A4",
"representative": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"signature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"type": "open"
}
"""
hash = self._process_value(hash, 'block')
payload = {
"hash": hash,
}
resp = self.call('unchecked_get', payload)
return json.loads(resp['contents'])
[docs] @doc_metadata(categories=['node', 'block'])
def unchecked_keys(self, key=None, count=None):
"""
Retrieves unchecked database keys, blocks hashes & a json
representations of unchecked pending blocks starting from **key** up
to **count**
.. version 8.0 required
:param key: Starting key to return unchecked keys for
:type key: str
:param count: Max number of keys/blocks to return
:type count: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.unchecked_keys(
... key="FA5B51D063BADDF345EFD7EF0D3C5FB115C85B1EF4CDE89D8B7DF3EAF60A04A4",
... count=1
... )
[
{
"key": "FA5B51D063BADDF345EFD7EF0D3C5FB115C85B1EF4CDE89D8B7DF3EAF60A04A4",
"hash": "000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
"contents": {
"account": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"work": "0000000000000000",
"source": "FA5B51D063BADDF345EFD7EF0D3C5FB115C85B1EF4CDE89D8B7DF3EAF60A04A4",
"representative": "xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
"signature": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"type": "open"
}
}
]
"""
payload = {}
if key:
payload['key'] = self._process_value(key, 'publickey')
if count is not None:
payload['count'] = self._process_value(count, 'int')
resp = self.call('unchecked_keys', payload)
unchecked = resp.get('unchecked') or []
for entry in unchecked:
entry['contents'] = json.loads(entry['contents'])
return unchecked
[docs] @doc_metadata(categories=['account'])
def validate_account_number(self, account):
"""
Check whether **account** is a valid account number
:param account: Account number to check
:type account: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.validate_account_number(
... account="xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000"
... )
True
"""
account = self._process_value(account, 'account')
payload = {
"account": account,
}
resp = self.call('validate_account_number', payload)
return resp['valid'] == '1'
[docs] @doc_metadata(categories=['wallet'])
def wallet_representative(self, wallet):
"""
Returns the default representative for **wallet**
:param wallet: Wallet to get default representative account for
:type wallet: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_representative(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
"xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000"
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
resp = self.call('wallet_representative', payload)
return resp['representative']
[docs] @doc_metadata(categories=['wallet'])
def wallet_representative_set(self, wallet, representative):
"""
Sets the default **representative** for **wallet**
.. enable_control required
:param wallet: Wallet to set default representative account for
:type wallet: str
:param representative: Representative account to set for **wallet**
:type representative: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_representative_set(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... representative="xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000"
... )
True
"""
wallet = self._process_value(wallet, 'wallet')
representative = self._process_value(representative, 'account')
payload = {
"wallet": wallet,
"representative": representative,
}
resp = self.call('wallet_representative_set', payload)
return resp['set'] == '1'
[docs] @doc_metadata(categories=['wallet'])
def wallet_add(self, wallet, key, work=True):
"""
Add an adhoc private key **key** to **wallet**
.. enable_control required
:param wallet: Wallet to add private key to
:type wallet: str
:param key: Private key to add
:type key: str
:param work: If false, disables work generation
:type work: bool
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_add(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... key="34F0A37AAD20F4A260F0A5B3CB3D7FB50673212263E58A380BC10474BB039CE4"
... )
"xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000"
"""
wallet = self._process_value(wallet, 'wallet')
key = self._process_value(key, 'privatekey')
payload = {
"wallet": wallet,
"key": key,
}
if not work:
payload['work'] = self._process_value(work, 'strbool')
resp = self.call('wallet_add', payload)
return resp['account']
[docs] @doc_metadata(categories=['wallet'])
def wallet_balance_total(self, wallet):
"""
Returns the sum of all accounts balances in **wallet**
:param wallet: Wallet to return sum of balances for
:type wallet: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_balance_total(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
{
"balance": 10000,
"pending": 10000
}
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
resp = self.call('wallet_balance_total', payload)
for k, v in resp.items():
resp[k] = int(v)
return resp
[docs] @doc_metadata(categories=['wallet'])
def wallet_balances(self, wallet):
"""
Returns how many rai is owned and how many have not yet been received
by all accounts in **wallet**
:param wallet: Wallet to return balances for
:type wallet: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_balances(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
{
"xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000": {
"balance": 10000,
"pending": 10000
}
}
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
resp = self.call('wallet_balances', payload)
balances = resp.get('balances') or {}
for account, balance in balances.items():
for k, v in balances[account].items():
balances[account][k] = int(v)
return balances
[docs] @doc_metadata(categories=['wallet'])
def wallet_change_seed(self, wallet, seed):
"""
Changes seed for **wallet** to **seed**
.. enable_control required
:param wallet: Wallet to change seed for
:type wallet: str
:param seed: Seed to change wallet to
:type seed: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_change_seed(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... seed="74F2B37AAD20F4A260F0A5B3CB3D7FB51673212263E58A380BC10474BB039CEE"
... )
True
"""
wallet = self._process_value(wallet, 'wallet')
seed = self._process_value(seed, 'seed')
payload = {
"wallet": wallet,
"seed": seed,
}
resp = self.call('wallet_change_seed', payload)
return 'success' in resp
[docs] @doc_metadata(categories=['wallet'])
def wallet_contains(self, wallet, account):
"""
Check whether **wallet** contains **account**
:param wallet: Wallet to check contains **account**
:type wallet: str
:param account: Account to check exists in **wallet**
:type account: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_contains(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... account="xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000"
... )
True
"""
wallet = self._process_value(wallet, 'wallet')
account = self._process_value(account, 'account')
payload = {
"wallet": wallet,
"account": account,
}
resp = self.call('wallet_contains', payload)
return resp['exists'] == '1'
[docs] @doc_metadata(categories=['wallet'])
def wallet_create(self):
"""
Creates a new random wallet id
.. enable_control required
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_create()
"000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
"""
resp = self.call('wallet_create')
return resp['wallet']
[docs] @doc_metadata(categories=['wallet'])
def wallet_destroy(self, wallet):
"""
Destroys **wallet** and all contained accounts
.. enable_control required
:param wallet: Wallet to destroy
:type wallet: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_destroy(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
True
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
resp = self.call('wallet_destroy', payload)
return resp == {}
[docs] @doc_metadata(categories=['wallet'])
def wallet_export(self, wallet):
"""
Return a json representation of **wallet**
:param wallet: Wallet to export
:type wallet: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_export(wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F")
{
"0000000000000000000000000000000000000000000000000000000000000000": "0000000000000000000000000000000000000000000000000000000000000001"
}
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
resp = self.call('wallet_export', payload)
return json.loads(resp['json'])
[docs] @doc_metadata(categories=['wallet'])
def wallet_frontiers(self, wallet):
"""
Returns a list of pairs of account and block hash representing the
head block starting for accounts from **wallet**
:param wallet: Wallet to return frontiers for
:type wallet: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_frontiers(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
{
"xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000": "000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
}
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
resp = self.call('wallet_frontiers', payload)
return resp.get('frontiers') or {}
[docs] @doc_metadata(categories=['wallet'])
def wallet_key_valid(self, wallet):
"""
Returns if a **wallet** key is valid
:param wallet: Wallet to check key is valid
:type wallet: str
>>> rpc.wallet_key_valid(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
True
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
resp = self.call('wallet_key_valid', payload)
return resp['valid'] == '1'
[docs] @doc_metadata(categories=['wallet'])
def wallet_lock(self, wallet):
"""
Locks a **wallet**
:param wallet: Wallet to lock
:type wallet: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_lock(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
True
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
resp = self.call('wallet_lock', payload)
return resp['locked'] == '1'
[docs] @doc_metadata(categories=['wallet'])
def wallet_locked(self, wallet):
"""
Checks whether **wallet** is locked
:param wallet: Wallet to check if locked
:type wallet: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_locked(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
False
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
resp = self.call('wallet_locked', payload)
return resp['locked'] == '1'
[docs] @doc_metadata(categories=['wallet'])
def wallet_unlock(self, wallet, password):
"""
Unlocks **wallet** using **password**
:param wallet: Wallet to unlock
:type wallet: str
:param password: Password to enter
:type password: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_unlock(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... password="test"
... )
True
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
"password": password,
}
resp = self.call('wallet_unlock', payload)
return resp['valid'] == '1'
[docs] @doc_metadata(categories=['wallet'])
def wallet_pending(self, wallet, count=None, threshold=None, source=False):
"""
Returns a list of block hashes which have not yet been received by
accounts in this **wallet**
.. enable_control required
.. version 8.0 required
:param wallet: Wallet to get list of pending block hashes for
:type wallet: str
:param count: Max amount of blocks to return
:type count: int
:param threshold: Minimum amount in raw per block
:type threshold: int
:param source: If true, returns source account as well
:type source: bool
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_pending(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... count=1
... )
{
"xrb_1111111111111111111111111111111111111111111111111117353trpda": [
"142A538F36833D1CC78B94E11C766F75818F8B940771335C6C1B8AB880C5BB1D"
],
"xrb_3t6k35gi95xu6tergt6p69ck76ogmitsa8mnijtpxm9fkcm736xtoncuohr3": [
"4C1FEEF0BEA7F50BE35489A1233FE002B212DEA554B55B1B470D78BD8F210C74"
]
}
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
if count is not None:
payload['count'] = self._process_value(count, 'int')
if threshold is not None:
payload['threshold'] = self._process_value(threshold, 'int')
if source:
payload['source'] = self._process_value(source, 'strbool')
resp = self.call('wallet_pending', payload)
blocks = resp.get('blocks') or {}
for account, data in blocks.items():
if isinstance(data, list): # list of block hashes, no change needed
continue
if not data:
blocks[account] = [] # convert a "" response to []
continue
for key, value in data.items():
if isinstance(value, six.string_types): # amount
data[key] = int(value)
elif isinstance(value, dict): # dict with "amount" and "source"
for key in ('amount',):
if key in value:
value[key] = int(value[key])
return blocks or {}
[docs] @doc_metadata(categories=['wallet'])
def wallet_republish(self, wallet, count):
"""
Rebroadcast blocks for accounts from **wallet** starting at frontier
down to **count** to the network
.. enable_control required
.. version 8.0 required
:param wallet: Wallet to rebroadcast blocks for
:type wallet: str
:param count: Max amount of blocks to rebroadcast since frontier block
:type count: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_republish(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... count=2
... )
[
"991CF190094C00F0B68E2E5F75F6BEE95A2E0BD93CEAA4A6734DB9F19B728948",
"A170D51B94E00371ACE76E35AC81DC9405D5D04D4CEBC399AEACE07AE05DD293",
"90D0C16AC92DD35814E84BFBCC739A039615D0A42A76EF44ADAEF1D99E9F8A35"
]
"""
wallet = self._process_value(wallet, 'wallet')
count = self._process_value(count, 'int')
payload = {
"wallet": wallet,
"count": count,
}
resp = self.call('wallet_republish', payload)
return resp.get('blocks') or []
[docs] @doc_metadata(categories=['work'])
def wallet_work_get(self, wallet):
"""
Returns a list of pairs of account and work from **wallet**
.. enable_control required
.. version 8.0 required
:param wallet: Wallet to return work for
:type wallet: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.wallet_work_get(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
{
"xrb_1111111111111111111111111111111111111111111111111111hifc8npp":
"432e5cf728c90f4f"
}
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
resp = self.call('wallet_work_get', payload)
return resp.get('works') or {}
[docs] @doc_metadata(categories=['wallet'])
def password_change(self, wallet, password):
"""
Changes the password for **wallet** to **password**
.. enable_control required
:param wallet: Wallet to change password for
:type wallet: str
:param password: Password to set
:type password: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.password_change(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... password="test"
... )
True
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
"password": password,
}
resp = self.call('password_change', payload)
return resp['changed'] == '1'
[docs] @doc_metadata(categories=['wallet'])
def password_enter(self, wallet, password):
"""
Enters the **password** in to **wallet**
:param wallet: Wallet to enter password for
:type wallet: str
:param password: Password to enter
:type password: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.password_enter(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... password="test"
... )
True
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
"password": password,
}
resp = self.call('password_enter', payload)
return resp['valid'] == '1'
[docs] @doc_metadata(categories=['wallet'])
def password_valid(self, wallet):
"""
Checks whether the password entered for **wallet** is valid
:param wallet: Wallet to check password for
:type wallet: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.password_valid(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
True
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
resp = self.call('password_valid', payload)
return resp['valid'] == '1'
[docs] @doc_metadata(categories=['node'])
def peers(self):
"""
Returns a list of pairs of peer IPv6:port and its node network version
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.peers()
{
"[::ffff:172.17.0.1]:32841": 3
}
"""
resp = self.call('peers')
result = {}
peers = resp.get('peers') or {}
for host, version in peers.items():
result[host] = int(version)
return result
[docs] @doc_metadata(categories=['account'])
def pending(self, account, count=None, threshold=None, source=False):
"""
Returns a list of pending block hashes with amount more or equal to
**threshold**
.. version 8.0 required
:param account: Account to get list of pending block hashes for
:type account: str
:param count: Max blocks to return
:type count: int
:param threshold: Minimum amount in raw for blocks
:type threshold: int
:param source: If true, returns source address as well
:type source: bool
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.pending(
... account="xrb_1111111111111111111111111111111111111111111111111117353trpda"
... )
[
"000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
]
>>> rpc.pending(
... account="xrb_1111111111111111111111111111111111111111111111111117353trpda",
... count=1,
... threshold=1000000000000000000000000
... )
{
"000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F": "6000000000000000000000000000000"
}
"""
account = self._process_value(account, 'account')
payload = {
"account": account,
}
if count is not None:
payload['count'] = self._process_value(count, 'int')
if threshold is not None:
payload['threshold'] = self._process_value(threshold, 'int')
if source:
payload['source'] = self._process_value(source, 'strbool')
resp = self.call('pending', payload)
blocks = resp.get('blocks') or {}
if isinstance(blocks, list):
return blocks
for block, value in blocks.items():
if isinstance(value, six.string_types): # amount
blocks[block] = int(value)
elif isinstance(value, dict): # dict with "amount" and "source"
for key in ('amount',):
if key in value:
value[key] = int(value[key])
return blocks
[docs] @doc_metadata(categories=['block'])
def pending_exists(self, hash):
"""
Check whether block is pending by **hash**
.. version 8.0 required
:param hash: Hash of block to check if pending
:type hash: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.pending_exists(
hash="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
)
True
"""
hash = self._process_value(hash, 'block')
payload = {
"hash": hash,
}
resp = self.call('pending_exists', payload)
return resp['exists'] == '1'
[docs] @doc_metadata(categories=['work'])
def work_cancel(self, hash):
"""
Stop generating **work** for block
.. enable_control required
:param hash: Hash to stop generating work for
:type hash: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.work_cancel(
... hash="718CC2121C3E641059BC1C2CFC45666C99E8AE922F7A807B7D07B62C995D79E2"
... )
True
"""
hash = self._process_value(hash, 'block')
payload = {
"hash": hash,
}
resp = self.call('work_cancel', payload)
return resp == {}
[docs] @doc_metadata(categories=['work'])
def work_generate(self, hash):
"""
Generates **work** for block
.. enable_control required
:param hash: Hash to start generating **work** for
:type hash: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.work_generate(
... hash="718CC2121C3E641059BC1C2CFC45666C99E8AE922F7A807B7D07B62C995D79E2"
... )
"2bf29ef00786a6bc"
"""
hash = self._process_value(hash, 'block')
payload = {
"hash": hash,
}
resp = self.call('work_generate', payload)
return resp['work']
[docs] @doc_metadata(categories=['work'])
def work_get(self, wallet, account):
"""
Retrieves work for **account** in **wallet**
.. enable_control required
.. version 8.0 required
:param wallet: Wallet to get account work for
:type wallet: str
:param account: Account to get work for
:type account: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.work_get(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... account="xrb_1111111111111111111111111111111111111111111111111111hifc8npp"
... )
"432e5cf728c90f4f"
"""
wallet = self._process_value(wallet, 'wallet')
account = self._process_value(account, 'account')
payload = {
"wallet": wallet,
"account": account,
}
resp = self.call('work_get', payload)
return resp['work']
[docs] @doc_metadata(categories=['work'])
def work_set(self, wallet, account, work):
"""
Set **work** for **account** in **wallet**
.. enable_control required
.. version 8.0 required
:param wallet: Wallet to set work for account for
:type wallet: str
:param account: Account to set work for
:type account: str
:param work: Work to set for account in wallet
:type work: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.work_set(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... account="xrb_1111111111111111111111111111111111111111111111111111hifc8npp",
... work="0000000000000000"
... )
True
"""
wallet = self._process_value(wallet, 'wallet')
account = self._process_value(account, 'account')
work = self._process_value(work, 'work')
payload = {
"wallet": wallet,
"account": account,
"work": work,
}
resp = self.call('work_set', payload)
return 'success' in resp
[docs] @doc_metadata(categories=['work'])
def work_peer_add(self, address, port):
"""
Add specific **IP address** and **port** as work peer for node until
restart
.. enable_control required
.. version 8.0 required
:param address: IP address of work peer to add
:type address: str
:param port: Port work peer to add
:type port: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.work_peer_add(address="::ffff:172.17.0.1", port="7076")
True
"""
address = self._process_value(address, 'ipaddr')
port = self._process_value(port, 'int')
payload = {
"address": address,
"port": port,
}
resp = self.call('work_peer_add', payload)
return 'success' in resp
[docs] @doc_metadata(categories=['work'])
def work_peers(self):
"""
Retrieve work peers
.. enable_control required
.. version 8.0 required
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.work_peers()
[
"::ffff:172.17.0.1:7076"
]
"""
resp = self.call('work_peers')
return resp.get('work_peers') or []
[docs] @doc_metadata(categories=['work'])
def work_peers_clear(self):
"""
Clear work peers node list until restart
.. enable_control required
.. version 8.0 required
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.work_peers_clear()
True
"""
resp = self.call('work_peers_clear')
return 'success' in resp
[docs] @doc_metadata(categories=['work', 'block'])
def work_validate(self, work, hash):
"""
Check whether **work** is valid for block
:param work: Work to validate
:type work: str
:param hash: Hash of block to validate work for
:type hash: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.work_validate(
... work="2bf29ef00786a6bc",
... hash="718CC2121C3E641059BC1C2CFC45666C99E8AE922F7A807B7D07B62C995D79E2"
... )
True
"""
work = self._process_value(work, 'work')
hash = self._process_value(hash, 'block')
payload = {
"work": work,
"hash": hash,
}
resp = self.call('work_validate', payload)
return resp['valid'] == '1'
[docs] @doc_metadata(categories=['block'])
def republish(self, hash, count=None, sources=None, destinations=None):
"""
Rebroadcast blocks starting at **hash** to the network
:param hash: Hash of block to start rebroadcasting from
:type hash: str
:param count: Max number of blocks to rebroadcast
:type count: int
:param sources: If set, additionally rebroadcasts source chain blocks
for receive/open up to **sources** depth
:type sources: int
:param destinations: If set, additionally rebroadcasts destination chain
blocks for receive/open up to **destinations** depth
:type destinations: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.republish(
... hash="991CF190094C00F0B68E2E5F75F6BEE95A2E0BD93CEAA4A6734DB9F19B728948"
... )
[
"991CF190094C00F0B68E2E5F75F6BEE95A2E0BD93CEAA4A6734DB9F19B728948",
"A170D51B94E00371ACE76E35AC81DC9405D5D04D4CEBC399AEACE07AE05DD293"
]
"""
hash = self._process_value(hash, 'block')
payload = {
"hash": hash,
}
if count is not None:
payload['count'] = self._process_value(count, 'int')
if sources is not None:
payload['sources'] = self._process_value(sources, 'int')
if destinations is not None:
payload['destinations'] = self._process_value(destinations, 'int')
resp = self.call('republish', payload)
return resp.get('blocks') or []
[docs] @doc_metadata(categories=['wallet'])
def search_pending(self, wallet):
"""
Tells the node to look for pending blocks for any account in
**wallet**
.. enable_control required
:param wallet: Wallet to search for pending blocks
:type wallet: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.search_pending(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
... )
True
"""
wallet = self._process_value(wallet, 'wallet')
payload = {
"wallet": wallet,
}
resp = self.call('search_pending', payload)
return resp['started'] == '1'
[docs] @doc_metadata(categories=['node'])
def search_pending_all(self):
"""
Tells the node to look for pending blocks for any account in all
available wallets
.. enable_control required
.. version 8.0 required
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.search_pending_all()
True
"""
resp = self.call('search_pending_all')
return 'success' in resp
[docs] @doc_metadata(categories=['wallet', 'account'])
def send(self, wallet, source, destination, amount, work=None):
"""
Send **amount** from **source** in **wallet** to **destination**
.. enable_control required
:param wallet: Wallet of account used to send funds
:type wallet: str
:param source: Account to send funds from
:type source: str
:param destination: Account to send funds to
:type destination: str
:param amount: Amount in raw to send
:type amount: int
:param work: If set, uses this work for the block
:type work: str
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.send(
... wallet="000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F",
... source="xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
... destination="xrb_3e3j5tkog48pnny9dmfzj1r16pg8t1e76dz5tmac6iq689wyjfpi00000000",
... amount=1000000,
... work="2bf29ef00786a6bc"
... )
"000D1BAEC8EC208142C99059B393051BAC8380F9B5A2E6B2489A277D81789F3F"
"""
wallet = self._process_value(wallet, 'wallet')
source = self._process_value(source, 'account')
destination = self._process_value(destination, 'account')
amount = self._process_value(amount, 'int')
payload = {
"wallet": wallet,
"source": source,
"destination": destination,
"amount": amount,
}
if work is not None:
payload['work'] = self._process_value(work, 'work')
resp = self.call('send', payload)
return resp['block']
[docs] @doc_metadata(categories=['block'])
def successors(self, block, count):
"""
Returns a list of block hashes in the account chain ending at
**block** up to **count**
:param block: Hash of block to start returning successors for
:type block: str
:param count: Max number of successor blocks to return
:type count: int
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.successors(
... block="991CF190094C00F0B68E2E5F75F6BEE95A2E0BD93CEAA4A6734DB9F19B728948",
... count=1
... )
[
"A170D51B94E00371ACE76E35AC81DC9405D5D04D4CEBC399AEACE07AE05DD293"
]
"""
block = self._process_value(block, 'block')
count = self._process_value(count, 'int')
payload = {
"block": block,
"count": count,
}
resp = self.call('successors', payload)
return resp.get('blocks') or []
[docs] @doc_metadata(categories=['node'])
def stop(self):
"""
Stop the node
.. enable_control required
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.stop()
True
"""
resp = self.call('stop')
return 'success' in resp
[docs] @doc_metadata(categories=['node'])
def version(self):
"""
Returns the node's RPC version
:raises: :py:exc:`nano.rpc.RPCException`
>>> rpc.version()
{
"rpc_version": 1,
"store_version": 10,
"node_vendor": "RaiBlocks 9.0"
}
"""
resp = self.call('version')
for key in ('rpc_version', 'store_version'):
resp[key] = int(resp[key])
return resp
RPCClient = Client