hive-nectar 0.2.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. hive_nectar-0.2.9.dist-info/METADATA +194 -0
  2. hive_nectar-0.2.9.dist-info/RECORD +87 -0
  3. hive_nectar-0.2.9.dist-info/WHEEL +4 -0
  4. hive_nectar-0.2.9.dist-info/entry_points.txt +2 -0
  5. hive_nectar-0.2.9.dist-info/licenses/LICENSE.txt +23 -0
  6. nectar/__init__.py +37 -0
  7. nectar/account.py +5076 -0
  8. nectar/amount.py +553 -0
  9. nectar/asciichart.py +303 -0
  10. nectar/asset.py +122 -0
  11. nectar/block.py +574 -0
  12. nectar/blockchain.py +1242 -0
  13. nectar/blockchaininstance.py +2590 -0
  14. nectar/blockchainobject.py +263 -0
  15. nectar/cli.py +5937 -0
  16. nectar/comment.py +1552 -0
  17. nectar/community.py +854 -0
  18. nectar/constants.py +95 -0
  19. nectar/discussions.py +1437 -0
  20. nectar/exceptions.py +152 -0
  21. nectar/haf.py +381 -0
  22. nectar/hive.py +630 -0
  23. nectar/imageuploader.py +114 -0
  24. nectar/instance.py +113 -0
  25. nectar/market.py +876 -0
  26. nectar/memo.py +542 -0
  27. nectar/message.py +379 -0
  28. nectar/nodelist.py +309 -0
  29. nectar/price.py +603 -0
  30. nectar/profile.py +74 -0
  31. nectar/py.typed +0 -0
  32. nectar/rc.py +333 -0
  33. nectar/snapshot.py +1024 -0
  34. nectar/storage.py +62 -0
  35. nectar/transactionbuilder.py +659 -0
  36. nectar/utils.py +630 -0
  37. nectar/version.py +3 -0
  38. nectar/vote.py +722 -0
  39. nectar/wallet.py +472 -0
  40. nectar/witness.py +728 -0
  41. nectarapi/__init__.py +12 -0
  42. nectarapi/exceptions.py +126 -0
  43. nectarapi/graphenerpc.py +596 -0
  44. nectarapi/node.py +194 -0
  45. nectarapi/noderpc.py +79 -0
  46. nectarapi/openapi.py +107 -0
  47. nectarapi/py.typed +0 -0
  48. nectarapi/rpcutils.py +98 -0
  49. nectarapi/version.py +3 -0
  50. nectarbase/__init__.py +15 -0
  51. nectarbase/ledgertransactions.py +106 -0
  52. nectarbase/memo.py +242 -0
  53. nectarbase/objects.py +521 -0
  54. nectarbase/objecttypes.py +21 -0
  55. nectarbase/operationids.py +102 -0
  56. nectarbase/operations.py +1357 -0
  57. nectarbase/py.typed +0 -0
  58. nectarbase/signedtransactions.py +89 -0
  59. nectarbase/transactions.py +11 -0
  60. nectarbase/version.py +3 -0
  61. nectargraphenebase/__init__.py +27 -0
  62. nectargraphenebase/account.py +1121 -0
  63. nectargraphenebase/aes.py +49 -0
  64. nectargraphenebase/base58.py +197 -0
  65. nectargraphenebase/bip32.py +575 -0
  66. nectargraphenebase/bip38.py +110 -0
  67. nectargraphenebase/chains.py +15 -0
  68. nectargraphenebase/dictionary.py +2 -0
  69. nectargraphenebase/ecdsasig.py +309 -0
  70. nectargraphenebase/objects.py +130 -0
  71. nectargraphenebase/objecttypes.py +8 -0
  72. nectargraphenebase/operationids.py +5 -0
  73. nectargraphenebase/operations.py +25 -0
  74. nectargraphenebase/prefix.py +13 -0
  75. nectargraphenebase/py.typed +0 -0
  76. nectargraphenebase/signedtransactions.py +221 -0
  77. nectargraphenebase/types.py +557 -0
  78. nectargraphenebase/unsignedtransactions.py +288 -0
  79. nectargraphenebase/version.py +3 -0
  80. nectarstorage/__init__.py +57 -0
  81. nectarstorage/base.py +317 -0
  82. nectarstorage/exceptions.py +15 -0
  83. nectarstorage/interfaces.py +244 -0
  84. nectarstorage/masterpassword.py +237 -0
  85. nectarstorage/py.typed +0 -0
  86. nectarstorage/ram.py +27 -0
  87. nectarstorage/sqlite.py +343 -0
nectar/exceptions.py ADDED
@@ -0,0 +1,152 @@
1
+ class NectarException(Exception):
2
+ """Base exception for all Nectar-related errors"""
3
+
4
+ pass
5
+
6
+
7
+ class WalletExists(NectarException):
8
+ """A wallet has already been created and requires a password to be
9
+ unlocked by means of :func:`nectar.wallet.Wallet.unlock`.
10
+ """
11
+
12
+ pass
13
+
14
+
15
+ class RPCConnectionRequired(NectarException):
16
+ """An RPC connection is required"""
17
+
18
+ pass
19
+
20
+
21
+ class InvalidMemoKeyException(NectarException):
22
+ """Memo key in message is invalid"""
23
+
24
+ pass
25
+
26
+
27
+ class WrongMemoKey(NectarException):
28
+ """The memo provided is not equal the one on the blockchain"""
29
+
30
+ pass
31
+
32
+
33
+ class OfflineHasNoRPCException(NectarException):
34
+ """When in offline mode, we don't have RPC"""
35
+
36
+ pass
37
+
38
+
39
+ class AccountExistsException(NectarException):
40
+ """The requested account already exists"""
41
+
42
+ pass
43
+
44
+
45
+ class AccountDoesNotExistsException(NectarException):
46
+ """The account does not exist"""
47
+
48
+ pass
49
+
50
+
51
+ class AssetDoesNotExistsException(NectarException):
52
+ """The asset does not exist"""
53
+
54
+ pass
55
+
56
+
57
+ class InvalidAssetException(NectarException):
58
+ """An invalid asset has been provided"""
59
+
60
+ pass
61
+
62
+
63
+ class InsufficientAuthorityError(NectarException):
64
+ """The transaction requires signature of a higher authority"""
65
+
66
+ pass
67
+
68
+
69
+ class VotingInvalidOnArchivedPost(NectarException):
70
+ """The transaction requires signature of a higher authority"""
71
+
72
+ pass
73
+
74
+
75
+ class MissingKeyError(NectarException):
76
+ """A required key couldn't be found in the wallet"""
77
+
78
+ pass
79
+
80
+
81
+ class InvalidWifError(NectarException):
82
+ """The provided private Key has an invalid format"""
83
+
84
+ pass
85
+
86
+
87
+ class BlockDoesNotExistsException(NectarException):
88
+ """The block does not exist"""
89
+
90
+ pass
91
+
92
+
93
+ class NoWalletException(NectarException):
94
+ """No Wallet could be found, please use :func:`nectar.wallet.Wallet.create` to
95
+ create a new wallet
96
+ """
97
+
98
+ pass
99
+
100
+
101
+ class WitnessDoesNotExistsException(NectarException):
102
+ """The witness does not exist"""
103
+
104
+ pass
105
+
106
+
107
+ class ContentDoesNotExistsException(NectarException):
108
+ """The content does not exist"""
109
+
110
+ pass
111
+
112
+
113
+ class VoteDoesNotExistsException(NectarException):
114
+ """The vote does not exist"""
115
+
116
+ pass
117
+
118
+
119
+ class WrongMasterPasswordException(NectarException):
120
+ """The password provided could not properly unlock the wallet"""
121
+
122
+ pass
123
+
124
+
125
+ class VestingBalanceDoesNotExistsException(NectarException):
126
+ """Vesting Balance does not exist"""
127
+
128
+ pass
129
+
130
+
131
+ class InvalidMessageSignature(NectarException):
132
+ """The message signature does not fit the message"""
133
+
134
+ pass
135
+
136
+
137
+ class NoWriteAccess(NectarException):
138
+ """Cannot store to sqlite3 database due to missing write access"""
139
+
140
+ pass
141
+
142
+
143
+ class BatchedCallsNotSupported(NectarException):
144
+ """Batch calls do not work"""
145
+
146
+ pass
147
+
148
+
149
+ class BlockWaitTimeExceeded(NectarException):
150
+ """Wait time for new block exceeded"""
151
+
152
+ pass
nectar/haf.py ADDED
@@ -0,0 +1,381 @@
1
+ import json
2
+ import logging
3
+ from typing import Any, Dict, Optional
4
+
5
+ import httpx
6
+
7
+ from nectar.instance import shared_blockchain_instance
8
+
9
+ log = logging.getLogger(__name__)
10
+
11
+
12
+ class HAF:
13
+ """Hive Account Feed (HAF) API client for accessing Hive blockchain endpoints.
14
+
15
+ This class provides access to various Hive API endpoints that are not part of the
16
+ standard RPC blockchain calls. It supports multiple API providers and handles
17
+ reputation queries and other HAF-related data.
18
+
19
+ :param str api: Base API URL to use for requests. Supported endpoints include:
20
+ - 'https://api.hive.blog' (default)
21
+ - 'https://api.syncad.com'
22
+ :param blockchain_instance: Blockchain instance for compatibility with the nectar ecosystem
23
+
24
+ .. code-block:: python
25
+
26
+ >>> from nectar.haf import HAF
27
+ >>> haf = HAF() # doctest: +SKIP
28
+ >>> reputation = haf.reputation("thecrazygm") # doctest: +SKIP
29
+ >>> print(reputation) # doctest: +SKIP
30
+
31
+ """
32
+
33
+ DEFAULT_APIS = ["https://api.hive.blog", "https://api.syncad.com"]
34
+
35
+ def __init__(
36
+ self, api: Optional[str] = None, blockchain_instance=None, timeout: Optional[float] = None
37
+ ):
38
+ """
39
+ Initialize the HAF client.
40
+
41
+ Parameters:
42
+ api (str, optional): Base API URL. If None, uses the first available default API.
43
+ blockchain_instance: Blockchain instance for ecosystem compatibility.
44
+ timeout (float, optional): Timeout for requests in seconds.
45
+ """
46
+ self.api = api or self.DEFAULT_APIS[0]
47
+ self.blockchain = blockchain_instance or shared_blockchain_instance()
48
+ self._timeout = float(timeout) if timeout else 30.0
49
+
50
+ # Validate API URL
51
+ if not self.api.startswith(("http://", "https://")):
52
+ raise ValueError(f"Invalid API URL: {self.api}. Must start with http:// or https://")
53
+
54
+ # Remove trailing slash if present
55
+ self.api = self.api.rstrip("/")
56
+
57
+ log.debug(f"Initialized HAF client with API: {self.api}")
58
+
59
+ def _make_request(self, endpoint: str, method: str = "GET", **kwargs) -> Any:
60
+ """
61
+ Make an HTTP request to the HAF API.
62
+
63
+ Parameters:
64
+ endpoint (str): API endpoint path (without leading slash)
65
+ method (str): HTTP method (default: 'GET')
66
+ **kwargs: Additional arguments passed to requests
67
+
68
+ Returns:
69
+ dict: JSON response from the API
70
+
71
+ Raises:
72
+ httpx.RequestError: If the request fails
73
+ ValueError: If the response is not valid JSON
74
+ """
75
+ url = f"{self.api}/{endpoint}"
76
+
77
+ # Set default headers
78
+ headers = kwargs.pop("headers", {})
79
+ headers.setdefault("accept", "application/json")
80
+ from nectar.version import version as nectar_version
81
+
82
+ headers.setdefault("User-Agent", f"hive-nectar/{nectar_version}")
83
+
84
+ log.debug(f"Making {method} request to: {url}")
85
+
86
+ try:
87
+ timeout = kwargs.pop("timeout", self._timeout)
88
+ with httpx.Client(timeout=timeout) as client:
89
+ response = client.request(method, url, headers=headers, **kwargs)
90
+ response.raise_for_status()
91
+ return response.json()
92
+
93
+ except httpx.RequestError as e:
94
+ log.error(f"Request failed for {url}: {e}")
95
+ raise
96
+ except (httpx.HTTPStatusError, json.JSONDecodeError) as e:
97
+ log.error(f"Invalid response from {url}: {e}")
98
+ raise ValueError(f"Invalid response from API: {e}")
99
+
100
+ def reputation(self, account: str) -> Optional[Dict[str, Any]]:
101
+ """
102
+ Get reputation information for a Hive account.
103
+
104
+ This method queries the reputation API endpoint to retrieve the account's
105
+ reputation score and related metadata.
106
+
107
+ Parameters:
108
+ account (str): Hive account name
109
+
110
+ Returns:
111
+ dict or None: Reputation data containing account information and reputation score,
112
+ or None if the request fails or account is not found
113
+
114
+ Example:
115
+ >>> haf = HAF() # doctest: +SKIP
116
+ >>> rep = haf.reputation("thecrazygm") # doctest: +SKIP
117
+ >>> print(rep) # doctest: +SKIP
118
+ {'account': 'thecrazygm', 'reputation': '71', ...}
119
+
120
+ """
121
+ if not account or not isinstance(account, str):
122
+ raise ValueError("Account name must be a non-empty string")
123
+
124
+ try:
125
+ endpoint = f"reputation-api/accounts/{account}/reputation"
126
+ response = self._make_request(endpoint)
127
+
128
+ log.debug(f"Retrieved reputation for account: {account}")
129
+ return response
130
+
131
+ except httpx.RequestError as e:
132
+ log.warning(f"Failed to retrieve reputation for account {account}: {e}")
133
+ return None
134
+ except Exception as e:
135
+ log.error(f"Unexpected error retrieving reputation for {account}: {e}")
136
+ return None
137
+
138
+ def get_available_apis(self) -> list[str]:
139
+ """
140
+ Get the list of available API endpoints.
141
+
142
+ Returns:
143
+ list: List of supported API URLs
144
+ """
145
+ return self.DEFAULT_APIS.copy()
146
+
147
+ def set_api(self, api: str) -> None:
148
+ """
149
+ Change the API endpoint.
150
+
151
+ Parameters:
152
+ api (str): New API URL to use
153
+
154
+ Raises:
155
+ ValueError: If the API URL is invalid
156
+ """
157
+ if api not in self.DEFAULT_APIS:
158
+ log.warning(f"Using non-default API: {api}")
159
+
160
+ # Validate URL
161
+ if not api.startswith(("http://", "https://")):
162
+ raise ValueError(f"Invalid API URL: {api}. Must start with http:// or https://")
163
+
164
+ self.api = api.rstrip("/")
165
+ log.info(f"Switched to API: {self.api}")
166
+
167
+ def get_current_api(self) -> str:
168
+ """
169
+ Get the currently active API endpoint.
170
+
171
+ Returns:
172
+ str: Current API URL
173
+ """
174
+ return self.api
175
+
176
+ def get_account_balances(self, account: str) -> Optional[Dict[str, Any]]:
177
+ """
178
+ Get account balances from the balance API.
179
+
180
+ This method retrieves comprehensive balance information including HBD, HIVE,
181
+ vesting shares, rewards, and other balance-related data for an account.
182
+
183
+ Parameters:
184
+ account (str): Hive account name
185
+
186
+ Returns:
187
+ dict or None: Account balance data or None if request fails
188
+
189
+ Example:
190
+ >>> haf = HAF() # doctest: +SKIP
191
+ >>> balances = haf.get_account_balances("thecrazygm") # doctest: +SKIP
192
+ >>> print(balances['hive_balance']) # doctest: +SKIP
193
+ """
194
+ if not account or not isinstance(account, str):
195
+ raise ValueError("Account name must be a non-empty string")
196
+
197
+ try:
198
+ endpoint = f"balance-api/accounts/{account}/balances"
199
+ response = self._make_request(endpoint)
200
+
201
+ log.debug(f"Retrieved balances for account: {account}")
202
+ return response
203
+
204
+ except httpx.RequestError as e:
205
+ log.warning(f"Failed to retrieve balances for account {account}: {e}")
206
+ return None
207
+ except Exception as e:
208
+ log.error(f"Unexpected error retrieving balances for {account}: {e}")
209
+ return None
210
+
211
+ def get_account_delegations(self, account: str) -> Optional[Dict[str, Any]]:
212
+ """
213
+ Get account delegations from the balance API.
214
+
215
+ This method retrieves both incoming and outgoing delegations for an account.
216
+
217
+ Parameters:
218
+ account (str): Hive account name
219
+
220
+ Returns:
221
+ dict or None: Delegation data containing incoming and outgoing delegations
222
+
223
+ Example:
224
+ >>> haf = HAF() # doctest: +SKIP
225
+ >>> delegations = haf.get_account_delegations("thecrazygm") # doctest: +SKIP
226
+ >>> print(delegations['incoming_delegations']) # doctest: +SKIP
227
+ """
228
+ if not account or not isinstance(account, str):
229
+ raise ValueError("Account name must be a non-empty string")
230
+
231
+ try:
232
+ endpoint = f"balance-api/accounts/{account}/delegations"
233
+ response = self._make_request(endpoint)
234
+
235
+ log.debug(f"Retrieved delegations for account: {account}")
236
+ return response
237
+
238
+ except httpx.RequestError as e:
239
+ log.warning(f"Failed to retrieve delegations for account {account}: {e}")
240
+ return None
241
+ except Exception as e:
242
+ log.error(f"Unexpected error retrieving delegations for {account}: {e}")
243
+ return None
244
+
245
+ def get_account_recurrent_transfers(self, account: str) -> Optional[Dict[str, Any]]:
246
+ """
247
+ Get account recurrent transfers from the balance API.
248
+
249
+ This method retrieves both incoming and outgoing recurrent transfers for an account.
250
+
251
+ Parameters:
252
+ account (str): Hive account name
253
+
254
+ Returns:
255
+ dict or None: Recurrent transfer data containing incoming and outgoing transfers
256
+
257
+ Example:
258
+ >>> haf = HAF() # doctest: +SKIP
259
+ >>> transfers = haf.get_account_recurrent_transfers("thecrazygm") # doctest: +SKIP
260
+ >>> print(transfers['outgoing_recurrent_transfers']) # doctest: +SKIP
261
+ """
262
+ if not account or not isinstance(account, str):
263
+ raise ValueError("Account name must be a non-empty string")
264
+
265
+ try:
266
+ endpoint = f"balance-api/accounts/{account}/recurrent-transfers"
267
+ response = self._make_request(endpoint)
268
+
269
+ log.debug(f"Retrieved recurrent transfers for account: {account}")
270
+ return response
271
+
272
+ except httpx.RequestError as e:
273
+ log.warning(f"Failed to retrieve recurrent transfers for account {account}: {e}")
274
+ return None
275
+ except Exception as e:
276
+ log.error(f"Unexpected error retrieving recurrent transfers for {account}: {e}")
277
+ return None
278
+
279
+ def get_reputation_version(self) -> Optional[str]:
280
+ """
281
+ Get the reputation tracker's version from the reputation API.
282
+
283
+ Returns:
284
+ str or None: Version string or None if request fails
285
+
286
+ Example:
287
+ >>> haf = HAF() # doctest: +SKIP
288
+ >>> version = haf.get_reputation_version() # doctest: +SKIP
289
+ >>> print(version) # doctest: +SKIP
290
+ """
291
+ try:
292
+ endpoint = "reputation-api/version"
293
+ response = self._make_request(endpoint)
294
+
295
+ log.debug("Retrieved reputation tracker version")
296
+ return response
297
+
298
+ except httpx.RequestError as e:
299
+ log.warning(f"Failed to retrieve reputation version: {e}")
300
+ return None
301
+ except Exception as e:
302
+ log.error(f"Unexpected error retrieving reputation version: {e}")
303
+ return None
304
+
305
+ def get_reputation_last_synced_block(self) -> Optional[int]:
306
+ """
307
+ Get the last block number synced by the reputation tracker.
308
+
309
+ Returns:
310
+ int or None: Last synced block number or None if request fails
311
+
312
+ Example:
313
+ >>> haf = HAF() # doctest: +SKIP
314
+ >>> block = haf.get_reputation_last_synced_block() # doctest: +SKIP
315
+ >>> print(block) # doctest: +SKIP
316
+ """
317
+ try:
318
+ endpoint = "reputation-api/last-synced-block"
319
+ response = self._make_request(endpoint)
320
+
321
+ log.debug("Retrieved last synced block for reputation tracker")
322
+ return response
323
+
324
+ except httpx.RequestError as e:
325
+ log.warning(f"Failed to retrieve last synced block: {e}")
326
+ return None
327
+ except Exception as e:
328
+ log.error(f"Unexpected error retrieving last synced block: {e}")
329
+ return None
330
+
331
+ def get_balance_version(self) -> Optional[str]:
332
+ """
333
+ Get the balance tracker's version from the balance API.
334
+
335
+ Returns:
336
+ str or None: Version string or None if request fails
337
+
338
+ Example:
339
+ >>> haf = HAF() # doctest: +SKIP
340
+ >>> version = haf.get_balance_version() # doctest: +SKIP
341
+ >>> print(version) # doctest: +SKIP
342
+ """
343
+ try:
344
+ endpoint = "balance-api/version"
345
+ response = self._make_request(endpoint)
346
+
347
+ log.debug("Retrieved balance tracker version")
348
+ return response
349
+
350
+ except httpx.RequestError as e:
351
+ log.warning(f"Failed to retrieve balance version: {e}")
352
+ return None
353
+ except Exception as e:
354
+ log.error(f"Unexpected error retrieving balance version: {e}")
355
+ return None
356
+
357
+ def get_balance_last_synced_block(self) -> Optional[int]:
358
+ """
359
+ Get the last block number synced by the balance tracker.
360
+
361
+ Returns:
362
+ int or None: Last synced block number or None if request fails
363
+
364
+ Example:
365
+ >>> haf = HAF() # doctest: +SKIP
366
+ >>> block = haf.get_balance_last_synced_block() # doctest: +SKIP
367
+ >>> print(block) # doctest: +SKIP
368
+ """
369
+ try:
370
+ endpoint = "balance-api/last-synced-block"
371
+ response = self._make_request(endpoint)
372
+
373
+ log.debug("Retrieved last synced block for balance tracker")
374
+ return response
375
+
376
+ except httpx.RequestError as e:
377
+ log.warning(f"Failed to retrieve last synced block: {e}")
378
+ return None
379
+ except Exception as e:
380
+ log.error(f"Unexpected error retrieving last synced block: {e}")
381
+ return None