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
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import hashlib
|
|
2
|
+
import logging
|
|
3
|
+
import struct
|
|
4
|
+
from binascii import hexlify
|
|
5
|
+
from typing import Any, Callable, Optional, Union
|
|
6
|
+
|
|
7
|
+
import ecdsa
|
|
8
|
+
from cryptography.hazmat.backends import default_backend
|
|
9
|
+
from cryptography.hazmat.primitives import hashes
|
|
10
|
+
from cryptography.hazmat.primitives.asymmetric import ec
|
|
11
|
+
from cryptography.hazmat.primitives.asymmetric.utils import (
|
|
12
|
+
Prehashed,
|
|
13
|
+
decode_dss_signature,
|
|
14
|
+
encode_dss_signature,
|
|
15
|
+
)
|
|
16
|
+
from ecdsa.ellipticcurve import Point
|
|
17
|
+
from ecdsa.numbertheory import inverse_mod, square_root_mod_prime
|
|
18
|
+
from ecdsa.util import number_to_string, sigdecode_string, sigencode_string, string_to_number
|
|
19
|
+
|
|
20
|
+
from .account import PrivateKey, PublicKey
|
|
21
|
+
|
|
22
|
+
log = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _is_canonical(sig: Union[bytes, bytearray]) -> bool:
|
|
26
|
+
"""
|
|
27
|
+
Return True if a 64-byte ECDSA signature (R || S) is in canonical form.
|
|
28
|
+
|
|
29
|
+
A canonical signature here means:
|
|
30
|
+
- Neither R nor S has its highest bit set (no negative integers when interpreted as signed big-endian).
|
|
31
|
+
- Neither R nor S has unnecessary leading zero bytes (no extra 0x00 padding before a non-negative highest byte).
|
|
32
|
+
|
|
33
|
+
Parameters:
|
|
34
|
+
sig (bytes or bytearray): 64-byte concatenation of R (32 bytes) followed by S (32 bytes).
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
bool: True if signature is canonical, False otherwise.
|
|
38
|
+
"""
|
|
39
|
+
sig = bytearray(sig)
|
|
40
|
+
return (
|
|
41
|
+
not (int(sig[0]) & 0x80)
|
|
42
|
+
and not (sig[0] == 0 and not (int(sig[1]) & 0x80))
|
|
43
|
+
and not (int(sig[32]) & 0x80)
|
|
44
|
+
and not (sig[32] == 0 and not (int(sig[33]) & 0x80))
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def compressedPubkey(pk: Union[ecdsa.keys.VerifyingKey, Any]) -> bytes:
|
|
49
|
+
"""
|
|
50
|
+
Return the 33-byte compressed secp256k1 public key for the given public-key object.
|
|
51
|
+
|
|
52
|
+
Accepts either an ecdsa.keys.VerifyingKey or an object exposing public_numbers().x and .y
|
|
53
|
+
(such as a cryptography EllipticCurvePublicKey). The output is 1 byte (0x02 if y is even,
|
|
54
|
+
0x03 if y is odd) followed by the 32-byte big-endian X coordinate.
|
|
55
|
+
|
|
56
|
+
Parameters:
|
|
57
|
+
pk: Public-key object (ecdsa.VerifyingKey or object with public_numbers().x and .y).
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
bytes: 33-byte compressed public key (prefix + 32-byte X).
|
|
61
|
+
"""
|
|
62
|
+
if isinstance(pk, ecdsa.keys.VerifyingKey):
|
|
63
|
+
order = ecdsa.SECP256k1.order
|
|
64
|
+
# Get the curve point from VerifyingKey
|
|
65
|
+
point = pk.pubkey.point # type: ignore[attr-defined]
|
|
66
|
+
x = int(point.x())
|
|
67
|
+
y = int(point.y())
|
|
68
|
+
elif isinstance(pk, PublicKey):
|
|
69
|
+
# Handle account.PublicKey type
|
|
70
|
+
order = ecdsa.SECP256k1.order
|
|
71
|
+
point = pk.point()
|
|
72
|
+
x = int(point.x())
|
|
73
|
+
y = int(point.y())
|
|
74
|
+
else:
|
|
75
|
+
order = ecdsa.SECP256k1.order
|
|
76
|
+
x = int(pk.public_numbers().x)
|
|
77
|
+
y = int(pk.public_numbers().y)
|
|
78
|
+
x_str = number_to_string(x, order)
|
|
79
|
+
return bytes(chr(2 + (y & 1)), "ascii") + x_str
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def recover_public_key(
|
|
83
|
+
digest: bytes, signature: bytes, i: int, message: Optional[bytes] = None
|
|
84
|
+
) -> Union[ecdsa.keys.VerifyingKey, ec.EllipticCurvePublicKey, None]:
|
|
85
|
+
"""
|
|
86
|
+
Recover the secp256k1 public key from an ECDSA signature and message hash.
|
|
87
|
+
|
|
88
|
+
If `message` is provided the function will construct a cryptography EllipticCurvePublicKey
|
|
89
|
+
from the recovered point and verify the signature against the message; on success it
|
|
90
|
+
returns that cryptography public key. If `message` is None the function returns an
|
|
91
|
+
ecdsa.VerifyingKey built from the recovered point after verifying the signature
|
|
92
|
+
against the provided digest. If verification fails, returns None (when `message` is None)
|
|
93
|
+
or raises a verification exception (when `message` is provided).
|
|
94
|
+
|
|
95
|
+
Parameters:
|
|
96
|
+
digest (bytes): The message hash (big-endian) used when signing.
|
|
97
|
+
signature (bytes): 64-byte signature consisting of r||s (raw concatenation).
|
|
98
|
+
i (int): Recovery identifier (0..3) selecting which of the possible curve points to use.
|
|
99
|
+
message (bytes or str, optional): Original message to verify against; if a str it is
|
|
100
|
+
encoded as UTF-8. When provided the function returns a cryptography public key
|
|
101
|
+
and performs verification using ECDSA-SHA256.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
cryptography.hazmat.primitives.asymmetric.ec.EllipticCurvePublicKey
|
|
105
|
+
when `message` is provided and verification succeeds;
|
|
106
|
+
ecdsa.keys.VerifyingKey
|
|
107
|
+
when `message` is None and digest-based verification succeeds;
|
|
108
|
+
None
|
|
109
|
+
when `message` is None and verification fails.
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
cryptography.exceptions.InvalidSignature: If `message` is provided and signature verification fails.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
# See http: //www.secg.org/download/aid-780/sec1-v2.pdf section 4.1.6 primarily
|
|
116
|
+
curve = ecdsa.SECP256k1.curve
|
|
117
|
+
G = ecdsa.SECP256k1.generator
|
|
118
|
+
order = ecdsa.SECP256k1.order
|
|
119
|
+
yp = i % 2
|
|
120
|
+
r, s = sigdecode_string(signature, order)
|
|
121
|
+
# 1.1
|
|
122
|
+
x = r + (i // 2) * order
|
|
123
|
+
# 1.3. This actually calculates for either effectively 02||X or 03||X depending on 'k' instead of always for 02||X as specified.
|
|
124
|
+
# This substitutes for the lack of reversing R later on. -R actually is defined to be just flipping the y-coordinate in the elliptic curve.
|
|
125
|
+
alpha = ((x * x * x) + (curve.a() * x) + curve.b()) % curve.p()
|
|
126
|
+
beta = square_root_mod_prime(alpha, curve.p())
|
|
127
|
+
y = beta if (beta - yp) % 2 == 0 else curve.p() - beta
|
|
128
|
+
# 1.4 Constructor of Point is supposed to check if nR is at infinity.
|
|
129
|
+
R = Point(curve, x, y, order)
|
|
130
|
+
# 1.5 Compute e
|
|
131
|
+
e = string_to_number(digest)
|
|
132
|
+
# 1.6 Compute Q = r^-1(sR - eG)
|
|
133
|
+
Q = inverse_mod(r, order) * (s * R + (-e % order) * G)
|
|
134
|
+
|
|
135
|
+
if message is not None:
|
|
136
|
+
if not isinstance(message, bytes):
|
|
137
|
+
message = bytes(message, "utf-8")
|
|
138
|
+
sigder = encode_dss_signature(r, s)
|
|
139
|
+
Q_point = Q.to_affine() # type: ignore[attr-defined]
|
|
140
|
+
public_key = ec.EllipticCurvePublicNumbers(
|
|
141
|
+
int(Q_point.x()), int(Q_point.y()), ec.SECP256K1()
|
|
142
|
+
).public_key(default_backend())
|
|
143
|
+
public_key.verify(sigder, message, ec.ECDSA(hashes.SHA256()))
|
|
144
|
+
return public_key
|
|
145
|
+
else:
|
|
146
|
+
# Not strictly necessary, but let's verify the message for paranoia's sake.
|
|
147
|
+
if not ecdsa.VerifyingKey.from_public_point(Q, curve=ecdsa.SECP256k1).verify_digest(
|
|
148
|
+
signature, digest, sigdecode=sigdecode_string
|
|
149
|
+
):
|
|
150
|
+
return None
|
|
151
|
+
return ecdsa.VerifyingKey.from_public_point(Q, curve=ecdsa.SECP256k1)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def recoverPubkeyParameter(
|
|
155
|
+
message: Optional[Union[str, bytes]],
|
|
156
|
+
digest: bytes,
|
|
157
|
+
signature: bytes,
|
|
158
|
+
pubkey: Union[PublicKey, ec.EllipticCurvePublicKey],
|
|
159
|
+
) -> Optional[int]:
|
|
160
|
+
"""
|
|
161
|
+
Determine the ECDSA recovery parameter (0–3) that, when used with the given digest and 64-byte signature (R||S), reproduces the provided public key.
|
|
162
|
+
|
|
163
|
+
Attempts each recovery index i in 0..3, recovers a candidate public key, and compares its compressed form to the compressed form of the supplied pubkey. If a match is found returns the matching index; otherwise returns None.
|
|
164
|
+
|
|
165
|
+
Parameters that need clarification:
|
|
166
|
+
- message: the original message (will be encoded as UTF-8 if not bytes) and is used when recovering a cryptography public key variant.
|
|
167
|
+
- digest: the message hash used for recovery.
|
|
168
|
+
- signature: 64-byte R||S signature (bytes-like).
|
|
169
|
+
- pubkey: the expected public key to match; may be a cryptography/ec or ecdsa-like public key object.
|
|
170
|
+
|
|
171
|
+
Returns:
|
|
172
|
+
int: matching recovery parameter in 0..3, or None if no match is found.
|
|
173
|
+
"""
|
|
174
|
+
if not isinstance(message, bytes):
|
|
175
|
+
if message is None:
|
|
176
|
+
message = b""
|
|
177
|
+
else:
|
|
178
|
+
message = bytes(message, "utf-8")
|
|
179
|
+
for i in range(0, 4):
|
|
180
|
+
if not isinstance(pubkey, PublicKey):
|
|
181
|
+
p = recover_public_key(digest, signature, i, message)
|
|
182
|
+
p_comp = hexlify(compressedPubkey(p))
|
|
183
|
+
pubkey_comp = hexlify(compressedPubkey(pubkey))
|
|
184
|
+
if p_comp == pubkey_comp:
|
|
185
|
+
return i
|
|
186
|
+
else: # pragma: no cover
|
|
187
|
+
p = recover_public_key(digest, signature, i)
|
|
188
|
+
if p is None:
|
|
189
|
+
continue
|
|
190
|
+
p_comp = hexlify(compressedPubkey(p))
|
|
191
|
+
p_string = hexlify(p.to_string()) # type: ignore[attr-defined]
|
|
192
|
+
if isinstance(pubkey, PublicKey):
|
|
193
|
+
pubkey_string = bytes(repr(pubkey), "latin")
|
|
194
|
+
else: # pragma: no cover
|
|
195
|
+
pubkey_string = hexlify(pubkey.to_string()) # type: ignore[attr-defined]
|
|
196
|
+
if p_string == pubkey_string or p_comp == pubkey_string:
|
|
197
|
+
return i
|
|
198
|
+
return None
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def sign_message(message: Union[str, bytes], wif: str, hashfn: Callable = hashlib.sha256) -> bytes:
|
|
202
|
+
"""
|
|
203
|
+
Sign a message using a private key in Wallet Import Format (WIF) and return a compact, canonical ECDSA signature.
|
|
204
|
+
|
|
205
|
+
Signs the provided message with secp256k1 ECDSA-SHA256 using the private key derived from the given WIF. The function repeats signing as needed until it produces a canonical 64-byte R||S signature (both R and S encoded as 32 bytes). It also computes the recovery parameter for the signature and encodes it into the first byte of the returned blob.
|
|
206
|
+
|
|
207
|
+
Parameters:
|
|
208
|
+
message (bytes or str): Message to sign. If a str is provided it is encoded as UTF-8 before hashing.
|
|
209
|
+
wif (str): Private key in Wallet Import Format (WIF).
|
|
210
|
+
hashfn (callable, optional): Hash function to apply to the message prior to recovery-parameter computation; defaults to hashlib.sha256.
|
|
211
|
+
|
|
212
|
+
Returns:
|
|
213
|
+
bytes: 65-byte compact signature: 1-byte recovery/version prefix (recovery parameter adjusted for compact/compressed form) followed by the 64-byte R||S sequence.
|
|
214
|
+
"""
|
|
215
|
+
|
|
216
|
+
if not isinstance(message, bytes):
|
|
217
|
+
message = bytes(message, "utf-8")
|
|
218
|
+
|
|
219
|
+
# Detect if message is already a digest
|
|
220
|
+
prehashed = len(message) == hashfn().digest_size
|
|
221
|
+
|
|
222
|
+
if prehashed:
|
|
223
|
+
digest = message
|
|
224
|
+
message_for_signing = message # the digest
|
|
225
|
+
algorithm_for_signing = ec.ECDSA(Prehashed(hashes.SHA256()))
|
|
226
|
+
message_for_recovery = None
|
|
227
|
+
else:
|
|
228
|
+
digest = hashfn(message).digest()
|
|
229
|
+
message_for_signing = message
|
|
230
|
+
algorithm_for_signing = ec.ECDSA(hashes.SHA256())
|
|
231
|
+
message_for_recovery = message
|
|
232
|
+
|
|
233
|
+
priv_key = PrivateKey(wif)
|
|
234
|
+
cnt = 0
|
|
235
|
+
private_key = ec.derive_private_key(int(repr(priv_key), 16), ec.SECP256K1(), default_backend())
|
|
236
|
+
public_key = private_key.public_key()
|
|
237
|
+
while True:
|
|
238
|
+
cnt += 1
|
|
239
|
+
if not cnt % 20:
|
|
240
|
+
log.info("Still searching for a canonical signature. Tried %d times already!" % cnt)
|
|
241
|
+
order = ecdsa.SECP256k1.order
|
|
242
|
+
sigder = private_key.sign(message_for_signing, algorithm_for_signing)
|
|
243
|
+
r, s = decode_dss_signature(sigder)
|
|
244
|
+
signature = sigencode_string(r, s, order)
|
|
245
|
+
# Make sure signature is canonical!
|
|
246
|
+
#
|
|
247
|
+
sigder = bytearray(sigder)
|
|
248
|
+
lenR = sigder[3]
|
|
249
|
+
lenS = sigder[5 + lenR]
|
|
250
|
+
if lenR == 32 and lenS == 32 and _is_canonical(signature):
|
|
251
|
+
# Derive the recovery parameter
|
|
252
|
+
#
|
|
253
|
+
i = recoverPubkeyParameter(message_for_recovery, digest, signature, public_key)
|
|
254
|
+
if i is None:
|
|
255
|
+
continue
|
|
256
|
+
i += 4 # compressed
|
|
257
|
+
i += 27 # compact
|
|
258
|
+
break
|
|
259
|
+
|
|
260
|
+
# pack signature
|
|
261
|
+
#
|
|
262
|
+
sigstr = struct.pack("<B", i)
|
|
263
|
+
sigstr += signature
|
|
264
|
+
|
|
265
|
+
return sigstr
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def verify_message(
|
|
269
|
+
message: Union[str, bytes],
|
|
270
|
+
signature: Union[str, bytes],
|
|
271
|
+
hashfn: Callable = hashlib.sha256,
|
|
272
|
+
recover_parameter: Optional[int] = None,
|
|
273
|
+
) -> Optional[bytes]:
|
|
274
|
+
"""
|
|
275
|
+
Verify an ECDSA secp256k1 signature against a message and return the signer's compressed public key.
|
|
276
|
+
|
|
277
|
+
Parameters:
|
|
278
|
+
message (bytes or str): The message to verify. If a str, it will be UTF-8 encoded.
|
|
279
|
+
signature (bytes or str): 65-byte compact signature where the first byte encodes the recovery parameter/version and the remaining 64 bytes are R||S. If a str, it will be UTF-8 encoded.
|
|
280
|
+
hashfn (callable): Hash function constructor used to compute the digest of the message (default: hashlib.sha256). Note: The actual verification uses SHA256 regardless of this parameter.
|
|
281
|
+
recover_parameter (int, optional): Explicit recovery parameter (0–3). If omitted, it is extracted from the signature's first byte.
|
|
282
|
+
|
|
283
|
+
Returns:
|
|
284
|
+
bytes: The 33-byte compressed public key of the recovered signer on successful verification.
|
|
285
|
+
|
|
286
|
+
Notes:
|
|
287
|
+
- The function computes the digest of `message` with `hashfn`, recovers the public key using the recovery parameter, converts the 64-byte R||S into DER form, and verifies the signature with ECDSA-SHA256.
|
|
288
|
+
- If the recovery parameter cannot be determined from the signature, None is returned.
|
|
289
|
+
- Cryptographic verification errors (e.g., invalid signature) will propagate as raised exceptions.
|
|
290
|
+
"""
|
|
291
|
+
if not isinstance(message, bytes):
|
|
292
|
+
message = bytes(message, "utf-8")
|
|
293
|
+
if not isinstance(signature, bytes):
|
|
294
|
+
signature = bytes(signature, "utf-8")
|
|
295
|
+
digest = hashfn(message).digest()
|
|
296
|
+
sig = signature[1:]
|
|
297
|
+
if recover_parameter is None:
|
|
298
|
+
recover_parameter = bytearray(signature)[0] - 4 - 27 # recover parameter only
|
|
299
|
+
if recover_parameter < 0:
|
|
300
|
+
log.info("Could not recover parameter")
|
|
301
|
+
return None
|
|
302
|
+
|
|
303
|
+
p = recover_public_key(digest, sig, recover_parameter, message)
|
|
304
|
+
order = ecdsa.SECP256k1.order
|
|
305
|
+
r, s = sigdecode_string(sig, order)
|
|
306
|
+
sigder = encode_dss_signature(r, s)
|
|
307
|
+
p.verify(sigder, digest, ec.ECDSA(Prehashed(hashes.SHA256()))) # type: ignore[attr-defined]
|
|
308
|
+
phex = compressedPubkey(p)
|
|
309
|
+
return phex
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any, Dict, List, Union
|
|
3
|
+
|
|
4
|
+
from nectargraphenebase.types import Id, JsonObj, Optional, String
|
|
5
|
+
|
|
6
|
+
from .operationids import operations
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Operation:
|
|
10
|
+
def __init__(self, op: Union[List[Any], Dict[str, Any], Any]) -> None:
|
|
11
|
+
if isinstance(op, list) and len(op) == 2:
|
|
12
|
+
if isinstance(op[0], int):
|
|
13
|
+
self.opId = op[0]
|
|
14
|
+
name = self.getOperationNameForId(self.opId)
|
|
15
|
+
else:
|
|
16
|
+
self.opId = self.operations().get(op[0], None)
|
|
17
|
+
name = op[0]
|
|
18
|
+
if self.opId is None:
|
|
19
|
+
raise ValueError("Unknown operation")
|
|
20
|
+
self.name = name[0].upper() + name[1:] # klassname
|
|
21
|
+
try:
|
|
22
|
+
klass = self._getklass(self.name)
|
|
23
|
+
except Exception:
|
|
24
|
+
raise NotImplementedError("Unimplemented Operation %s" % self.name)
|
|
25
|
+
self.op = klass(op[1])
|
|
26
|
+
self.appbase = False
|
|
27
|
+
elif isinstance(op, dict):
|
|
28
|
+
if len(op["type"]) > 10 and op["type"][-9:] == "operation":
|
|
29
|
+
name = op["type"][:-10]
|
|
30
|
+
else:
|
|
31
|
+
name = op["type"]
|
|
32
|
+
self.opId = self.operations().get(name, None)
|
|
33
|
+
if self.opId is None:
|
|
34
|
+
raise ValueError("Unknown operation")
|
|
35
|
+
self.name = name[0].upper() + name[1:] # klassname
|
|
36
|
+
try:
|
|
37
|
+
klass = self._getklass(self.name)
|
|
38
|
+
except Exception:
|
|
39
|
+
raise NotImplementedError("Unimplemented Operation %s" % self.name)
|
|
40
|
+
self.op = klass(op["value"])
|
|
41
|
+
self.appbase = True
|
|
42
|
+
else:
|
|
43
|
+
self.op = op
|
|
44
|
+
self.name = type(self.op).__name__.lower() # also store name
|
|
45
|
+
self.opId = self.operations()[self.name]
|
|
46
|
+
|
|
47
|
+
def operations(self) -> Dict[str, int]:
|
|
48
|
+
return operations
|
|
49
|
+
|
|
50
|
+
def getOperationNameForId(self, i: int) -> str:
|
|
51
|
+
"""Convert an operation id into the corresponding string"""
|
|
52
|
+
for key in self.operations():
|
|
53
|
+
if int(self.operations()[key]) is int(i):
|
|
54
|
+
return key
|
|
55
|
+
return "Unknown Operation ID %d" % i
|
|
56
|
+
|
|
57
|
+
def _getklass(self, name: str) -> type:
|
|
58
|
+
module = __import__("graphenebase.operations", fromlist=["operations"])
|
|
59
|
+
class_ = getattr(module, name)
|
|
60
|
+
return class_
|
|
61
|
+
|
|
62
|
+
def __bytes__(self) -> bytes:
|
|
63
|
+
if self.opId is None:
|
|
64
|
+
raise ValueError("Operation ID is None, cannot serialize operation")
|
|
65
|
+
return bytes(Id(self.opId)) + bytes(self.op)
|
|
66
|
+
|
|
67
|
+
def __str__(self) -> str:
|
|
68
|
+
# Try to get JSON data from operation, fallback to raw object if toJson not available
|
|
69
|
+
# This handles both GrapheneObject instances (with toJson) and raw data
|
|
70
|
+
try:
|
|
71
|
+
op_data = self.op.toJson() # type: ignore[attr-defined]
|
|
72
|
+
except (AttributeError, TypeError):
|
|
73
|
+
op_data = self.op
|
|
74
|
+
return json.dumps([self.opId, op_data])
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class GrapheneObject:
|
|
78
|
+
"""Core abstraction class
|
|
79
|
+
|
|
80
|
+
This class is used for any JSON reflected object in Graphene.
|
|
81
|
+
|
|
82
|
+
* ``instance.__json__()``: encodes data into json format
|
|
83
|
+
* ``bytes(instance)``: encodes data into wire format
|
|
84
|
+
* ``str(instances)``: dumps json object as string
|
|
85
|
+
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def __init__(self, data: Any = None) -> None:
|
|
89
|
+
self.data = data
|
|
90
|
+
|
|
91
|
+
def __bytes__(self) -> bytes:
|
|
92
|
+
if self.data is None:
|
|
93
|
+
return b""
|
|
94
|
+
b = b""
|
|
95
|
+
for name, value in list(self.data.items()):
|
|
96
|
+
if isinstance(value, str):
|
|
97
|
+
b += bytes(value, "utf-8")
|
|
98
|
+
else:
|
|
99
|
+
b += bytes(value)
|
|
100
|
+
return b
|
|
101
|
+
|
|
102
|
+
def __json__(self) -> Dict[str, Any]:
|
|
103
|
+
if self.data is None:
|
|
104
|
+
return {}
|
|
105
|
+
d = {} # JSON output is *not* ordered
|
|
106
|
+
for name, value in list(self.data.items()):
|
|
107
|
+
if isinstance(value, Optional) and value.isempty():
|
|
108
|
+
continue
|
|
109
|
+
|
|
110
|
+
if isinstance(value, String):
|
|
111
|
+
d.update({name: str(value)})
|
|
112
|
+
else:
|
|
113
|
+
try:
|
|
114
|
+
d.update({name: JsonObj(value)})
|
|
115
|
+
except Exception:
|
|
116
|
+
d.update({name: value.__str__()})
|
|
117
|
+
return d
|
|
118
|
+
|
|
119
|
+
def __str__(self) -> str:
|
|
120
|
+
return json.dumps(self.__json__())
|
|
121
|
+
|
|
122
|
+
def toJson(self) -> Dict[str, Any]:
|
|
123
|
+
return self.__json__()
|
|
124
|
+
|
|
125
|
+
def json(self) -> Dict[str, Any]:
|
|
126
|
+
return self.__json__()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def isArgsThisClass(self: Any, args: tuple) -> bool:
|
|
130
|
+
return len(args) == 1 and type(args[0]).__name__ == type(self).__name__
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from collections import OrderedDict
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
from .objects import GrapheneObject, isArgsThisClass
|
|
5
|
+
from .types import (
|
|
6
|
+
Set,
|
|
7
|
+
String,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Demooepration(GrapheneObject):
|
|
12
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
13
|
+
if isArgsThisClass(self, args):
|
|
14
|
+
self.data = args[0].data
|
|
15
|
+
else:
|
|
16
|
+
if len(args) == 1 and len(kwargs) == 0:
|
|
17
|
+
kwargs = args[0]
|
|
18
|
+
super().__init__(
|
|
19
|
+
OrderedDict(
|
|
20
|
+
[
|
|
21
|
+
("string", String(kwargs["string"])),
|
|
22
|
+
("extensions", Set([])),
|
|
23
|
+
]
|
|
24
|
+
)
|
|
25
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Prefix:
|
|
5
|
+
"""This class is meant to allow changing the prefix.
|
|
6
|
+
The prefix is used to link a public key to a specific blockchain.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
prefix: str = "STM"
|
|
10
|
+
|
|
11
|
+
def set_prefix(self, prefix: Optional[str]) -> None:
|
|
12
|
+
if prefix:
|
|
13
|
+
self.prefix = prefix
|
|
File without changes
|