hive-nectar 0.0.2__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.
- hive_nectar-0.0.2.dist-info/METADATA +182 -0
- hive_nectar-0.0.2.dist-info/RECORD +86 -0
- hive_nectar-0.0.2.dist-info/WHEEL +4 -0
- hive_nectar-0.0.2.dist-info/entry_points.txt +2 -0
- hive_nectar-0.0.2.dist-info/licenses/LICENSE.txt +23 -0
- nectar/__init__.py +32 -0
- nectar/account.py +4371 -0
- nectar/amount.py +475 -0
- nectar/asciichart.py +270 -0
- nectar/asset.py +82 -0
- nectar/block.py +446 -0
- nectar/blockchain.py +1178 -0
- nectar/blockchaininstance.py +2284 -0
- nectar/blockchainobject.py +221 -0
- nectar/blurt.py +563 -0
- nectar/cli.py +6285 -0
- nectar/comment.py +1217 -0
- nectar/community.py +513 -0
- nectar/constants.py +111 -0
- nectar/conveyor.py +309 -0
- nectar/discussions.py +1709 -0
- nectar/exceptions.py +149 -0
- nectar/hive.py +546 -0
- nectar/hivesigner.py +420 -0
- nectar/imageuploader.py +72 -0
- nectar/instance.py +129 -0
- nectar/market.py +1013 -0
- nectar/memo.py +449 -0
- nectar/message.py +357 -0
- nectar/nodelist.py +444 -0
- nectar/price.py +557 -0
- nectar/profile.py +65 -0
- nectar/rc.py +308 -0
- nectar/snapshot.py +726 -0
- nectar/steem.py +582 -0
- nectar/storage.py +53 -0
- nectar/transactionbuilder.py +622 -0
- nectar/utils.py +545 -0
- nectar/version.py +2 -0
- nectar/vote.py +557 -0
- nectar/wallet.py +472 -0
- nectar/witness.py +617 -0
- nectarapi/__init__.py +11 -0
- nectarapi/exceptions.py +123 -0
- nectarapi/graphenerpc.py +589 -0
- nectarapi/node.py +178 -0
- nectarapi/noderpc.py +229 -0
- nectarapi/rpcutils.py +97 -0
- nectarapi/version.py +2 -0
- nectarbase/__init__.py +14 -0
- nectarbase/ledgertransactions.py +75 -0
- nectarbase/memo.py +243 -0
- nectarbase/objects.py +429 -0
- nectarbase/objecttypes.py +22 -0
- nectarbase/operationids.py +102 -0
- nectarbase/operations.py +1297 -0
- nectarbase/signedtransactions.py +48 -0
- nectarbase/transactions.py +11 -0
- nectarbase/version.py +2 -0
- nectargrapheneapi/__init__.py +6 -0
- nectargraphenebase/__init__.py +27 -0
- nectargraphenebase/account.py +846 -0
- nectargraphenebase/aes.py +52 -0
- nectargraphenebase/base58.py +192 -0
- nectargraphenebase/bip32.py +494 -0
- nectargraphenebase/bip38.py +134 -0
- nectargraphenebase/chains.py +149 -0
- nectargraphenebase/dictionary.py +3 -0
- nectargraphenebase/ecdsasig.py +326 -0
- nectargraphenebase/objects.py +123 -0
- nectargraphenebase/objecttypes.py +6 -0
- nectargraphenebase/operationids.py +3 -0
- nectargraphenebase/operations.py +23 -0
- nectargraphenebase/prefix.py +11 -0
- nectargraphenebase/py23.py +38 -0
- nectargraphenebase/signedtransactions.py +201 -0
- nectargraphenebase/types.py +419 -0
- nectargraphenebase/unsignedtransactions.py +283 -0
- nectargraphenebase/version.py +2 -0
- nectarstorage/__init__.py +38 -0
- nectarstorage/base.py +306 -0
- nectarstorage/exceptions.py +16 -0
- nectarstorage/interfaces.py +237 -0
- nectarstorage/masterpassword.py +239 -0
- nectarstorage/ram.py +30 -0
- nectarstorage/sqlite.py +334 -0
nectarbase/memo.py
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import hashlib
|
|
3
|
+
from binascii import hexlify, unhexlify
|
|
4
|
+
|
|
5
|
+
from nectargraphenebase.base58 import base58decode, base58encode
|
|
6
|
+
from nectargraphenebase.py23 import py23_bytes
|
|
7
|
+
from nectargraphenebase.types import varintdecode
|
|
8
|
+
|
|
9
|
+
try:
|
|
10
|
+
from Cryptodome.Cipher import AES
|
|
11
|
+
except ImportError:
|
|
12
|
+
try:
|
|
13
|
+
from Crypto.Cipher import AES
|
|
14
|
+
except ImportError:
|
|
15
|
+
raise ImportError("Missing dependency: pyCryptodome")
|
|
16
|
+
import struct
|
|
17
|
+
|
|
18
|
+
from nectargraphenebase.account import PublicKey
|
|
19
|
+
|
|
20
|
+
from .objects import Memo
|
|
21
|
+
|
|
22
|
+
default_prefix = "STM"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_shared_secret(priv, pub):
|
|
26
|
+
"""Derive the share secret between ``priv`` and ``pub``
|
|
27
|
+
:param `Base58` priv: Private Key
|
|
28
|
+
:param `Base58` pub: Public Key
|
|
29
|
+
:return: Shared secret
|
|
30
|
+
:rtype: hex
|
|
31
|
+
The shared secret is generated such that::
|
|
32
|
+
Pub(Alice) * Priv(Bob) = Pub(Bob) * Priv(Alice)
|
|
33
|
+
"""
|
|
34
|
+
pub_point = pub.point()
|
|
35
|
+
priv_point = int(repr(priv), 16)
|
|
36
|
+
res = pub_point * priv_point
|
|
37
|
+
res_hex = "%032x" % res.x()
|
|
38
|
+
# Zero padding
|
|
39
|
+
res_hex = "0" * (64 - len(res_hex)) + res_hex
|
|
40
|
+
return res_hex
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def init_aes(shared_secret, nonce):
|
|
44
|
+
"""Initialize AES instance
|
|
45
|
+
:param hex shared_secret: Shared Secret to use as encryption key
|
|
46
|
+
:param int nonce: Random nonce
|
|
47
|
+
:return: AES instance
|
|
48
|
+
:rtype: AES
|
|
49
|
+
"""
|
|
50
|
+
" Shared Secret "
|
|
51
|
+
ss = hashlib.sha512(unhexlify(shared_secret)).digest()
|
|
52
|
+
" Seed "
|
|
53
|
+
seed = py23_bytes(str(nonce), "ascii") + hexlify(ss)
|
|
54
|
+
seed_digest = hexlify(hashlib.sha512(seed).digest()).decode("ascii")
|
|
55
|
+
" AES "
|
|
56
|
+
key = unhexlify(seed_digest[0:64])
|
|
57
|
+
iv = unhexlify(seed_digest[64:96])
|
|
58
|
+
return AES.new(key, AES.MODE_CBC, iv)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def init_aes_bts(shared_secret, nonce):
|
|
62
|
+
"""Initialize AES instance
|
|
63
|
+
:param hex shared_secret: Shared Secret to use as encryption key
|
|
64
|
+
:param int nonce: Random nonce
|
|
65
|
+
:return: AES instance
|
|
66
|
+
:rtype: AES
|
|
67
|
+
"""
|
|
68
|
+
" Shared Secret "
|
|
69
|
+
ss = hashlib.sha512(unhexlify(shared_secret)).digest()
|
|
70
|
+
" Seed "
|
|
71
|
+
seed = bytes(str(nonce), "ascii") + hexlify(ss)
|
|
72
|
+
seed_digest = hexlify(hashlib.sha512(seed).digest()).decode("ascii")
|
|
73
|
+
" AES "
|
|
74
|
+
key = unhexlify(seed_digest[0:64])
|
|
75
|
+
iv = unhexlify(seed_digest[64:96])
|
|
76
|
+
return AES.new(key, AES.MODE_CBC, iv)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def init_aes(shared_secret, nonce):
|
|
80
|
+
"""Initialize AES instance
|
|
81
|
+
:param hex shared_secret: Shared Secret to use as encryption key
|
|
82
|
+
:param int nonce: Random nonce
|
|
83
|
+
"""
|
|
84
|
+
shared_secret = hashlib.sha512(unhexlify(shared_secret)).hexdigest()
|
|
85
|
+
# Seed
|
|
86
|
+
ss = unhexlify(shared_secret)
|
|
87
|
+
n = struct.pack("<Q", int(nonce))
|
|
88
|
+
encryption_key = hashlib.sha512(n + ss).hexdigest()
|
|
89
|
+
# Check'sum'
|
|
90
|
+
check = hashlib.sha256(unhexlify(encryption_key)).digest()
|
|
91
|
+
check = struct.unpack_from("<I", check[:4])[0]
|
|
92
|
+
# AES
|
|
93
|
+
key = unhexlify(encryption_key[0:64])
|
|
94
|
+
iv = unhexlify(encryption_key[64:96])
|
|
95
|
+
return AES.new(key, AES.MODE_CBC, iv), check
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _pad(s, BS):
|
|
99
|
+
numBytes = BS - len(s) % BS
|
|
100
|
+
return s + numBytes * struct.pack("B", numBytes)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _unpad(s, BS):
|
|
104
|
+
count = s[-1]
|
|
105
|
+
if s[-count::] == count * struct.pack("B", count):
|
|
106
|
+
return s[:-count]
|
|
107
|
+
return s
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def encode_memo_bts(priv, pub, nonce, message):
|
|
111
|
+
"""Encode a message with a shared secret between Alice and Bob
|
|
112
|
+
|
|
113
|
+
:param PrivateKey priv: Private Key (of Alice)
|
|
114
|
+
:param PublicKey pub: Public Key (of Bob)
|
|
115
|
+
:param int nonce: Random nonce
|
|
116
|
+
:param str message: Memo message
|
|
117
|
+
:return: Encrypted message
|
|
118
|
+
:rtype: hex
|
|
119
|
+
|
|
120
|
+
"""
|
|
121
|
+
shared_secret = get_shared_secret(priv, pub)
|
|
122
|
+
aes = init_aes_bts(shared_secret, nonce)
|
|
123
|
+
" Checksum "
|
|
124
|
+
raw = py23_bytes(message, "utf8")
|
|
125
|
+
checksum = hashlib.sha256(raw).digest()
|
|
126
|
+
raw = checksum[0:4] + raw
|
|
127
|
+
" Padding "
|
|
128
|
+
raw = _pad(raw, 16)
|
|
129
|
+
" Encryption "
|
|
130
|
+
return hexlify(aes.encrypt(raw)).decode("ascii")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def decode_memo_bts(priv, pub, nonce, message):
|
|
134
|
+
"""Decode a message with a shared secret between Alice and Bob
|
|
135
|
+
|
|
136
|
+
:param PrivateKey priv: Private Key (of Bob)
|
|
137
|
+
:param PublicKey pub: Public Key (of Alice)
|
|
138
|
+
:param int nonce: Nonce used for Encryption
|
|
139
|
+
:param bytes message: Encrypted Memo message
|
|
140
|
+
:return: Decrypted message
|
|
141
|
+
:rtype: str
|
|
142
|
+
:raise ValueError: if message cannot be decoded as valid UTF-8
|
|
143
|
+
string
|
|
144
|
+
|
|
145
|
+
"""
|
|
146
|
+
shared_secret = get_shared_secret(priv, pub)
|
|
147
|
+
aes = init_aes_bts(shared_secret, nonce)
|
|
148
|
+
" Encryption "
|
|
149
|
+
raw = py23_bytes(message, "ascii")
|
|
150
|
+
cleartext = aes.decrypt(unhexlify(raw))
|
|
151
|
+
" Checksum "
|
|
152
|
+
checksum = cleartext[0:4]
|
|
153
|
+
message = cleartext[4:]
|
|
154
|
+
message = _unpad(message, 16)
|
|
155
|
+
" Verify checksum "
|
|
156
|
+
check = hashlib.sha256(message).digest()[0:4]
|
|
157
|
+
if check != checksum: # pragma: no cover
|
|
158
|
+
raise ValueError("checksum verification failure")
|
|
159
|
+
return message.decode("utf8")
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def encode_memo(priv, pub, nonce, message, **kwargs):
|
|
163
|
+
"""Encode a message with a shared secret between Alice and Bob
|
|
164
|
+
|
|
165
|
+
:param PrivateKey priv: Private Key (of Alice)
|
|
166
|
+
:param PublicKey pub: Public Key (of Bob)
|
|
167
|
+
:param int nonce: Random nonce
|
|
168
|
+
:param str message: Memo message
|
|
169
|
+
:return: Encrypted message
|
|
170
|
+
:rtype: hex
|
|
171
|
+
"""
|
|
172
|
+
shared_secret = get_shared_secret(priv, pub)
|
|
173
|
+
aes, check = init_aes(shared_secret, nonce)
|
|
174
|
+
" Padding "
|
|
175
|
+
raw = py23_bytes(message, "utf8")
|
|
176
|
+
raw = _pad(raw, 16)
|
|
177
|
+
" Encryption "
|
|
178
|
+
cipher = hexlify(aes.encrypt(raw)).decode("ascii")
|
|
179
|
+
prefix = kwargs.pop("prefix", default_prefix)
|
|
180
|
+
s = {
|
|
181
|
+
"from": format(priv.pubkey, prefix),
|
|
182
|
+
"to": format(pub, prefix),
|
|
183
|
+
"nonce": nonce,
|
|
184
|
+
"check": check,
|
|
185
|
+
"encrypted": cipher,
|
|
186
|
+
"prefix": prefix,
|
|
187
|
+
}
|
|
188
|
+
tx = Memo(**s)
|
|
189
|
+
return "#" + base58encode(hexlify(py23_bytes(tx)).decode("ascii"))
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def extract_memo_data(message):
|
|
193
|
+
"""Returns the stored pubkey keys, nonce, checksum and encrypted message of a memo"""
|
|
194
|
+
raw = base58decode(message[1:])
|
|
195
|
+
from_key = PublicKey(raw[:66])
|
|
196
|
+
raw = raw[66:]
|
|
197
|
+
to_key = PublicKey(raw[:66])
|
|
198
|
+
raw = raw[66:]
|
|
199
|
+
nonce = str(struct.unpack_from("<Q", unhexlify(raw[:16]))[0])
|
|
200
|
+
raw = raw[16:]
|
|
201
|
+
check = struct.unpack_from("<I", unhexlify(raw[:8]))[0]
|
|
202
|
+
raw = raw[8:]
|
|
203
|
+
cipher = raw
|
|
204
|
+
return from_key, to_key, nonce, check, cipher
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def decode_memo(priv, message):
|
|
208
|
+
"""Decode a message with a shared secret between Alice and Bob
|
|
209
|
+
|
|
210
|
+
:param PrivateKey priv: Private Key (of Bob)
|
|
211
|
+
:param base58encoded message: Encrypted Memo message
|
|
212
|
+
:return: Decrypted message
|
|
213
|
+
:rtype: str
|
|
214
|
+
:raise ValueError: if message cannot be decoded as valid UTF-8
|
|
215
|
+
string
|
|
216
|
+
"""
|
|
217
|
+
# decode structure
|
|
218
|
+
from_key, to_key, nonce, check, cipher = extract_memo_data(message)
|
|
219
|
+
|
|
220
|
+
if repr(to_key) == repr(priv.pubkey):
|
|
221
|
+
shared_secret = get_shared_secret(priv, from_key)
|
|
222
|
+
elif repr(from_key) == repr(priv.pubkey):
|
|
223
|
+
shared_secret = get_shared_secret(priv, to_key)
|
|
224
|
+
else:
|
|
225
|
+
raise ValueError("Incorrect PrivateKey")
|
|
226
|
+
|
|
227
|
+
# Init encryption
|
|
228
|
+
aes, checksum = init_aes(shared_secret, nonce)
|
|
229
|
+
# Check
|
|
230
|
+
if not check == checksum:
|
|
231
|
+
raise AssertionError("Checksum failure")
|
|
232
|
+
# Encryption
|
|
233
|
+
# remove the varint prefix (FIXME, long messages!)
|
|
234
|
+
numBytes = 16 - len(cipher) % 16
|
|
235
|
+
n = 16 - numBytes
|
|
236
|
+
message = cipher[n:]
|
|
237
|
+
message = aes.decrypt(unhexlify(py23_bytes(message, "ascii")))
|
|
238
|
+
message = _unpad(message, 16)
|
|
239
|
+
n = varintdecode(message)
|
|
240
|
+
if (len(message) - n) > 0 and (len(message) - n) < 8:
|
|
241
|
+
return "#" + message[len(message) - n :].decode("utf8")
|
|
242
|
+
else:
|
|
243
|
+
return "#" + message.decode("utf8")
|
nectarbase/objects.py
ADDED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
import decimal
|
|
3
|
+
import json
|
|
4
|
+
import struct
|
|
5
|
+
from collections import OrderedDict
|
|
6
|
+
|
|
7
|
+
from nectargraphenebase.account import PublicKey
|
|
8
|
+
from nectargraphenebase.chains import known_chains
|
|
9
|
+
from nectargraphenebase.objects import GrapheneObject, isArgsThisClass
|
|
10
|
+
from nectargraphenebase.objects import Operation as GPHOperation
|
|
11
|
+
from nectargraphenebase.py23 import py23_bytes, string_types
|
|
12
|
+
from nectargraphenebase.types import (
|
|
13
|
+
Array,
|
|
14
|
+
Bytes,
|
|
15
|
+
Id,
|
|
16
|
+
Int16,
|
|
17
|
+
Map,
|
|
18
|
+
PointInTime,
|
|
19
|
+
Static_variant,
|
|
20
|
+
String,
|
|
21
|
+
Uint16,
|
|
22
|
+
Uint32,
|
|
23
|
+
Uint64,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
from .operationids import operations
|
|
27
|
+
|
|
28
|
+
default_prefix = "STM"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def value_to_decimal(value, decimal_places):
|
|
32
|
+
decimal.getcontext().rounding = decimal.ROUND_DOWN # define rounding method
|
|
33
|
+
return decimal.Decimal(str(float(value))).quantize(
|
|
34
|
+
decimal.Decimal("1e-{}".format(decimal_places))
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class Amount(object):
|
|
39
|
+
def __init__(self, d, prefix=default_prefix, json_str=False):
|
|
40
|
+
self.json_str = json_str
|
|
41
|
+
if isinstance(d, string_types):
|
|
42
|
+
self.amount, self.symbol = d.strip().split(" ")
|
|
43
|
+
self.precision = None
|
|
44
|
+
for c in known_chains:
|
|
45
|
+
if self.precision is not None:
|
|
46
|
+
continue
|
|
47
|
+
if known_chains[c]["prefix"] != prefix:
|
|
48
|
+
continue
|
|
49
|
+
for asset in known_chains[c]["chain_assets"]:
|
|
50
|
+
if self.precision is not None:
|
|
51
|
+
continue
|
|
52
|
+
if asset["symbol"] == self.symbol:
|
|
53
|
+
self.precision = asset["precision"]
|
|
54
|
+
self.asset = asset["asset"]
|
|
55
|
+
elif asset["asset"] == self.symbol:
|
|
56
|
+
self.precision = asset["precision"]
|
|
57
|
+
self.asset = asset["asset"]
|
|
58
|
+
if self.precision is None:
|
|
59
|
+
raise Exception("Asset unknown")
|
|
60
|
+
self.amount = round(value_to_decimal(self.amount, self.precision) * 10**self.precision)
|
|
61
|
+
# Workaround to allow transfers in HIVE
|
|
62
|
+
|
|
63
|
+
self.str_repr = "{:.{}f} {}".format(
|
|
64
|
+
(float(self.amount) / 10**self.precision), self.precision, self.symbol
|
|
65
|
+
)
|
|
66
|
+
elif isinstance(d, list):
|
|
67
|
+
self.amount = d[0]
|
|
68
|
+
self.asset = d[2]
|
|
69
|
+
self.precision = d[1]
|
|
70
|
+
self.symbol = None
|
|
71
|
+
for c in known_chains:
|
|
72
|
+
if known_chains[c]["prefix"] != prefix:
|
|
73
|
+
continue
|
|
74
|
+
for asset in known_chains[c]["chain_assets"]:
|
|
75
|
+
if asset["asset"] == self.asset:
|
|
76
|
+
self.symbol = asset["symbol"]
|
|
77
|
+
if self.symbol is None:
|
|
78
|
+
raise ValueError("Unknown NAI, cannot resolve symbol")
|
|
79
|
+
a = Array([String(d[0]), d[1], d[2]])
|
|
80
|
+
self.str_repr = str(a.__str__())
|
|
81
|
+
elif isinstance(d, dict) and "nai" in d:
|
|
82
|
+
self.asset = d["nai"]
|
|
83
|
+
self.symbol = None
|
|
84
|
+
for c in known_chains:
|
|
85
|
+
if known_chains[c]["prefix"] != prefix:
|
|
86
|
+
continue
|
|
87
|
+
for asset in known_chains[c]["chain_assets"]:
|
|
88
|
+
if asset["asset"] == d["nai"]:
|
|
89
|
+
self.symbol = asset["symbol"]
|
|
90
|
+
if self.symbol is None:
|
|
91
|
+
raise ValueError("Unknown NAI, cannot resolve symbol")
|
|
92
|
+
self.amount = d["amount"]
|
|
93
|
+
self.precision = d["precision"]
|
|
94
|
+
self.str_repr = json.dumps(d)
|
|
95
|
+
else:
|
|
96
|
+
self.amount = d.amount
|
|
97
|
+
self.symbol = d.symbol
|
|
98
|
+
self.asset = d.asset["asset"]
|
|
99
|
+
self.precision = d.asset["precision"]
|
|
100
|
+
self.amount = round(value_to_decimal(self.amount, self.precision) * 10**self.precision)
|
|
101
|
+
self.str_repr = str(d)
|
|
102
|
+
# self.str_repr = json.dumps((d.json()))
|
|
103
|
+
# self.str_repr = '{:.{}f} {}'.format((float(self.amount) / 10 ** self.precision), self.precision, self.asset)
|
|
104
|
+
|
|
105
|
+
def __bytes__(self):
|
|
106
|
+
# padding
|
|
107
|
+
# Workaround to allow transfers in HIVE
|
|
108
|
+
if self.symbol == "HBD":
|
|
109
|
+
self.symbol = "SBD"
|
|
110
|
+
elif self.symbol == "HIVE":
|
|
111
|
+
self.symbol = "STEEM"
|
|
112
|
+
symbol = self.symbol + "\x00" * (7 - len(self.symbol))
|
|
113
|
+
return (
|
|
114
|
+
struct.pack("<q", int(self.amount))
|
|
115
|
+
+ struct.pack("<b", self.precision)
|
|
116
|
+
+ py23_bytes(symbol, "ascii")
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
def __str__(self):
|
|
120
|
+
if self.json_str:
|
|
121
|
+
return json.dumps(
|
|
122
|
+
{"amount": str(self.amount), "precision": self.precision, "nai": self.asset}
|
|
123
|
+
)
|
|
124
|
+
return self.str_repr
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class Operation(GPHOperation):
|
|
128
|
+
def __init__(self, *args, **kwargs):
|
|
129
|
+
self.appbase = kwargs.pop("appbase", False)
|
|
130
|
+
self.prefix = kwargs.pop("prefix", default_prefix)
|
|
131
|
+
super(Operation, self).__init__(*args, **kwargs)
|
|
132
|
+
|
|
133
|
+
def _getklass(self, name):
|
|
134
|
+
module = __import__("nectarbase.operations", fromlist=["operations"])
|
|
135
|
+
class_ = getattr(module, name)
|
|
136
|
+
return class_
|
|
137
|
+
|
|
138
|
+
def operations(self):
|
|
139
|
+
return operations
|
|
140
|
+
|
|
141
|
+
def getOperationNameForId(self, i):
|
|
142
|
+
"""Convert an operation id into the corresponding string"""
|
|
143
|
+
for key in self.operations():
|
|
144
|
+
if int(self.operations()[key]) is int(i):
|
|
145
|
+
return key
|
|
146
|
+
return "Unknown Operation ID %d" % i
|
|
147
|
+
|
|
148
|
+
def json(self):
|
|
149
|
+
return json.loads(str(self))
|
|
150
|
+
# return json.loads(str(json.dumps([self.name, self.op.toJson()])))
|
|
151
|
+
|
|
152
|
+
def __bytes__(self):
|
|
153
|
+
return py23_bytes(Id(self.opId)) + py23_bytes(self.op)
|
|
154
|
+
|
|
155
|
+
def __str__(self):
|
|
156
|
+
if self.appbase:
|
|
157
|
+
return json.dumps({"type": self.name.lower() + "_operation", "value": self.op.toJson()})
|
|
158
|
+
else:
|
|
159
|
+
return json.dumps([self.name.lower(), self.op.toJson()])
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class Memo(GrapheneObject):
|
|
163
|
+
def __init__(self, *args, **kwargs):
|
|
164
|
+
if isArgsThisClass(self, args):
|
|
165
|
+
self.data = args[0].data
|
|
166
|
+
else:
|
|
167
|
+
prefix = kwargs.pop("prefix", default_prefix)
|
|
168
|
+
if "encrypted" not in kwargs or not kwargs["encrypted"]:
|
|
169
|
+
super(Memo, self).__init__(None)
|
|
170
|
+
else:
|
|
171
|
+
if len(args) == 1 and len(kwargs) == 0:
|
|
172
|
+
kwargs = args[0]
|
|
173
|
+
if "encrypted" in kwargs and kwargs["encrypted"]:
|
|
174
|
+
super(Memo, self).__init__(
|
|
175
|
+
OrderedDict(
|
|
176
|
+
[
|
|
177
|
+
("from", PublicKey(kwargs["from"], prefix=prefix)),
|
|
178
|
+
("to", PublicKey(kwargs["to"], prefix=prefix)),
|
|
179
|
+
("nonce", Uint64(int(kwargs["nonce"]))),
|
|
180
|
+
("check", Uint32(int(kwargs["check"]))),
|
|
181
|
+
("encrypted", Bytes(kwargs["encrypted"])),
|
|
182
|
+
]
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class WitnessProps(GrapheneObject):
|
|
188
|
+
def __init__(self, *args, **kwargs):
|
|
189
|
+
if isArgsThisClass(self, args):
|
|
190
|
+
self.data = args[0].data
|
|
191
|
+
else:
|
|
192
|
+
if len(args) == 1 and len(kwargs) == 0:
|
|
193
|
+
kwargs = args[0]
|
|
194
|
+
prefix = kwargs.get("prefix", default_prefix)
|
|
195
|
+
if "sbd_interest_rate" in kwargs:
|
|
196
|
+
super(WitnessProps, self).__init__(
|
|
197
|
+
OrderedDict(
|
|
198
|
+
[
|
|
199
|
+
(
|
|
200
|
+
"account_creation_fee",
|
|
201
|
+
Amount(kwargs["account_creation_fee"], prefix=prefix),
|
|
202
|
+
),
|
|
203
|
+
("maximum_block_size", Uint32(kwargs["maximum_block_size"])),
|
|
204
|
+
("sbd_interest_rate", Uint16(kwargs["sbd_interest_rate"])),
|
|
205
|
+
]
|
|
206
|
+
)
|
|
207
|
+
)
|
|
208
|
+
elif "hbd_interest_rate" in kwargs:
|
|
209
|
+
super(WitnessProps, self).__init__(
|
|
210
|
+
OrderedDict(
|
|
211
|
+
[
|
|
212
|
+
(
|
|
213
|
+
"account_creation_fee",
|
|
214
|
+
Amount(kwargs["account_creation_fee"], prefix=prefix),
|
|
215
|
+
),
|
|
216
|
+
("maximum_block_size", Uint32(kwargs["maximum_block_size"])),
|
|
217
|
+
("hbd_interest_rate", Uint16(kwargs["hbd_interest_rate"])),
|
|
218
|
+
]
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
else:
|
|
222
|
+
super(WitnessProps, self).__init__(
|
|
223
|
+
OrderedDict(
|
|
224
|
+
[
|
|
225
|
+
(
|
|
226
|
+
"account_creation_fee",
|
|
227
|
+
Amount(kwargs["account_creation_fee"], prefix=prefix),
|
|
228
|
+
),
|
|
229
|
+
("maximum_block_size", Uint32(kwargs["maximum_block_size"])),
|
|
230
|
+
]
|
|
231
|
+
)
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class Price(GrapheneObject):
|
|
236
|
+
def __init__(self, *args, **kwargs):
|
|
237
|
+
if isArgsThisClass(self, args):
|
|
238
|
+
self.data = args[0].data
|
|
239
|
+
else:
|
|
240
|
+
if len(args) == 1 and len(kwargs) == 0:
|
|
241
|
+
kwargs = args[0]
|
|
242
|
+
prefix = kwargs.get("prefix", default_prefix)
|
|
243
|
+
super(Price, self).__init__(
|
|
244
|
+
OrderedDict(
|
|
245
|
+
[
|
|
246
|
+
("base", Amount(kwargs["base"], prefix=prefix)),
|
|
247
|
+
("quote", Amount(kwargs["quote"], prefix=prefix)),
|
|
248
|
+
]
|
|
249
|
+
)
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class Permission(GrapheneObject):
|
|
254
|
+
def __init__(self, *args, **kwargs):
|
|
255
|
+
if isArgsThisClass(self, args):
|
|
256
|
+
self.data = args[0].data
|
|
257
|
+
else:
|
|
258
|
+
prefix = kwargs.pop("prefix", default_prefix)
|
|
259
|
+
|
|
260
|
+
if len(args) == 1 and len(kwargs) == 0:
|
|
261
|
+
kwargs = args[0]
|
|
262
|
+
|
|
263
|
+
# Sort keys (FIXME: ideally, the sorting is part of Public
|
|
264
|
+
# Key and not located here)
|
|
265
|
+
kwargs["key_auths"] = sorted(
|
|
266
|
+
kwargs["key_auths"],
|
|
267
|
+
key=lambda x: repr(PublicKey(x[0], prefix=prefix)),
|
|
268
|
+
reverse=False,
|
|
269
|
+
)
|
|
270
|
+
kwargs["account_auths"] = sorted(
|
|
271
|
+
kwargs["account_auths"],
|
|
272
|
+
key=lambda x: x[0],
|
|
273
|
+
reverse=False,
|
|
274
|
+
)
|
|
275
|
+
accountAuths = Map([[String(e[0]), Uint16(e[1])] for e in kwargs["account_auths"]])
|
|
276
|
+
keyAuths = Map(
|
|
277
|
+
[[PublicKey(e[0], prefix=prefix), Uint16(e[1])] for e in kwargs["key_auths"]]
|
|
278
|
+
)
|
|
279
|
+
super(Permission, self).__init__(
|
|
280
|
+
OrderedDict(
|
|
281
|
+
[
|
|
282
|
+
("weight_threshold", Uint32(int(kwargs["weight_threshold"]))),
|
|
283
|
+
("account_auths", accountAuths),
|
|
284
|
+
("key_auths", keyAuths),
|
|
285
|
+
]
|
|
286
|
+
)
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class Extension(Array):
|
|
291
|
+
def __str__(self):
|
|
292
|
+
"""We overload the __str__ function because the json
|
|
293
|
+
representation is different for extensions
|
|
294
|
+
"""
|
|
295
|
+
return json.dumps(self.json)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class ExchangeRate(GrapheneObject):
|
|
299
|
+
def __init__(self, *args, **kwargs):
|
|
300
|
+
if isArgsThisClass(self, args):
|
|
301
|
+
self.data = args[0].data
|
|
302
|
+
else:
|
|
303
|
+
if len(args) == 1 and len(kwargs) == 0:
|
|
304
|
+
kwargs = args[0]
|
|
305
|
+
|
|
306
|
+
prefix = kwargs.get("prefix", default_prefix)
|
|
307
|
+
super(ExchangeRate, self).__init__(
|
|
308
|
+
OrderedDict(
|
|
309
|
+
[
|
|
310
|
+
("base", Amount(kwargs["base"], prefix=prefix)),
|
|
311
|
+
("quote", Amount(kwargs["quote"], prefix=prefix)),
|
|
312
|
+
]
|
|
313
|
+
)
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
class Beneficiary(GrapheneObject):
|
|
318
|
+
def __init__(self, *args, **kwargs):
|
|
319
|
+
if isArgsThisClass(self, args):
|
|
320
|
+
self.data = args[0].data
|
|
321
|
+
else:
|
|
322
|
+
if len(args) == 1 and len(kwargs) == 0:
|
|
323
|
+
kwargs = args[0]
|
|
324
|
+
super(Beneficiary, self).__init__(
|
|
325
|
+
OrderedDict(
|
|
326
|
+
[
|
|
327
|
+
("account", String(kwargs["account"])),
|
|
328
|
+
("weight", Int16(kwargs["weight"])),
|
|
329
|
+
]
|
|
330
|
+
)
|
|
331
|
+
)
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
class Beneficiaries(GrapheneObject):
|
|
335
|
+
def __init__(self, *args, **kwargs):
|
|
336
|
+
if isArgsThisClass(self, args):
|
|
337
|
+
self.data = args[0].data
|
|
338
|
+
else:
|
|
339
|
+
if len(args) == 1 and len(kwargs) == 0:
|
|
340
|
+
kwargs = args[0]
|
|
341
|
+
|
|
342
|
+
super(Beneficiaries, self).__init__(
|
|
343
|
+
OrderedDict(
|
|
344
|
+
[
|
|
345
|
+
("beneficiaries", Array([Beneficiary(o) for o in kwargs["beneficiaries"]])),
|
|
346
|
+
]
|
|
347
|
+
)
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
class CommentOptionExtensions(Static_variant):
|
|
352
|
+
"""Serialize Comment Payout Beneficiaries.
|
|
353
|
+
|
|
354
|
+
:param list beneficiaries: A static_variant containing beneficiaries.
|
|
355
|
+
|
|
356
|
+
Example::
|
|
357
|
+
|
|
358
|
+
[0,
|
|
359
|
+
{'beneficiaries': [
|
|
360
|
+
{'account': 'furion', 'weight': 10000}
|
|
361
|
+
]}
|
|
362
|
+
]
|
|
363
|
+
|
|
364
|
+
"""
|
|
365
|
+
|
|
366
|
+
def __init__(self, o):
|
|
367
|
+
if type(o) == dict and "type" in o and "value" in o:
|
|
368
|
+
if o["type"] == "comment_payout_beneficiaries":
|
|
369
|
+
type_id = 0
|
|
370
|
+
else:
|
|
371
|
+
type_id = ~0
|
|
372
|
+
data = o["value"]
|
|
373
|
+
else:
|
|
374
|
+
type_id, data = o
|
|
375
|
+
if type_id == 0:
|
|
376
|
+
data = Beneficiaries(data)
|
|
377
|
+
else:
|
|
378
|
+
raise Exception("Unknown CommentOptionExtension")
|
|
379
|
+
super(CommentOptionExtensions, self).__init__(data, type_id)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
class UpdateProposalEndDate(GrapheneObject):
|
|
383
|
+
def __init__(self, *args, **kwargs):
|
|
384
|
+
if isArgsThisClass(self, args):
|
|
385
|
+
self.data = args[0].data
|
|
386
|
+
else:
|
|
387
|
+
if len(args) == 1 and len(kwargs) == 0:
|
|
388
|
+
kwargs = args[0]
|
|
389
|
+
|
|
390
|
+
super(UpdateProposalEndDate, self).__init__(
|
|
391
|
+
OrderedDict(
|
|
392
|
+
[
|
|
393
|
+
("end_date", PointInTime(kwargs["end_date"])),
|
|
394
|
+
]
|
|
395
|
+
)
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
class UpdateProposalExtensions(Static_variant):
|
|
400
|
+
"""Serialize Update proposal extensions.
|
|
401
|
+
|
|
402
|
+
:param end_date: A static_variant containing the new end_date.
|
|
403
|
+
|
|
404
|
+
Example::
|
|
405
|
+
|
|
406
|
+
{
|
|
407
|
+
'type': '1',
|
|
408
|
+
'value':
|
|
409
|
+
{
|
|
410
|
+
'end_date': '2021-04-05T13:39:48'
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
"""
|
|
415
|
+
|
|
416
|
+
def __init__(self, o):
|
|
417
|
+
if isinstance(o, dict) and "type" in o and "value" in o:
|
|
418
|
+
if o["type"] == "update_proposal_end_date":
|
|
419
|
+
type_id = 1
|
|
420
|
+
else:
|
|
421
|
+
type_id = ~0
|
|
422
|
+
else:
|
|
423
|
+
type_id, data = o
|
|
424
|
+
|
|
425
|
+
if type_id == 1:
|
|
426
|
+
data = UpdateProposalEndDate(o["value"])
|
|
427
|
+
else:
|
|
428
|
+
raise Exception("Unknown UpdateProposalExtension")
|
|
429
|
+
super(UpdateProposalExtensions, self).__init__(data, type_id, False)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
#: Object types for object ids
|
|
3
|
+
object_type = {}
|
|
4
|
+
object_type["dynamic_global_property"] = 0
|
|
5
|
+
object_type["reserved0"] = 1
|
|
6
|
+
object_type["account"] = 2
|
|
7
|
+
object_type["witness"] = 3
|
|
8
|
+
object_type["transaction"] = 4
|
|
9
|
+
object_type["block_summary"] = 5
|
|
10
|
+
object_type["chain_property"] = 6
|
|
11
|
+
object_type["witness_schedule"] = 7
|
|
12
|
+
object_type["comment"] = 8
|
|
13
|
+
object_type["category"] = 9
|
|
14
|
+
object_type["comment_vote"] = 10
|
|
15
|
+
object_type["vote"] = 11
|
|
16
|
+
object_type["witness_vote"] = 12
|
|
17
|
+
object_type["limit_order"] = 13
|
|
18
|
+
object_type["feed_history"] = 14
|
|
19
|
+
object_type["convert_request"] = 15
|
|
20
|
+
object_type["liquidity_reward_balance"] = 16
|
|
21
|
+
object_type["operation"] = 17
|
|
22
|
+
object_type["account_history"] = 18
|