hive-nectar 0.0.11__py3-none-any.whl → 0.1.1__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.

Potentially problematic release.


This version of hive-nectar might be problematic. Click here for more details.

Files changed (56) hide show
  1. {hive_nectar-0.0.11.dist-info → hive_nectar-0.1.1.dist-info}/METADATA +12 -11
  2. hive_nectar-0.1.1.dist-info/RECORD +88 -0
  3. nectar/__init__.py +1 -4
  4. nectar/account.py +791 -685
  5. nectar/amount.py +82 -21
  6. nectar/asset.py +1 -2
  7. nectar/block.py +49 -23
  8. nectar/blockchain.py +111 -143
  9. nectar/blockchaininstance.py +396 -247
  10. nectar/blockchainobject.py +33 -5
  11. nectar/cli.py +1058 -1349
  12. nectar/comment.py +313 -181
  13. nectar/community.py +39 -43
  14. nectar/constants.py +1 -14
  15. nectar/discussions.py +793 -139
  16. nectar/hive.py +137 -77
  17. nectar/hivesigner.py +106 -68
  18. nectar/imageuploader.py +33 -23
  19. nectar/instance.py +31 -79
  20. nectar/market.py +128 -264
  21. nectar/memo.py +40 -13
  22. nectar/message.py +23 -10
  23. nectar/nodelist.py +115 -81
  24. nectar/price.py +80 -61
  25. nectar/profile.py +6 -3
  26. nectar/rc.py +45 -25
  27. nectar/snapshot.py +285 -163
  28. nectar/storage.py +16 -5
  29. nectar/transactionbuilder.py +132 -41
  30. nectar/utils.py +37 -17
  31. nectar/version.py +1 -1
  32. nectar/vote.py +171 -30
  33. nectar/wallet.py +26 -19
  34. nectar/witness.py +153 -54
  35. nectarapi/graphenerpc.py +147 -133
  36. nectarapi/noderpc.py +12 -6
  37. nectarapi/rpcutils.py +12 -6
  38. nectarapi/version.py +1 -1
  39. nectarbase/ledgertransactions.py +24 -1
  40. nectarbase/objects.py +17 -6
  41. nectarbase/operations.py +160 -90
  42. nectarbase/signedtransactions.py +38 -2
  43. nectarbase/version.py +1 -1
  44. nectargraphenebase/account.py +295 -17
  45. nectargraphenebase/chains.py +0 -135
  46. nectargraphenebase/ecdsasig.py +152 -176
  47. nectargraphenebase/types.py +18 -4
  48. nectargraphenebase/unsignedtransactions.py +1 -1
  49. nectargraphenebase/version.py +1 -1
  50. hive_nectar-0.0.11.dist-info/RECORD +0 -91
  51. nectar/blurt.py +0 -562
  52. nectar/conveyor.py +0 -308
  53. nectar/steem.py +0 -581
  54. {hive_nectar-0.0.11.dist-info → hive_nectar-0.1.1.dist-info}/WHEEL +0 -0
  55. {hive_nectar-0.0.11.dist-info → hive_nectar-0.1.1.dist-info}/entry_points.txt +0 -0
  56. {hive_nectar-0.0.11.dist-info → hive_nectar-0.1.1.dist-info}/licenses/LICENSE.txt +0 -0
nectar/storage.py CHANGED
@@ -15,12 +15,23 @@ timeformat = "%Y%m%d-%H%M%S"
15
15
 
16
16
  def generate_config_store(config, blockchain="hive"):
17
17
  #: Default configuration
18
+ """
19
+ Populate a configuration mapping with sensible defaults for Hive-related settings and return it.
20
+
21
+ This function mutates the provided mapping in-place by ensuring a set of default configuration keys exist and returns the same mapping. When `blockchain` is "hive" it fills the "node" entry with a current list of Hive nodes; for other values "node" is set to an empty list. Defaults include client and RPC placeholders, order expiration (7 days), HiveSigner endpoints (`hs_api_url`, `hs_oauth_base_url`) and a backward-compatible `oauth_base_url` pointing to the HiveSigner OAuth base URL, canonical URL, default derivation path, and boolean switches for `use_condenser` and `use_tor`.
22
+
23
+ Parameters:
24
+ config (MutableMapping): A dict-like configuration object to populate. It will be modified in place.
25
+ blockchain (str): Chain identifier; "hive" populates Hive nodes, any other value leaves the node list empty.
26
+
27
+ Returns:
28
+ MutableMapping: The same `config` mapping after defaults have been set.
29
+ """
18
30
  nodelist = NodeList()
19
31
  if blockchain == "hive":
20
32
  nodes = nodelist.get_hive_nodes(testnet=False)
21
- elif blockchain == "steem":
22
- nodes = nodelist.get_steem_nodes(testnet=False)
23
33
  else:
34
+ # Hive-only
24
35
  nodes = []
25
36
 
26
37
  config.setdefault("node", nodes)
@@ -30,13 +41,13 @@ def generate_config_store(config, blockchain="hive"):
30
41
  config.setdefault("rpcuser", "")
31
42
  config.setdefault("order-expiration", 7 * 24 * 60 * 60)
32
43
  config.setdefault("client_id", "")
33
- config.setdefault("sc2_client_id", None)
34
44
  config.setdefault("hs_client_id", None)
35
45
  config.setdefault("hot_sign_redirect_uri", None)
36
- config.setdefault("sc2_api_url", "https://api.steemconnect.com/api/")
37
- config.setdefault("oauth_base_url", "https://api.steemconnect.com/oauth2/")
46
+ # HiveSigner defaults
38
47
  config.setdefault("hs_api_url", "https://hivesigner.com/api/")
39
48
  config.setdefault("hs_oauth_base_url", "https://hivesigner.com/oauth2/")
49
+ # Backward-compat key used elsewhere; keep but point to HiveSigner
50
+ config.setdefault("oauth_base_url", config["hs_oauth_base_url"])
40
51
  config.setdefault("default_canonical_url", "https://hive.blog")
41
52
  config.setdefault("default_path", "48'/13'/0'/0'/0'")
42
53
  config.setdefault("use_condenser", True)
@@ -7,7 +7,7 @@ from binascii import unhexlify
7
7
  from datetime import timedelta
8
8
 
9
9
  from nectar.instance import shared_blockchain_instance
10
- from nectarbase import operations # removed deprecated transactions module
10
+ from nectarbase import operations
11
11
  from nectarbase.ledgertransactions import Ledger_Transaction
12
12
  from nectarbase.objects import Operation
13
13
  from nectarbase.signedtransactions import Signed_Transaction
@@ -34,7 +34,7 @@ class TransactionBuilder(dict):
34
34
  :param dict tx: transaction (Optional). If not set, the new transaction is created.
35
35
  :param int expiration: Delay in seconds until transactions are supposed
36
36
  to expire *(optional)* (default is 30)
37
- :param Hive/Steem blockchain_instance: If not set, shared_blockchain_instance() is used
37
+ :param Hive blockchain_instance: If not set, shared_blockchain_instance() is used
38
38
 
39
39
  .. testcode::
40
40
 
@@ -52,12 +52,26 @@ class TransactionBuilder(dict):
52
52
 
53
53
  """
54
54
 
55
- def __init__(self, tx={}, blockchain_instance=None, **kwargs):
56
- if blockchain_instance is None:
57
- if kwargs.get("steem_instance"):
58
- blockchain_instance = kwargs["steem_instance"]
59
- elif kwargs.get("hive_instance"):
60
- blockchain_instance = kwargs["hive_instance"]
55
+ def __init__(self, tx=None, blockchain_instance=None, **kwargs):
56
+ """
57
+ Initialize a TransactionBuilder, optionally from an existing transaction dict.
58
+
59
+ If `tx` is a dict, its contents are loaded into the builder (operations are taken from tx["operations"])
60
+ and reconstruction is marked as not required. Otherwise the builder starts empty and is marked to
61
+ require reconstruction before use. The constructor clears any prior state, captures ledger/path and
62
+ condenser API configuration from the provided blockchain instance (or the shared instance), and sets
63
+ the transaction expiration from kwargs or the blockchain default.
64
+
65
+ Parameters:
66
+ tx (dict, optional): An existing transaction dictionary to load. When provided and valid,
67
+ operations are initialized from tx["operations"] and reconstruction is disabled.
68
+ expiration (int, optional, via kwargs): Transaction expiration (seconds or blockchain-specific
69
+ expiration value). If omitted, the blockchain instance's default expiration is used.
70
+
71
+ Notes:
72
+ - The `blockchain_instance` parameter is a shared service/client and is intentionally not
73
+ documented here beyond its effect on builder configuration.
74
+ """
61
75
  self.blockchain = blockchain_instance or shared_blockchain_instance()
62
76
  self.clear()
63
77
  if tx and isinstance(tx, dict):
@@ -195,12 +209,22 @@ class TransactionBuilder(dict):
195
209
  return r
196
210
 
197
211
  def appendSigner(self, account, permission):
198
- """Try to obtain the wif key from the wallet by telling which account
199
- and permission is supposed to sign the transaction
200
- It is possible to add more than one signer.
212
+ """
213
+ Register an account as a signer for this transaction by locating or assigning the needed signing keys.
214
+
215
+ Attempts to resolve signing credentials for `account` at the requested `permission` and attaches them to the builder state. Behavior varies by signing mode:
216
+ - Ledger mode: verifies that the ledger-derived public key for the currently selected path is authorized for the account/permission (raises AssertionError if not).
217
+ - HiveSigner mode: sets the HiveSigner username (only supports "posting") and returns.
218
+ - Wallet mode: fetches private keys from the local wallet and stores corresponding WIFs; if the account argument is a PublicKey, the matching WIF is retrieved and added.
201
219
 
202
- :param str account: account to sign transaction with
203
- :param str permission: type of permission, e.g. "active", "owner" etc
220
+ Parameters:
221
+ account (str | Account | PublicKey): account name, Account instance, or public key identifying the signer.
222
+ permission (str): permission level to use ("active", "owner", or "posting").
223
+
224
+ Raises:
225
+ WalletLocked: if the local wallet is locked when wallet keys are required.
226
+ AssertionError: for invalid permission values, if the requested permission cannot be accessed, or if a ledger public key is not found in the account authorities.
227
+ ValueError: if HiveSigner is used with a permission other than "posting".
204
228
  """
205
229
  if not self.blockchain.is_connected():
206
230
  return
@@ -241,11 +265,16 @@ class TransactionBuilder(dict):
241
265
  )
242
266
  return
243
267
 
268
+ if self.blockchain.use_hs and self.blockchain.hivesigner is not None:
269
+ # HiveSigner only supports posting permission
270
+ if permission != "posting":
271
+ raise ValueError(
272
+ f"HiveSigner only supports 'posting' permission, got '{permission}'"
273
+ )
274
+ self.blockchain.hivesigner.set_username(account["name"], permission)
275
+ return
244
276
  if self.blockchain.wallet.locked():
245
277
  raise WalletLocked()
246
- if self.blockchain.use_sc2 and self.blockchain.steemconnect is not None:
247
- self.blockchain.steemconnect.set_username(account["name"], permission)
248
- return
249
278
 
250
279
  if account["name"] not in self.signing_accounts:
251
280
  # is the account an instance of public key?
@@ -398,24 +427,45 @@ class TransactionBuilder(dict):
398
427
  return ref_block_num, ref_block_prefix
399
428
 
400
429
  def sign(self, reconstruct_tx=True):
401
- """Sign a provided transaction with the provided key(s)
402
- One or many wif keys to use for signing a transaction.
403
- The wif keys can be provided by "appendWif" or the
404
- signer can be defined "appendSigner". The wif keys
405
- from all signer that are defined by "appendSigner
406
- will be loaded from the wallet.
430
+ """
431
+ Sign the built transaction using HiveSigner, a Ledger device, or local WIFs and attach signatures to the builder.
407
432
 
408
- :param bool reconstruct_tx: when set to False and tx
409
- is already contructed, it will not reconstructed
410
- and already added signatures remain
433
+ If the transaction is not constructed (or if reconstruct_tx is True) the transaction will be reconstructed before signing. The method attempts signing in this order:
434
+ 1. HiveSigner (if configured) uses remote signing and attaches returned signatures.
435
+ 2. Ledger (if ledger mode is active) — signs with the Ledger device and appends signatures.
436
+ 3. Local WIFs from the builder — uses stored WIFs to sign the transaction and appends signatures.
411
437
 
438
+ Parameters:
439
+ reconstruct_tx (bool): If False and the transaction is already constructed, existing signatures are preserved and the transaction will not be rebuilt before signing. Defaults to True.
440
+
441
+ Returns:
442
+ The object returned by the signing step:
443
+ - HiveSigner result (dict) when HiveSigner was used successfully,
444
+ - Ledger_Transaction when signed via Ledger,
445
+ - Signed_Transaction when signed locally with WIFs.
446
+ Returns None if there are no operations to sign.
447
+
448
+ Raises:
449
+ MissingKeyError: If local signing is attempted but no WIFs are available.
412
450
  """
413
451
  if not self._is_constructed() or (self._is_constructed() and reconstruct_tx):
414
452
  self.constructTx()
415
453
  if "operations" not in self or not self["operations"]:
416
454
  return
417
- if self.blockchain.use_sc2:
418
- return
455
+ if self.blockchain.use_hs and self.blockchain.hivesigner is not None:
456
+ # Use HiveSigner for signing
457
+ try:
458
+ signed_tx = self.blockchain.hivesigner.sign(self.json())
459
+ if signed_tx and "signatures" in signed_tx:
460
+ # Attach the signatures from HiveSigner to the transaction
461
+ self["signatures"] = signed_tx["signatures"]
462
+ return signed_tx
463
+ else:
464
+ raise ValueError("HiveSigner failed to sign transaction")
465
+ except Exception as e:
466
+ log.error(f"HiveSigner signing failed: {e}")
467
+ # Fall back to regular signing if HiveSigner fails
468
+ pass
419
469
  # We need to set the default prefix, otherwise pubkeys are
420
470
  # presented wrongly!
421
471
  if self.blockchain.rpc is not None:
@@ -495,7 +545,23 @@ class TransactionBuilder(dict):
495
545
  return ret
496
546
 
497
547
  def get_required_signatures(self, available_keys=list()):
498
- """Returns public key from signature"""
548
+ """
549
+ Return the subset of public keys required to sign this transaction from a set of available keys.
550
+
551
+ This method requires an active RPC connection and delegates to the node's
552
+ get_required_signatures API to determine which of the provided available_keys
553
+ are necessary to satisfy the transaction's authority requirements.
554
+
555
+ Parameters:
556
+ available_keys (list): Iterable of public key strings to consider when
557
+ determining required signers.
558
+
559
+ Returns:
560
+ list: Public key strings that the node reports are required to sign the transaction.
561
+
562
+ Raises:
563
+ OfflineHasNoRPCException: If called while offline (no RPC available).
564
+ """
499
565
  if not self.blockchain.is_connected():
500
566
  raise OfflineHasNoRPCException("No RPC available in offline mode!")
501
567
  self.blockchain.rpc.set_next_node_on_empty_reply(False)
@@ -510,16 +576,28 @@ class TransactionBuilder(dict):
510
576
  return ret
511
577
 
512
578
  def broadcast(self, max_block_age=-1, trx_id=True):
513
- """Broadcast a transaction to the steem network
514
- Returns the signed transaction and clears itself
515
- after broadast
516
-
517
- Clears itself when broadcast was not successfully.
518
-
519
- :param int max_block_age: parameter only used
520
- for appbase ready nodes
521
- :param bool trx_id: When True, trx_id is return
522
-
579
+ """
580
+ Broadcast the built transaction to the Hive network and clear the builder state.
581
+
582
+ If the transaction is not yet signed this method will attempt to sign it first.
583
+ If no operations are present the call returns None. When broadcasting is disabled
584
+ (nobroadcast) the constructed transaction dict is returned and the builder is cleared.
585
+
586
+ Parameters:
587
+ max_block_age (int): Passed to appbase/network_broadcast calls to constrain
588
+ acceptable block age; ignored for condenser API paths. Default -1.
589
+ trx_id (bool): If True and a signing step produced a transaction id, attach
590
+ it to the returned result when the RPC response lacks a `trx_id`. Default True.
591
+
592
+ Returns:
593
+ dict or whatever the underlying broadcast method returns, or None if there
594
+ are no operations to broadcast.
595
+
596
+ Side effects:
597
+ - May sign the transaction if it is not already signed.
598
+ - May use HiveSigner for broadcast when configured; falls back to RPC on failure.
599
+ - Clears internal transaction state on successful broadcast or on errors.
600
+ - May raise exceptions from the signing or RPC broadcast calls.
523
601
  """
524
602
  # Cannot broadcast an empty transaction
525
603
  if not self._is_signed():
@@ -539,6 +617,21 @@ class TransactionBuilder(dict):
539
617
  args = self.json()
540
618
  broadcast_api = "condenser"
541
619
 
620
+ if self.blockchain.use_hs and self.blockchain.hivesigner is not None:
621
+ # Use HiveSigner for broadcasting
622
+ try:
623
+ # HiveSigner.broadcast expects operations list, not full transaction
624
+ username = self.signing_accounts[0] if self.signing_accounts else None
625
+ ret = self.blockchain.hivesigner.broadcast(
626
+ self.json()["operations"], username=username
627
+ )
628
+ self.clear()
629
+ return ret
630
+ except Exception as e:
631
+ log.error(f"HiveSigner broadcast failed: {e}")
632
+ # Fall back to RPC broadcasting if HiveSigner fails
633
+ pass
634
+
542
635
  if self.blockchain.nobroadcast:
543
636
  log.info("Not broadcasting anything!")
544
637
  self.clear()
@@ -546,9 +639,7 @@ class TransactionBuilder(dict):
546
639
  # Broadcast
547
640
  try:
548
641
  self.blockchain.rpc.set_next_node_on_empty_reply(False)
549
- if self.blockchain.use_sc2:
550
- ret = self.blockchain.steemconnect.broadcast(self["operations"])
551
- elif self.blockchain.blocking and self._use_condenser_api:
642
+ if self.blockchain.blocking and self._use_condenser_api:
552
643
  ret = self.blockchain.rpc.broadcast_transaction_synchronous(args, api=broadcast_api)
553
644
  if "trx" in ret:
554
645
  ret.update(**ret.get("trx"))
nectar/utils.py CHANGED
@@ -174,21 +174,12 @@ def derive_permlink(
174
174
 
175
175
 
176
176
  def resolve_authorperm(identifier):
177
- """Correctly split a string containing an authorperm.
178
-
179
- Splits the string into author and permlink with the
180
- following separator: ``/``.
181
-
182
- Examples:
183
-
184
- .. code-block:: python
185
-
186
- >>> from nectar.utils import resolve_authorperm
187
- >>> author, permlink = resolve_authorperm('https://d.tube/#!/v/pottlund/m5cqkd1a')
188
- >>> author, permlink = resolve_authorperm("https://steemit.com/witness-category/@gtg/24lfrm-gtg-witness-log")
189
- >>> author, permlink = resolve_authorperm("@gtg/24lfrm-gtg-witness-log")
190
- >>> author, permlink = resolve_authorperm("https://busy.org/@gtg/24lfrm-gtg-witness-log")
177
+ """
178
+ Parse an author/permlink identifier and return (author, permlink).
191
179
 
180
+ Accepts plain "author/permlink" or "@author/permlink", site URLs containing "/@author/permlink",
181
+ and dtube-style URLs containing "#!/v/<author>/<permlink>". Returns a 2-tuple of strings
182
+ (author, permlink). Raises ValueError if the identifier cannot be parsed.
192
183
  """
193
184
  # without any http(s)
194
185
  match = re.match(r"@?([\w\-\.]*)/([\w\-]*)", identifier)
@@ -396,6 +387,37 @@ def seperate_yaml_dict_from_body(content):
396
387
 
397
388
 
398
389
  def create_yaml_header(comment, json_metadata={}, reply_identifier=None):
390
+ """
391
+ Create a YAML front-matter header string from post/comment data and metadata.
392
+
393
+ Builds a YAML block (string) beginning and ending with '---' that includes selected fields when present:
394
+ - title (quoted)
395
+ - permlink
396
+ - author
397
+ - "authored by" (from json_metadata["author"])
398
+ - description (quoted)
399
+ - canonical_url
400
+ - app
401
+ - last_update (from comment["last_update"] or comment["updated"])
402
+ - max_accepted_payout
403
+ - percent_hbd
404
+ - community (added when json_metadata["tags"] exists and comment["category"] differs from the first tag)
405
+ - tags (comma-separated list)
406
+ - beneficiaries (comma-separated entries formatted as "account:XX.XX%"; weights are converted from parts-per-10000 to percent with two decimals)
407
+ - reply_identifier
408
+
409
+ Parameters:
410
+ comment (dict): Source post/comment data. Expected keys used include
411
+ "title", "permlink", "author", "last_update" or "updated",
412
+ "max_accepted_payout", optional "percent_hbd", optional "category",
413
+ and optional "beneficiaries" (list of {"account": str, "weight": int}).
414
+ json_metadata (dict, optional): Parsed JSON metadata; may contain "author",
415
+ "description", "canonical_url", "app", and "tags" (list of strings).
416
+ reply_identifier (str or None, optional): If provided, added as "reply_identifier".
417
+
418
+ Returns:
419
+ str: The composed YAML front-matter block as a string.
420
+ """
399
421
  yaml_prefix = "---\n"
400
422
  if comment["title"] != "":
401
423
  yaml_prefix += 'title: "%s"\n' % comment["title"]
@@ -415,9 +437,7 @@ def create_yaml_header(comment, json_metadata={}, reply_identifier=None):
415
437
  elif "updated" in comment:
416
438
  yaml_prefix += "last_update: %s\n" % comment["updated"]
417
439
  yaml_prefix += "max_accepted_payout: %s\n" % str(comment["max_accepted_payout"])
418
- if "percent_steem_dollars" in comment:
419
- yaml_prefix += "percent_steem_dollars: %s\n" % str(comment["percent_steem_dollars"])
420
- elif "percent_hbd" in comment:
440
+ if "percent_hbd" in comment:
421
441
  yaml_prefix += "percent_hbd: %s\n" % str(comment["percent_hbd"])
422
442
  if "tags" in json_metadata:
423
443
  if (
nectar/version.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """THIS FILE IS GENERATED FROM nectar PYPROJECT.TOML."""
2
2
 
3
- version = "0.0.11"
3
+ version = "0.1.1"