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.
- hive_nectar-0.2.9.dist-info/METADATA +194 -0
- hive_nectar-0.2.9.dist-info/RECORD +87 -0
- hive_nectar-0.2.9.dist-info/WHEEL +4 -0
- hive_nectar-0.2.9.dist-info/entry_points.txt +2 -0
- hive_nectar-0.2.9.dist-info/licenses/LICENSE.txt +23 -0
- nectar/__init__.py +37 -0
- nectar/account.py +5076 -0
- nectar/amount.py +553 -0
- nectar/asciichart.py +303 -0
- nectar/asset.py +122 -0
- nectar/block.py +574 -0
- nectar/blockchain.py +1242 -0
- nectar/blockchaininstance.py +2590 -0
- nectar/blockchainobject.py +263 -0
- nectar/cli.py +5937 -0
- nectar/comment.py +1552 -0
- nectar/community.py +854 -0
- nectar/constants.py +95 -0
- nectar/discussions.py +1437 -0
- nectar/exceptions.py +152 -0
- nectar/haf.py +381 -0
- nectar/hive.py +630 -0
- nectar/imageuploader.py +114 -0
- nectar/instance.py +113 -0
- nectar/market.py +876 -0
- nectar/memo.py +542 -0
- nectar/message.py +379 -0
- nectar/nodelist.py +309 -0
- nectar/price.py +603 -0
- nectar/profile.py +74 -0
- nectar/py.typed +0 -0
- nectar/rc.py +333 -0
- nectar/snapshot.py +1024 -0
- nectar/storage.py +62 -0
- nectar/transactionbuilder.py +659 -0
- nectar/utils.py +630 -0
- nectar/version.py +3 -0
- nectar/vote.py +722 -0
- nectar/wallet.py +472 -0
- nectar/witness.py +728 -0
- nectarapi/__init__.py +12 -0
- nectarapi/exceptions.py +126 -0
- nectarapi/graphenerpc.py +596 -0
- nectarapi/node.py +194 -0
- nectarapi/noderpc.py +79 -0
- nectarapi/openapi.py +107 -0
- nectarapi/py.typed +0 -0
- nectarapi/rpcutils.py +98 -0
- nectarapi/version.py +3 -0
- nectarbase/__init__.py +15 -0
- nectarbase/ledgertransactions.py +106 -0
- nectarbase/memo.py +242 -0
- nectarbase/objects.py +521 -0
- nectarbase/objecttypes.py +21 -0
- nectarbase/operationids.py +102 -0
- nectarbase/operations.py +1357 -0
- nectarbase/py.typed +0 -0
- nectarbase/signedtransactions.py +89 -0
- nectarbase/transactions.py +11 -0
- nectarbase/version.py +3 -0
- nectargraphenebase/__init__.py +27 -0
- nectargraphenebase/account.py +1121 -0
- nectargraphenebase/aes.py +49 -0
- nectargraphenebase/base58.py +197 -0
- nectargraphenebase/bip32.py +575 -0
- nectargraphenebase/bip38.py +110 -0
- nectargraphenebase/chains.py +15 -0
- nectargraphenebase/dictionary.py +2 -0
- nectargraphenebase/ecdsasig.py +309 -0
- nectargraphenebase/objects.py +130 -0
- nectargraphenebase/objecttypes.py +8 -0
- nectargraphenebase/operationids.py +5 -0
- nectargraphenebase/operations.py +25 -0
- nectargraphenebase/prefix.py +13 -0
- nectargraphenebase/py.typed +0 -0
- nectargraphenebase/signedtransactions.py +221 -0
- nectargraphenebase/types.py +557 -0
- nectargraphenebase/unsignedtransactions.py +288 -0
- nectargraphenebase/version.py +3 -0
- nectarstorage/__init__.py +57 -0
- nectarstorage/base.py +317 -0
- nectarstorage/exceptions.py +15 -0
- nectarstorage/interfaces.py +244 -0
- nectarstorage/masterpassword.py +237 -0
- nectarstorage/py.typed +0 -0
- nectarstorage/ram.py +27 -0
- nectarstorage/sqlite.py +343 -0
nectar/imageuploader.py
ADDED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import io
|
|
2
|
+
from binascii import hexlify
|
|
3
|
+
from typing import Any, Dict, Optional, Union
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
from httpx import ConnectError, HTTPStatusError, RequestError, TimeoutException
|
|
7
|
+
|
|
8
|
+
from nectar.account import Account
|
|
9
|
+
from nectar.exceptions import MissingKeyError
|
|
10
|
+
from nectargraphenebase.ecdsasig import sign_message
|
|
11
|
+
|
|
12
|
+
from .instance import shared_blockchain_instance
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ImageUploader:
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
base_url: str = "https://images.hive.blog",
|
|
19
|
+
challenge: str = "ImageSigningChallenge",
|
|
20
|
+
blockchain_instance: Optional[Any] = None,
|
|
21
|
+
) -> None:
|
|
22
|
+
"""
|
|
23
|
+
Initialize the ImageUploader.
|
|
24
|
+
|
|
25
|
+
Parameters:
|
|
26
|
+
base_url (str): Base URL of the image upload service (default: "https://images.hive.blog").
|
|
27
|
+
challenge (str): ASCII string prepended to the image bytes when constructing the signing message; ensures signatures are bound to this uploader's purpose.
|
|
28
|
+
|
|
29
|
+
Notes:
|
|
30
|
+
blockchain_instance is an optional blockchain client; if not provided a shared instance is used.
|
|
31
|
+
"""
|
|
32
|
+
self.challenge = challenge
|
|
33
|
+
self.base_url = base_url
|
|
34
|
+
self.blockchain = blockchain_instance or shared_blockchain_instance()
|
|
35
|
+
|
|
36
|
+
def upload(
|
|
37
|
+
self,
|
|
38
|
+
image: Union[str, bytes, io.BytesIO],
|
|
39
|
+
account: Union[str, Account],
|
|
40
|
+
image_name: Optional[str] = None,
|
|
41
|
+
) -> Dict[str, Any]:
|
|
42
|
+
"""
|
|
43
|
+
Upload an image to the configured image service, signing the upload with the account's posting key.
|
|
44
|
+
|
|
45
|
+
The function accepts a filesystem path (str), raw bytes, or an io.BytesIO for the image. It locates the account's posting private key from the blockchain wallet, signs the image data together with the uploader's challenge string, and POSTs the image under the key `image_name` (defaults to "image") to: <base_url>/<account_name>/<signature_hex>.
|
|
46
|
+
|
|
47
|
+
Parameters:
|
|
48
|
+
image (str | bytes | io.BytesIO): Path to an image file, raw image bytes, or an in-memory bytes buffer.
|
|
49
|
+
account (str | Account): Account identifier (must have posting permission); used to select the signing key.
|
|
50
|
+
image_name (str, optional): Form field name for the uploaded image (defaults to "image").
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
dict: Parsed JSON response from the image service.
|
|
54
|
+
|
|
55
|
+
Raises:
|
|
56
|
+
AssertionError: If the account's posting permission (and therefore a posting key) cannot be accessed.
|
|
57
|
+
"""
|
|
58
|
+
account = Account(account, blockchain_instance=self.blockchain)
|
|
59
|
+
if "posting" not in account:
|
|
60
|
+
account.refresh()
|
|
61
|
+
if "posting" not in account:
|
|
62
|
+
raise AssertionError("Could not access posting permission")
|
|
63
|
+
posting_wif = None
|
|
64
|
+
for authority in account["posting"]["key_auths"]:
|
|
65
|
+
try:
|
|
66
|
+
posting_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(authority[0])
|
|
67
|
+
break
|
|
68
|
+
except MissingKeyError:
|
|
69
|
+
continue
|
|
70
|
+
if not posting_wif:
|
|
71
|
+
raise AssertionError("No local private posting key available to sign the image.")
|
|
72
|
+
|
|
73
|
+
if isinstance(image, str):
|
|
74
|
+
with open(image, "rb") as f:
|
|
75
|
+
image_data = f.read()
|
|
76
|
+
elif isinstance(image, io.BytesIO):
|
|
77
|
+
image_data = image.read()
|
|
78
|
+
else:
|
|
79
|
+
image_data = image
|
|
80
|
+
|
|
81
|
+
message = bytes(self.challenge, "ascii") + image_data
|
|
82
|
+
signature = sign_message(message, posting_wif)
|
|
83
|
+
signature_in_hex = hexlify(signature).decode("ascii")
|
|
84
|
+
|
|
85
|
+
# Prepare files for httpx
|
|
86
|
+
# We want to send the file with the field name "image" (expected by images.hive.blog)
|
|
87
|
+
# and the filename specified by image_name.
|
|
88
|
+
# If image_name is not provided, we default to "image".
|
|
89
|
+
filename = image_name or "image"
|
|
90
|
+
# files = {'field_name': ('filename', file_data)}
|
|
91
|
+
files = {"image": (filename, image_data)}
|
|
92
|
+
url = "{}/{}/{}".format(self.base_url, account["name"], signature_in_hex)
|
|
93
|
+
|
|
94
|
+
retries = 3
|
|
95
|
+
timeout = 60
|
|
96
|
+
|
|
97
|
+
with httpx.Client(timeout=timeout) as client:
|
|
98
|
+
for i in range(retries + 1):
|
|
99
|
+
try:
|
|
100
|
+
r = client.post(url, files=files)
|
|
101
|
+
r.raise_for_status()
|
|
102
|
+
return r.json()
|
|
103
|
+
except (
|
|
104
|
+
ConnectError,
|
|
105
|
+
RequestError,
|
|
106
|
+
TimeoutException,
|
|
107
|
+
HTTPStatusError,
|
|
108
|
+
) as e:
|
|
109
|
+
if i < retries:
|
|
110
|
+
continue
|
|
111
|
+
raise AssertionError(f"Upload failed after {retries} retries: {str(e)}") from e
|
|
112
|
+
|
|
113
|
+
# Should be unreachable if loop works correctly
|
|
114
|
+
raise AssertionError("Upload failed")
|
nectar/instance.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
|
|
3
|
+
# Track shared httpx client alongside the shared Hive instance so callers that
|
|
4
|
+
# construct Hive directly (e.g., Hive(keys=[...])) still reuse the same pool.
|
|
5
|
+
_shared_transport: Dict[str, Any] = {}
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SharedInstance:
|
|
9
|
+
"""Singleton for the shared Blockchain Instance (Hive-only)."""
|
|
10
|
+
|
|
11
|
+
instance = None
|
|
12
|
+
config = {}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def shared_blockchain_instance() -> Any:
|
|
16
|
+
"""Initialize and return the shared Hive instance.
|
|
17
|
+
|
|
18
|
+
Hive-only: this always returns a `nectar.Hive` instance, regardless of any
|
|
19
|
+
legacy configuration that may have referenced other chains.
|
|
20
|
+
"""
|
|
21
|
+
if not SharedInstance.instance:
|
|
22
|
+
clear_cache()
|
|
23
|
+
SharedInstance.instance = _build_hive(**SharedInstance.config)
|
|
24
|
+
return SharedInstance.instance
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def set_shared_blockchain_instance(blockchain_instance: Any) -> None:
|
|
28
|
+
"""
|
|
29
|
+
Override the shared Hive instance used by the module and clear related caches.
|
|
30
|
+
|
|
31
|
+
This sets SharedInstance.instance to the provided blockchain instance and calls clear_cache()
|
|
32
|
+
to invalidate any cached blockchain objects so consumers observe the new instance immediately.
|
|
33
|
+
"""
|
|
34
|
+
clear_cache()
|
|
35
|
+
SharedInstance.instance = blockchain_instance
|
|
36
|
+
if hasattr(blockchain_instance, "rpc") and getattr(blockchain_instance, "rpc", None):
|
|
37
|
+
_shared_transport["rpc"] = blockchain_instance.rpc
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def shared_hive_instance() -> Any:
|
|
41
|
+
"""Initialize (if needed) and return the shared Hive instance."""
|
|
42
|
+
return shared_blockchain_instance()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def set_shared_hive_instance(hive_instance: Any) -> None:
|
|
46
|
+
"""
|
|
47
|
+
Override the global shared Hive instance used by the module.
|
|
48
|
+
|
|
49
|
+
Replaces the current SharedInstance.instance with the provided hive_instance and clears related caches so subsequent calls return the new instance.
|
|
50
|
+
|
|
51
|
+
Parameters:
|
|
52
|
+
hive_instance: The nectar.Hive instance to set as the shared global instance.
|
|
53
|
+
"""
|
|
54
|
+
set_shared_blockchain_instance(hive_instance)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def clear_cache() -> None:
|
|
58
|
+
"""
|
|
59
|
+
Clear cached blockchain object state.
|
|
60
|
+
|
|
61
|
+
Performs a lazy import of BlockchainObject and calls its clear_cache() method to purge any in-memory caches of blockchain objects (used when the shared Hive instance or configuration changes).
|
|
62
|
+
"""
|
|
63
|
+
from .blockchainobject import BlockchainObject
|
|
64
|
+
|
|
65
|
+
BlockchainObject.clear_cache()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def set_shared_config(config: Dict[str, Any]) -> None:
|
|
69
|
+
"""
|
|
70
|
+
Set configuration for the shared Hive instance without creating the instance.
|
|
71
|
+
|
|
72
|
+
Updates the global SharedInstance.config with the provided mapping. If a shared instance already exists, clears internal caches and resets the shared instance to None so the new configuration will take effect on next access.
|
|
73
|
+
|
|
74
|
+
Parameters:
|
|
75
|
+
config (dict): Configuration options to merge into the shared instance configuration.
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
AssertionError: If `config` is not a dict.
|
|
79
|
+
"""
|
|
80
|
+
if not isinstance(config, dict):
|
|
81
|
+
raise AssertionError()
|
|
82
|
+
SharedInstance.config.update(config)
|
|
83
|
+
# if one is already set, delete
|
|
84
|
+
if SharedInstance.instance:
|
|
85
|
+
clear_cache()
|
|
86
|
+
SharedInstance.instance = None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _build_hive(**config: Any) -> Any:
|
|
90
|
+
"""Internal helper to build a Hive instance while reusing shared transports."""
|
|
91
|
+
from .hive import Hive
|
|
92
|
+
|
|
93
|
+
hive = Hive(**config)
|
|
94
|
+
stored_rpc = _shared_transport.get("rpc")
|
|
95
|
+
configured_nodes = config.get("node", [])
|
|
96
|
+
if isinstance(configured_nodes, str):
|
|
97
|
+
configured_nodes = [configured_nodes]
|
|
98
|
+
|
|
99
|
+
def _rpc_matches_config(rpc_obj: Any) -> bool:
|
|
100
|
+
if not configured_nodes or rpc_obj is None:
|
|
101
|
+
return True
|
|
102
|
+
return getattr(rpc_obj, "url", None) in configured_nodes
|
|
103
|
+
|
|
104
|
+
if stored_rpc and getattr(stored_rpc, "url", None) and _rpc_matches_config(stored_rpc):
|
|
105
|
+
hive.rpc = stored_rpc
|
|
106
|
+
else:
|
|
107
|
+
rpc_obj = getattr(hive, "rpc", None)
|
|
108
|
+
if rpc_obj is not None:
|
|
109
|
+
current_url = getattr(rpc_obj, "url", None)
|
|
110
|
+
if not current_url and hasattr(rpc_obj, "rpcconnect"):
|
|
111
|
+
rpc_obj.rpcconnect()
|
|
112
|
+
_shared_transport["rpc"] = getattr(hive, "rpc", None)
|
|
113
|
+
return hive
|