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/witness.py ADDED
@@ -0,0 +1,728 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import warnings
5
+ from datetime import date, datetime, timezone
6
+ from typing import Any
7
+
8
+ from prettytable import PrettyTable
9
+
10
+ from nectarapi.exceptions import NoMethodWithName, RPCError
11
+ from nectarbase import operations
12
+
13
+ from .account import Account
14
+ from .amount import Amount
15
+ from .blockchainobject import BlockchainObject
16
+ from .exceptions import WitnessDoesNotExistsException
17
+ from .instance import shared_blockchain_instance
18
+ from .utils import formatTimeString, parse_time
19
+
20
+
21
+ class Witness(BlockchainObject):
22
+ """Read data about a witness in the chain
23
+
24
+ :param str owner: Name of the witness
25
+ :param bool lazy: Use lazy loading
26
+ :param bool full: Get full data about witness
27
+ :param nectar.nectar.nectar blockchain_instance: nectar
28
+ instance to use when accessing the RPC
29
+ """
30
+
31
+ type_id = 3
32
+
33
+ def __init__(
34
+ self,
35
+ owner: str | dict[str, Any],
36
+ full: bool = False,
37
+ lazy: bool = False,
38
+ blockchain_instance: Any | None = None,
39
+ **kwargs: Any,
40
+ ) -> None:
41
+ # Warn about any unused kwargs to maintain backward compatibility
42
+ """
43
+ Initialize a Witness object representing a blockchain witness.
44
+
45
+ Parameters:
46
+ owner (str | dict): Witness owner account name or a dictionary of witness fields. If a dict is provided, it will be parsed into the internal witness representation.
47
+ full (bool): If True, load full witness data when available; otherwise keep a lighter representation.
48
+ lazy (bool): If True, defer network loading until data is accessed.
49
+
50
+ Notes:
51
+ - `blockchain_instance` defaults to the shared blockchain instance when not provided.
52
+ - Any unexpected keyword arguments are accepted for backward compatibility but will trigger a DeprecationWarning and be ignored.
53
+ """
54
+ if kwargs:
55
+ for key in kwargs:
56
+ warnings.warn(
57
+ f"Unexpected keyword argument '{key}' passed to Witness.__init__. "
58
+ "This may be a deprecated parameter and will be ignored.",
59
+ DeprecationWarning,
60
+ stacklevel=2,
61
+ )
62
+ self.full = full
63
+ self.lazy = lazy
64
+ self.blockchain = blockchain_instance or shared_blockchain_instance()
65
+ if isinstance(owner, dict):
66
+ owner = self._parse_json_data(owner)
67
+ super().__init__(
68
+ owner, lazy=lazy, full=full, id_item="owner", blockchain_instance=self.blockchain
69
+ )
70
+
71
+ def refresh(self) -> None:
72
+ """
73
+ Refresh the witness data from the blockchain and reinitialize this object.
74
+
75
+ If the witness identifier is empty or the blockchain is not connected, the method returns early.
76
+ Fetches witness data via the configured RPC, parses
77
+ timestamps and numeric fields via _parse_json_data, and reinitializes the Witness instance with the
78
+ retrieved data (respecting this object's lazy/full flags and blockchain instance).
79
+
80
+ Raises:
81
+ WitnessDoesNotExistsException: If no witness information is found for the current identifier.
82
+ """
83
+ if not self.identifier:
84
+ return
85
+ if not self.blockchain.is_connected():
86
+ return
87
+ self.blockchain.rpc.set_next_node_on_empty_reply(False)
88
+ witness = self.blockchain.rpc.find_witnesses({"owners": [self.identifier]})["witnesses"]
89
+ if len(witness) > 0:
90
+ witness = witness[0]
91
+ if not witness:
92
+ raise WitnessDoesNotExistsException(self.identifier)
93
+ witness = self._parse_json_data(witness)
94
+ super().__init__(
95
+ witness,
96
+ id_item="owner",
97
+ lazy=self.lazy,
98
+ full=self.full,
99
+ blockchain_instance=self.blockchain,
100
+ )
101
+
102
+ def _parse_json_data(self, witness: dict) -> dict:
103
+ parse_times = [
104
+ "created",
105
+ "last_sbd_exchange_update",
106
+ "hardfork_time_vote",
107
+ "last_hbd_exchange_update",
108
+ ]
109
+ for p in parse_times:
110
+ if p in witness and isinstance(witness.get(p), str):
111
+ witness[p] = parse_time(witness.get(p, "1970-01-01T00:00:00"))
112
+ parse_int = [
113
+ "votes",
114
+ "virtual_last_update",
115
+ "virtual_position",
116
+ "virtual_scheduled_time",
117
+ ]
118
+ for p in parse_int:
119
+ if p in witness and isinstance(witness.get(p), str):
120
+ witness[p] = int(witness.get(p, "0"))
121
+ return witness
122
+
123
+ def json(self) -> dict[str, Any]:
124
+ output = self.copy()
125
+ parse_times = [
126
+ "created",
127
+ "last_sbd_exchange_update",
128
+ "hardfork_time_vote",
129
+ "last_hbd_exchange_update",
130
+ ]
131
+ for p in parse_times:
132
+ if p in output:
133
+ p_date = output.get(p, datetime(1970, 1, 1, 0, 0))
134
+ if isinstance(p_date, (datetime, date)):
135
+ output[p] = formatTimeString(p_date)
136
+ else:
137
+ output[p] = p_date
138
+ parse_int = [
139
+ "votes",
140
+ "virtual_last_update",
141
+ "virtual_position",
142
+ "virtual_scheduled_time",
143
+ ]
144
+ for p in parse_int:
145
+ if p in output and isinstance(output[p], int):
146
+ output[p] = str(output[p])
147
+ return json.loads(str(json.dumps(output)))
148
+
149
+ @property
150
+ def account(self) -> Account:
151
+ return Account(self["owner"], blockchain_instance=self.blockchain)
152
+
153
+ @property
154
+ def is_active(self) -> bool:
155
+ return (
156
+ len(self["signing_key"]) > 3
157
+ and self["signing_key"][3:] != "1111111111111111111111111111111114T1Anm"
158
+ )
159
+
160
+ def feed_publish(self, base, quote=None, account=None):
161
+ """
162
+ Publish a witness feed price (exchange rate) to the blockchain.
163
+
164
+ Accepts the base and quote as Amount objects, strings, or numeric values and submits a Feed_publish operation using the provided account (defaults to the witness owner).
165
+
166
+ Parameters:
167
+ base: Amount | str | number
168
+ The base side of the exchange_rate (must use the blockchain's backed token symbol).
169
+ quote: Amount | str | number, optional
170
+ The quote side of the exchange_rate. Defaults to "1.000 <TOKEN>" where <TOKEN> is the blockchain token_symbol.
171
+ account: str | Account, optional
172
+ Account name or Account object used to sign and publish the feed. If omitted, the witness owner is used.
173
+
174
+ Returns:
175
+ The result returned by blockchain.finalizeOp (typically the broadcast/transaction result).
176
+
177
+ Raises:
178
+ ValueError: If no account is provided and the witness has no owner.
179
+ AssertionError: If the resolved base or quote symbols do not match the blockchain's expected backed_token_symbol and token_symbol, respectively.
180
+ """
181
+ quote = quote if quote is not None else "1.000 %s" % (self.blockchain.token_symbol)
182
+ if not account:
183
+ account = self["owner"]
184
+ if not account:
185
+ raise ValueError("You need to provide an account")
186
+
187
+ account = Account(account, blockchain_instance=self.blockchain)
188
+ if isinstance(base, Amount):
189
+ base = Amount(base, blockchain_instance=self.blockchain)
190
+ elif isinstance(base, str):
191
+ base = Amount(base, blockchain_instance=self.blockchain)
192
+ else:
193
+ base = Amount(
194
+ base, self.blockchain.backed_token_symbol, blockchain_instance=self.blockchain
195
+ )
196
+
197
+ if isinstance(quote, Amount):
198
+ quote = Amount(quote, blockchain_instance=self.blockchain)
199
+ elif isinstance(quote, str):
200
+ quote = Amount(quote, blockchain_instance=self.blockchain)
201
+ else:
202
+ quote = Amount(quote, self.blockchain.token_symbol, blockchain_instance=self.blockchain)
203
+
204
+ if not base.symbol == self.blockchain.backed_token_symbol:
205
+ raise AssertionError()
206
+ if not quote.symbol == self.blockchain.token_symbol:
207
+ raise AssertionError()
208
+ op = operations.Feed_publish(
209
+ **{
210
+ "publisher": account["name"],
211
+ "exchange_rate": {
212
+ "base": base,
213
+ "quote": quote,
214
+ },
215
+ "prefix": self.blockchain.prefix,
216
+ "json_str": True,
217
+ }
218
+ )
219
+ return self.blockchain.finalizeOp(op, account, "active")
220
+
221
+ def update_witness(self, signing_key, url, props, account=None):
222
+ """Update witness
223
+
224
+ :param str signing_key: Signing key
225
+ :param str url: URL
226
+ :param dict props: Properties
227
+ :param str account: (optional) witness account name
228
+
229
+ Properties:::
230
+
231
+ {
232
+ "account_creation_fee": x,
233
+ "maximum_block_size": x,
234
+ "sbd_interest_rate": x,
235
+ }
236
+
237
+ """
238
+ if not account:
239
+ account = self["owner"]
240
+ return self.blockchain.witness_update(signing_key, url, props, account=account)
241
+
242
+ def update(self, *args: Any, **kwargs: Any) -> Any:
243
+ """Compatibility shim to avoid clashing with MutableMapping.update"""
244
+ return self.update_witness(*args, **kwargs)
245
+
246
+ @staticmethod
247
+ def get_witness_by_account(
248
+ account_name: str, blockchain_instance: Any | None = None
249
+ ) -> dict[str, Any] | None:
250
+ """
251
+ Fetch witness information by account name using the `get_witness_by_account` RPC call.
252
+
253
+ :param str account_name: Name of the witness account
254
+ :param blockchain_instance: Nectar instance (optional)
255
+ :return: Witness data dictionary or None if not found/error
256
+ """
257
+ blockchain = blockchain_instance or shared_blockchain_instance()
258
+ try:
259
+ return blockchain.rpc.get_witness_by_account(account_name)
260
+ except (RPCError, NoMethodWithName):
261
+ return None
262
+
263
+
264
+ class WitnessesObject(list):
265
+ def printAsTable(self, sort_key="votes", reverse=True, return_str=False, **kwargs):
266
+ no_feed = False
267
+ if (
268
+ len(self) > 0
269
+ and "sbd_exchange_rate" not in self[0]
270
+ and "hbd_exchange_rate" not in self[0]
271
+ ):
272
+ table_header = ["Name", "Votes [PV]", "Disabled", "Missed", "Fee", "Size", "Version"]
273
+ no_feed = True
274
+ else:
275
+ table_header = [
276
+ "Name",
277
+ "Votes [PV]",
278
+ "Disabled",
279
+ "Missed",
280
+ "Feed base",
281
+ "Feed quote",
282
+ "Feed update",
283
+ "Fee",
284
+ "Size",
285
+ "Interest",
286
+ "Version",
287
+ ]
288
+ if "sbd_exchange_rate" in self[0]:
289
+ bd_exchange_rate = "sbd_exchange_rate"
290
+ bd_interest_rate = "sbd_interest_rate"
291
+ last_bd_exchange_update = "last_sbd_exchange_update"
292
+ else:
293
+ bd_exchange_rate = "hbd_exchange_rate"
294
+ bd_interest_rate = "hbd_interest_rate"
295
+ last_bd_exchange_update = "last_hbd_exchange_update"
296
+ t = PrettyTable(table_header)
297
+ t.align = "l"
298
+ if sort_key == "base":
299
+ sortedList = sorted(
300
+ self, key=lambda self: self[bd_exchange_rate]["base"], reverse=reverse
301
+ )
302
+ elif sort_key == "quote":
303
+ sortedList = sorted(
304
+ self, key=lambda self: self[bd_exchange_rate]["quote"], reverse=reverse
305
+ )
306
+ elif sort_key == "last_sbd_exchange_update" or sort_key == "last_hbd_exchange_update":
307
+ sortedList = sorted(
308
+ self,
309
+ key=lambda self: (
310
+ datetime.now(timezone.utc) - self[last_bd_exchange_update]
311
+ ).total_seconds(),
312
+ reverse=reverse,
313
+ )
314
+ elif sort_key == "account_creation_fee":
315
+ sortedList = sorted(
316
+ self, key=lambda self: self["props"]["account_creation_fee"], reverse=reverse
317
+ )
318
+ elif sort_key == "sbd_interest_rate" or sort_key == "hbd_interest_rate":
319
+ sortedList = sorted(
320
+ self, key=lambda self: self["props"][bd_interest_rate], reverse=reverse
321
+ )
322
+ elif sort_key == "maximum_block_size":
323
+ sortedList = sorted(
324
+ self, key=lambda self: self["props"]["maximum_block_size"], reverse=reverse
325
+ )
326
+ elif sort_key == "votes":
327
+ sortedList = sorted(self, key=lambda self: int(self[sort_key]), reverse=reverse)
328
+ else:
329
+ sortedList = sorted(self, key=lambda self: self[sort_key], reverse=reverse)
330
+ for witness in sortedList:
331
+ disabled = ""
332
+ if not witness.is_active:
333
+ disabled = "yes"
334
+
335
+ if no_feed:
336
+ t.add_row(
337
+ [
338
+ witness["owner"],
339
+ str(round(int(witness["votes"]) / 1e15, 2)),
340
+ disabled,
341
+ str(witness["total_missed"]),
342
+ str(witness["props"]["account_creation_fee"]),
343
+ str(witness["props"]["maximum_block_size"]),
344
+ witness["running_version"],
345
+ ]
346
+ )
347
+ else:
348
+ # Handle both string and datetime formats for exchange update timestamps
349
+ exchange_update = witness[last_bd_exchange_update]
350
+ if isinstance(exchange_update, str):
351
+ exchange_update_dt = datetime.strptime(
352
+ exchange_update, "%Y-%m-%dT%H:%M:%S"
353
+ ).replace(tzinfo=timezone.utc)
354
+ else:
355
+ exchange_update_dt = exchange_update
356
+ td = datetime.now(timezone.utc) - exchange_update_dt
357
+ t.add_row(
358
+ [
359
+ witness["owner"],
360
+ str(round(int(witness["votes"]) / 1e15, 2)),
361
+ disabled,
362
+ str(witness["total_missed"]),
363
+ str(
364
+ Amount(
365
+ witness[bd_exchange_rate]["base"],
366
+ blockchain_instance=witness.blockchain,
367
+ )
368
+ ),
369
+ str(
370
+ Amount(
371
+ witness[bd_exchange_rate]["quote"],
372
+ blockchain_instance=witness.blockchain,
373
+ )
374
+ ),
375
+ str(td.days)
376
+ + " days "
377
+ + str(td.seconds // 3600)
378
+ + ":"
379
+ + str((td.seconds // 60) % 60),
380
+ str(
381
+ Amount(
382
+ witness["props"]["account_creation_fee"],
383
+ blockchain_instance=witness.blockchain,
384
+ )
385
+ ),
386
+ str(witness["props"]["maximum_block_size"]),
387
+ str(witness["props"][bd_interest_rate] / 100) + " %",
388
+ witness["running_version"],
389
+ ]
390
+ )
391
+ if return_str:
392
+ return t.get_string(**kwargs)
393
+ else:
394
+ print(t.get_string(**kwargs))
395
+
396
+ def get_votes_sum(self):
397
+ vote_sum = 0
398
+ for witness in self:
399
+ vote_sum += int(witness["votes"])
400
+ return vote_sum
401
+
402
+ def __contains__(self, item: object, /) -> bool: # type: ignore[override]
403
+ from .account import Account
404
+
405
+ if isinstance(item, Account):
406
+ name = item["name"]
407
+ elif isinstance(item, str):
408
+ name = item
409
+ elif len(self) > 0 and hasattr(self[0], "blockchain") and isinstance(item, dict):
410
+ account = Account(item, blockchain_instance=self[0].blockchain)
411
+ name = account["name"]
412
+ else:
413
+ # Fallback to string comparison
414
+ name = str(item)
415
+
416
+ return any([name == x["owner"] for x in self])
417
+
418
+ def __str__(self):
419
+ return self.printAsTable(return_str=True)
420
+
421
+ def __repr__(self):
422
+ return "<{} {}>".format(
423
+ self.__class__.__name__,
424
+ str(getattr(self, "identifier", f"list_{len(self)}")),
425
+ )
426
+
427
+
428
+ class GetWitnesses(WitnessesObject):
429
+ """Obtain a list of witnesses
430
+
431
+ :param list name_list: list of witneses to fetch
432
+ :param int batch_limit: (optional) maximum number of witnesses
433
+ to fetch per call, defaults to 100
434
+ :param nectar.nectar.nectar blockchain_instance: nectar instance to use when
435
+ accessing the RPC
436
+
437
+ .. code-block:: python
438
+
439
+ from nectar.witness import GetWitnesses
440
+ w = GetWitnesses(["gtg", "jesta"])
441
+ print(w[0].json())
442
+ print(w[1].json())
443
+
444
+ """
445
+
446
+ def __init__(
447
+ self,
448
+ name_list,
449
+ batch_limit=100,
450
+ lazy=False,
451
+ full=True,
452
+ blockchain_instance=None,
453
+ ):
454
+ """
455
+ Initialize the GetWitnesses collection by fetching witness objects for the given list of account names.
456
+
457
+ Names are fetched in batches (size controlled by `batch_limit`). If no blockchain connection is available the initializer returns early and the collection remains empty.
458
+
459
+ Parameters:
460
+ name_list (Iterable[str]): Account names of witnesses to retrieve.
461
+ batch_limit (int): Maximum number of names to request per batch.
462
+ lazy (bool): If True, create Witness objects in lazy-loading mode.
463
+ full (bool): If True, create Witness objects with full data loaded.
464
+ """
465
+ self.blockchain = blockchain_instance or shared_blockchain_instance()
466
+ if not self.blockchain.is_connected():
467
+ return
468
+ witnesses = []
469
+ name_cnt = 0
470
+ while name_cnt < len(name_list):
471
+ self.blockchain.rpc.set_next_node_on_empty_reply(False)
472
+ witnesses += self.blockchain.rpc.find_witnesses(
473
+ {"owners": name_list[name_cnt : batch_limit + name_cnt]}
474
+ )["witnesses"]
475
+ name_cnt += batch_limit
476
+ self.identifier = ""
477
+ super().__init__(
478
+ [
479
+ Witness(x, lazy=lazy, full=full, blockchain_instance=self.blockchain)
480
+ for x in witnesses
481
+ ]
482
+ )
483
+
484
+
485
+ class Witnesses(WitnessesObject):
486
+ """Obtain a list of **active** witnesses and the current schedule
487
+
488
+ :param nectar.nectar.nectar blockchain_instance: nectar instance to use when
489
+ accessing the RPC
490
+
491
+ .. code-block:: python
492
+
493
+ >>> from nectar.witness import Witnesses
494
+ >>> Witnesses()
495
+ <Witnesses >
496
+
497
+ """
498
+
499
+ def __init__(self, lazy=False, full=True, blockchain_instance=None):
500
+ """
501
+ Initialize a Witnesses collection and load active witnesses from the configured blockchain.
502
+
503
+ Parameters:
504
+ lazy (bool): If True, create Witness objects without fetching full data (deferred loading).
505
+ full (bool): If True, eager-load full witness data when constructing each Witness.
506
+
507
+ Notes:
508
+ Resolves the blockchain instance from `blockchain_instance` or the shared default and immediately calls `refresh()` to populate the list of active witnesses.
509
+ """
510
+ self.blockchain = blockchain_instance or shared_blockchain_instance()
511
+ self.lazy = lazy
512
+ self.full = full
513
+ self.refresh()
514
+
515
+ def refresh(self):
516
+ self.blockchain.rpc.set_next_node_on_empty_reply(False)
517
+ self.active_witnessess = self.blockchain.rpc.get_active_witnesses()["witnesses"]
518
+ self.schedule = self.blockchain.rpc.get_witness_schedule()
519
+ try:
520
+ self.witness_count = self.blockchain.rpc.get_witness_count()
521
+ except (RPCError, NoMethodWithName):
522
+ try:
523
+ # Some nodes expose the method only on condenser_api; fall back to that.
524
+ self.witness_count = self.blockchain.rpc.get_witness_count(api="condenser_api")
525
+ except Exception:
526
+ # As a last resort, derive the count from the active witness set.
527
+ self.witness_count = len(self.active_witnessess)
528
+ self.current_witness = self.blockchain.get_dynamic_global_properties(use_stored_data=False)[
529
+ "current_witness"
530
+ ]
531
+ self.identifier = ""
532
+ super().__init__(
533
+ [
534
+ Witness(x, lazy=self.lazy, full=self.full, blockchain_instance=self.blockchain)
535
+ for x in self.active_witnessess
536
+ ]
537
+ )
538
+
539
+
540
+ class WitnessesVotedByAccount(WitnessesObject):
541
+ """Obtain a list of witnesses which have been voted by an account
542
+
543
+ :param str account: Account name
544
+ :param nectar.nectar.nectar blockchain_instance: nectar instance to use when
545
+ accessing the RPC
546
+
547
+ .. code-block:: python
548
+
549
+ >>> from nectar.witness import WitnessesVotedByAccount
550
+ >>> WitnessesVotedByAccount("gtg")
551
+ <WitnessesVotedByAccount gtg>
552
+
553
+ """
554
+
555
+ def __init__(self, account, lazy=False, full=True, blockchain_instance=None):
556
+ """
557
+ Initialize a WitnessesVotedByAccount collection for the given account.
558
+
559
+ Resolves the provided account to a full Account object, reads the list of witnesses that the account voted for, and populates the list with Witness objects created with the specified loading flags. If the account has no witness votes recorded the constructor returns early and the collection remains empty. The instance identifier is set to the account name.
560
+
561
+ Parameters:
562
+ account (str|Account): Account name or Account-like object to inspect for witness votes.
563
+ lazy (bool): If True, create Witness objects in lazy-loading mode. Defaults to False.
564
+ full (bool): If True, request full witness data when constructing Witness objects. Defaults to True.
565
+
566
+ Note:
567
+ The blockchain instance is taken from the optional `blockchain_instance` argument or the shared default; it is not documented here as a parameter.
568
+ """
569
+ self.blockchain = blockchain_instance or shared_blockchain_instance()
570
+ self.account = Account(account, full=True, blockchain_instance=self.blockchain)
571
+ account_name = self.account["name"]
572
+ self.identifier = account_name
573
+ self.blockchain.rpc.set_next_node_on_empty_reply(False)
574
+ if "witnesses_voted_for" not in self.account:
575
+ return
576
+ limit = self.account["witnesses_voted_for"]
577
+ witnessess_dict = self.blockchain.rpc.list_witness_votes(
578
+ {"start": [account_name], "limit": limit, "order": "by_account_witness"}
579
+ )["votes"]
580
+ witnessess = []
581
+ for w in witnessess_dict:
582
+ witnessess.append(w["witness"])
583
+
584
+ super().__init__(
585
+ [
586
+ Witness(x, lazy=lazy, full=full, blockchain_instance=self.blockchain)
587
+ for x in witnessess
588
+ ]
589
+ )
590
+
591
+
592
+ class WitnessesRankedByVote(WitnessesObject):
593
+ """Obtain a list of witnesses ranked by Vote
594
+
595
+ :param str from_account: Witness name from which the lists starts (default = "")
596
+ :param int limit: Limits the number of shown witnesses (default = 100)
597
+ :param nectar.nectar.nectar blockchain_instance: nectar instance to use when
598
+ accessing the RPC
599
+
600
+ .. code-block:: python
601
+
602
+ >>> from nectar.witness import WitnessesRankedByVote
603
+ >>> WitnessesRankedByVote(limit=100)
604
+ <WitnessesRankedByVote >
605
+
606
+ """
607
+
608
+ def __init__(
609
+ self,
610
+ from_account="",
611
+ limit=100,
612
+ lazy=False,
613
+ full=False,
614
+ blockchain_instance=None,
615
+ ):
616
+ """
617
+ Initialize a list of witnesses ranked by vote, with optional pagination.
618
+
619
+ Builds a WitnessesRankedByVote list starting at `from_account` and returning up to `limit`
620
+ entries. The constructor transparently pages RPC calls when the requested `limit`
621
+ exceeds the per-call query limit, handles RPC calls and wraps returned witness entries as Witness objects.
622
+
623
+ Parameters:
624
+ from_account (str): Account name to start ranking from (inclusive). When empty, ranking starts from the top.
625
+ limit (int): Maximum number of witnesses to return.
626
+ lazy (bool): If True, create Witness objects in lazy-loading mode.
627
+ full (bool): If True, fully load each Witness on creation.
628
+
629
+ Notes:
630
+ - `blockchain_instance` is taken from the shared instance when not provided.
631
+ - The method uses different RPC endpoints depending on the node configuration
632
+ (appbase vs non-appbase, and condenser mode) and automatically pages results
633
+ to satisfy `limit`.
634
+ - Returns early (no list created) if no witnesses are found.
635
+ """
636
+ self.blockchain = blockchain_instance or shared_blockchain_instance()
637
+ witnessList = []
638
+ last_limit = limit
639
+ self.identifier = ""
640
+ self.blockchain.rpc.set_next_node_on_empty_reply(False)
641
+ query_limit = 1000
642
+
643
+ if from_account == "":
644
+ last_account = None
645
+ else:
646
+ last_account = Witness(from_account, blockchain_instance=self.blockchain)["votes"]
647
+ if limit > query_limit:
648
+ while last_limit > query_limit:
649
+ tmpList = WitnessesRankedByVote(last_account, query_limit)
650
+ if last_limit < limit:
651
+ witnessList.extend(tmpList[1:])
652
+ last_limit -= query_limit - 1
653
+ else:
654
+ witnessList.extend(tmpList)
655
+ last_limit -= query_limit
656
+
657
+ last_account = witnessList[-1]["votes"]
658
+ if last_limit < limit:
659
+ last_limit += 1
660
+ witnessess = self.blockchain.rpc.list_witnesses(
661
+ {"start": [0, last_account], "limit": last_limit, "order": "by_vote_name"}
662
+ )["witnesses"]
663
+
664
+ # self.witness_count = len(self.voted_witnessess)
665
+ if last_limit < limit:
666
+ witnessess = witnessess[1:]
667
+ if len(witnessess) > 0:
668
+ for x in witnessess:
669
+ witnessList.append(
670
+ Witness(x, lazy=lazy, full=full, blockchain_instance=self.blockchain)
671
+ )
672
+ if len(witnessList) == 0:
673
+ return
674
+ super().__init__(witnessList)
675
+
676
+
677
+ class ListWitnesses(WitnessesObject):
678
+ """List witnesses ranked by name
679
+
680
+ :param str from_account: Witness name from which the list starts (default = "")
681
+ :param int limit: Limits the number of shown witnesses (default = 100)
682
+ :param nectar.nectar.nectar blockchain_instance: nectar instance to use when
683
+ accessing the RPC
684
+
685
+ .. code-block:: python
686
+
687
+ >>> from nectar.witness import ListWitnesses
688
+ >>> ListWitnesses(from_account="gtg", limit=100)
689
+ <ListWitnesses gtg>
690
+
691
+ """
692
+
693
+ def __init__(
694
+ self,
695
+ from_account="",
696
+ limit=100,
697
+ lazy=False,
698
+ full=False,
699
+ blockchain_instance=None,
700
+ ):
701
+ """
702
+ Initialize a ListWitnesses collection starting from a given account name.
703
+
704
+ Creates a list of Witness objects beginning at `from_account` (lexicographic start)
705
+ up to `limit` entries. If no witnesses are found the constructor returns early
706
+ leaving the instance empty. The object uses the provided blockchain instance
707
+ (or the shared default) to query the node and sets `identifier` to `from_account`.
708
+
709
+ Parameters:
710
+ from_account (str): Account name to start listing witnesses from (inclusive).
711
+ limit (int): Maximum number of witness entries to retrieve.
712
+ lazy (bool): If True, construct Witness objects in lazy mode (defer full data load).
713
+ full (bool): If True, request full witness data when constructing Witness objects.
714
+ """
715
+ self.blockchain = blockchain_instance or shared_blockchain_instance()
716
+ self.identifier = from_account
717
+ self.blockchain.rpc.set_next_node_on_empty_reply(False)
718
+ witnessess = self.blockchain.rpc.list_witnesses(
719
+ {"start": from_account, "limit": limit, "order": "by_name"}
720
+ )["witnesses"]
721
+ if len(witnessess) == 0:
722
+ return
723
+ super().__init__(
724
+ [
725
+ Witness(x, lazy=lazy, full=full, blockchain_instance=self.blockchain)
726
+ for x in witnessess
727
+ ]
728
+ )