-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwriter.py
338 lines (287 loc) · 10.6 KB
/
writer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
import argparse
import os
import requests
import sys
import time
import urllib.parse
from typing import TYPE_CHECKING, Any, NamedTuple, Optional
from inspect import currentframe, getframeinfo
BASE_URL = 'http://localhost:PORT/'
WALLET_ID = '_id'
HEADERS = { 'X-Wallet-Id': WALLET_ID }
NETWORK = 'mainnet'
FIRST_ADDRESS = None
class Token(NamedTuple):
name: str
symbol: str
uid: str
# TODO add docstring to all methods
# TODO add types to all methods
# TODO proper logging: https://docs.python.org/3/howto/logging.html
# TODO check HTR balance
def print_debug(*args, **kwargs) -> None:
if DEBUG: print(*args, **kwargs)
def get_wallet_endpoint(command: str) -> str:
endpoints = {
'start': '/start',
'status': '/wallet/status',
'balance': '/wallet/balance',
'addresses': '/wallet/addresses',
'send': '/wallet/send-tx',
'create-nft': '/wallet/create-nft'
}
return urllib.parse.urljoin(BASE_URL, endpoints[command])
def get_wallet_service_endpoint(address: str) -> str:
#https://explorer-service.mainnet.hathor.network/address/tokens?address=HRkMF9CuwdCvqat8voBcb3a2bjRiRfRPBX&limit=50
#https://explorer-service.testnet.hathor.network/address/tokens?address=HRkMF9CuwdCvqat8voBcb3a2bjRiRfRPBX&limit=50
return 'https://explorer-service.{}.hathor.network/address/tokens?address={}&limit=50'.format(NETWORK, address)
def check_wallet_status() -> str:
try:
r = requests.get(get_wallet_endpoint('status'), headers = HEADERS)
if r.status_code != 200:
sys.exit(-1)
resp = r.json()
# If the wallet is not started, the 'success' key will be set to False.
# If it's started, there's will be no 'success' key and we can get the
# 'statusMessage' key
print_debug('check_wallet_status', resp)
if 'success' in resp and not resp['success']:
# wallet not started
print_debug('check_wallet_status not started')
return 'Not started'
elif 'statusMessage' in resp:
global NETWORK
NETWORK = resp['network']
print_debug('check_wallet_status started', resp)
return resp['statusMessage']
else:
print('Unknown wallet status, exiting')
sys.exit(-1)
except Exception as err:
frameinfo = getframeinfo(currentframe())
print('ERROR', frameinfo.lineno, err)
print('Is the wallet headless running?')
sys.exit(-1)
def is_wallet_ready() -> bool:
return check_wallet_status() == 'Ready'
def _get_addresses() -> list[str]:
try:
r = requests.get(get_wallet_endpoint('addresses'), headers = HEADERS)
if r.status_code != 200:
sys.exit(-1)
resp = r.json()
return resp['addresses']
except Exception as err:
frameinfo = getframeinfo(currentframe())
print('ERROR', frameinfo.lineno, err)
print('Is the wallet headless running?')
sys.exit(-1)
def _get_tokens(address: str) -> list[str]:
print_debug('get tokens for address', address)
#print('endpoint', get_wallet_service_endpoint(address))
try:
r = requests.get(get_wallet_service_endpoint(address))
if r.status_code != 200:
sys.exit(-1)
# Expected reply
# {
# "total": 1,
# "tokens": {
# "00001316b675b667970b8dfce5a8af0999771d11101d97f7b929250be64e62f8": {
# "token_id": "00001316b675b667970b8dfce5a8af0999771d11101d97f7b929250be64e62f8",
# "name": "EasyToken24102555A1",
# "symbol": "EASY"
# }
# }
# }
resp = r.json()
return resp['tokens']
except Exception as err:
frameinfo = getframeinfo(currentframe())
print('ERROR', frameinfo.lineno, err)
print('Is the wallet headless running?')
sys.exit(-1)
def get_tokens():
tokens = {}
addresses = _get_addresses()
# TODO to speed up
#addresses = _get_addresses()[:3]
global FIRST_ADDRESS
FIRST_ADDRESS = addresses[0]
for address in addresses:
# merge the dicts
tokens |= _get_tokens(address)
# sleep so we don't hit the API rate limit, as we are fetching from remote endpoint
time.sleep(0.5)
# use Token namedtuple
for uid, token in tokens.items():
t = Token(token['name'], token['symbol'], uid)
tokens[uid] = t
# we don't save HTR token
tokens.pop('00', None)
return list(tokens.values())
def _print_tokens(tokens):
count = 0
print('Symbol, name, uid')
for t in tokens:
count += 1
print('{}: {}, {}, {}'.format(count, t.symbol, t.name, t.uid))
def _store_info(info: str, token_uid: str, address: str) -> bool:
payload = { 'outputs': [
{ 'type': 'data', 'data': info },
{ 'address': address, 'value': 1, 'token': token_uid }
]
}
print_debug('_store_info payload', payload)
try:
r = requests.post(get_wallet_endpoint('send'), headers = HEADERS, json = payload)
if r.status_code != 200:
print_debug('_store_info exit', r.status_code, r.text)
sys.exit(-1)
resp = r.json()
if not resp['success']:
print('Error sending transaction', resp)
return False
else:
print('Successfully stored, tx hash', resp['hash'])
return True
except Exception as err:
frameinfo = getframeinfo(currentframe())
print('ERROR', frameinfo.lineno, err)
print('Is the wallet headless running?')
sys.exit(-1)
def add_new_entry(tokens):
_print_tokens(tokens)
choice = int(input('Add to which token? '))
token = tokens[choice - 1]
# TODO it would be nice to show the last N entries from this tokens
info = input('What information shall be stored? ')
print('Storing on token {} ({}) the following information: {}'.format(token.name, token.symbol, info))
confirm = input("Confirm [y/n]?")
if confirm == 'y':
_store_info(info, token.uid, FIRST_ADDRESS)
elif confirm == 'n':
print('Operation aborted')
else:
print('Unknown path')
def _create_token(name: str, symbol:str, info: str):
payload = { 'name': name, 'symbol': symbol, 'amount': 1, 'data': info, 'address': FIRST_ADDRESS , 'change_address': FIRST_ADDRESS }
print_debug('_create_token payload', payload)
try:
r = requests.post(get_wallet_endpoint('create-nft'), headers = HEADERS, json = payload)
if r.status_code != 200:
print_debug('_create_token exit', r.status_code, r.text)
sys.exit(-1)
resp = r.json()
if not resp['success']:
print('Error creating token', resp)
return None
else:
uid = resp['hash']
print('Successfully created token', uid)
return Token(name, symbol, uid)
except Exception as err:
frameinfo = getframeinfo(currentframe())
print('ERROR', frameinfo.lineno, err)
print('Is the wallet headless running?')
sys.exit(-1)
def create_token():
symbol = input('What\'s the token symbol? ')
name = input('What\'s the token name? ')
info = input('What\'s the initial information to be stored? ')
print('Name:', name)
print('Symbol:', symbol)
print('Information:', info)
confirm = input("Confirm [y/n]?")
if confirm == 'y':
return _create_token(name, symbol, info)
elif confirm == 'n':
print('Operation aborted')
else:
print('Unknown path')
def start_wallet(seed: str) -> None:
print('Starting wallet at {} with seed {}'.format(BASE_URL, seed))
payload = {'wallet-id': WALLET_ID, 'seedKey': seed}
try:
r = requests.post(get_wallet_endpoint('start'), data = payload)
if r.status_code != 200:
sys.exit(-1)
resp = r.json()
if resp['success']:
print('Wallet started')
time.sleep(2)
elif resp['errorCode'] == 'WALLET_ALREADY_STARTED':
print('Wallet already started')
else:
print('Unhandled error starting wallet', resp['errorCode'])
sys.exit(-1)
# wait for wallet to load
ready = False
while not ready:
status = check_wallet_status()
if status == 'Ready':
print('Wallet ready')
ready = True
elif status == 'Error':
print('Error loading wallet')
sys.exit(-1)
else:
print('Wallet not ready. Waiting...')
time.sleep(2)
except Exception as err:
frameinfo = getframeinfo(currentframe())
print('ERROR', frameinfo.lineno, err)
print('Is the wallet headless running?')
sys.exit(-1)
def main(port: str, seed: str, debug: bool):
tokens = []
global BASE_URL, DEBUG
DEBUG = debug
BASE_URL = BASE_URL.replace('PORT', port)
# TODO check if wallet is started
# set network and first address
while True:
print('\nWhat would you like to do?')
print('1. Add new entry to existing tokens')
print('2. Check existing tokens')
print('3. Create new token')
print('4. Start wallet')
print('5. Check wallet status')
print('6. Exit')
choice = int(input("Choose option: "))
if choice == 1:
if not is_wallet_ready():
print('Wallet not ready\n')
continue
if not tokens:
print('Fetching tokens...')
tokens = get_tokens()
add_new_entry(tokens)
elif choice == 2:
if not is_wallet_ready():
print('Wallet not ready\n')
continue
if not tokens:
print('Fetching tokens...')
tokens = get_tokens()
_print_tokens(tokens)
elif choice == 3:
token = create_token()
if token: tokens.append(token)
pass
elif choice == 4:
start_wallet(seed)
elif choice == 5:
print('Wallet status:', check_wallet_status())
elif choice == 6:
print('See you soon!')
break
else:
print('Invalid option\n')
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('--port', nargs='?', default='8000')
parser.add_argument('--seed', nargs='?', default='default')
parser.add_argument('--debug', default=False, action='store_true')
args = parser.parse_args()
main(args.port, args.seed, args.debug)