This repository has been archived by the owner on Jun 30, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 43
/
manage.py
executable file
·306 lines (242 loc) · 10.5 KB
/
manage.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
import argparse
import json
import os
import datetime
from simplecoin import create_manage_app, db, currencies, powerpools, redis_conn
from simplecoin.scheduler import SchedulerCommand
from simplecoin.models import (Transaction, UserSettings, Credit, ShareSlice,
DeviceSlice, Block, CreditExchange)
from urlparse import urlparse
from flask import current_app, _request_ctx_stack
from flask.ext.migrate import stamp
from flask.ext.script import Manager, Shell, Server
from flask.ext.migrate import MigrateCommand
manager = Manager(create_manage_app)
@manager.option('-e', '--emit', help='prints the SQL that is executed',
action="store_true")
def init_db(emit=False):
""" Resets entire database to empty state """
if emit:
import logging
logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
res = raw_input("You shouldn't probably ever do this in production! Are you"
" really, really sure you want to reset the DB {}? [y/n] "
.format(db.engine))
if res != "y":
return
else:
db.session.commit()
db.drop_all()
db.create_all()
stamp()
@manager.command
def list_donation_perc():
""" Gives a summary of number of users at each donation amount """
summ = {}
warn = False
for entry in UserSettings.query.all():
summ.setdefault(entry.pdonation_perc, 0)
summ[entry.pdonation_perc] += 1
if entry.pdonation_perc < 0:
warn = True
if warn:
print("WARNING: A user has set a donation percentage below 0!")
print "User fee summary"
print "\n".join(["{0:.2f}% donation from {1} users"
.format(k * 100, v) for k, v in sorted(summ.items())])
@manager.option("--currency", type=str, dest="currency", default=None)
@manager.option('stop_id', type=int)
@manager.option('start_id', type=int)
def del_payouts(start_id, stop_id, currency=None):
"""
Deletes payouts between start and stop id and removes their id from the
associated Credits.
Expects a start and stop payout id for payouts to be deleted
If currency is passed, only payout matching that currency will be removed
::Warning:: This can really fuck things up!
"""
from simplecoin.models import Payout
payouts = Payout.query.filter(Payout.id >= start_id,
Payout.id <= stop_id).all()
if currency is not None:
payouts = [payout for payout in payouts if payout.currency == currency]
pids = [payout.id for payout in payouts]
credits = Credit.query.filter(Credit.payout_id.in_(pids)).all()
for credit in credits:
credit.payout = None
if credit.block and credit.block.orphan:
credit.payable = False
db.session.flush()
for payout in payouts:
print "ID: {} ### USER: {} ### CREATED: {} ### AMOUNT: {} ### " \
"CREDIT_COUNT: {}".format(payout.id, payout.user,
payout.created_at, payout.amount,
payout.count)
db.session.delete(payout)
print "Preparing to delete {} Payouts.".format(len(pids))
res = raw_input("Are you really sure you want to delete these payouts? [y/n] ")
if res != "y":
db.session.rollback()
return
db.session.commit()
@manager.option("currency", type=str)
def update_trade_requests(currency):
"""
Looks at all uncompleted sell requests for a currency and
re-looks at their credits.
A Trade request's credits should always be payable - but this can
get messed up if there is a long chain of orphans that is only later
discovered (after the trade request is generated). This can happen from
a daemon being on the wrong fork for a while, and then switching to the
'official' fork.
It is important that this function be run AFTER running update_block_state
and del_payouts, which check the maturity of blocks & removes old incorrect
payouts
If any credits in the TR are discovered to now not be payable then subtract
that credit amount from the TR.
"""
from simplecoin.models import TradeRequest
trs = TradeRequest.query.filter_by(_status=0, currency=currency,
type="sell").all()
adjustment = {}
for tr in trs:
for credit in tr.credits[:]:
if credit.payable is False:
print "Found unpayable credit for {} {} on TR #{}".format(credit.amount, credit.currency, tr.id)
tr.quantity -= credit.amount
tr.credits.remove(credit)
adjustment.setdefault(tr.id, 0)
adjustment[tr.id] -= credit.amount
if adjustment:
print "Preparing to update TRs: {}.".format(adjustment)
else:
print "Nothing to update...exiting."
exit(0)
res = raw_input("Are you really sure you want to perform this update? [y/n] ")
if res != "y" and res != "yes":
db.session.rollback()
return
db.session.commit()
@manager.option('input', type=argparse.FileType('r'))
def import_shares(input):
for i, line in enumerate(input):
data = json.loads(line)
data['time'] = datetime.datetime.utcfromtimestamp(data['time'])
slc = ShareSlice(algo="scrypt", **data)
floored = DeviceSlice.floor_time(data['time'], data['span'])
if data['time'] != floored:
current_app.logger.warn("{} != {}".format(data['time'], floored))
data['time'] = floored
db.session.add(slc)
if i % 100 == 0:
print "{} completed".format(i)
db.session.commit()
@manager.option('input', type=argparse.FileType('r'))
def import_device_slices(input):
for i, row in enumerate(input):
data = json.loads(row)
data['time'] = datetime.datetime.utcfromtimestamp(data['time'])
data['stat'] = data.pop('_stat')
# Do a basic integrity check
floored = DeviceSlice.floor_time(data['time'], data['span'])
if data['time'] != floored:
current_app.logger.warn("{} != {}".format(data['time'], floored))
data['time'] = floored
db.session.add(DeviceSlice(**data))
# Print periodic progress
if i % 100 == 0:
db.session.commit()
print("{} inserted!".format(i))
@manager.command
def dump_effective_config():
import pprint
pprint.pprint(dict(current_app.config))
@manager.option('host')
def forward_coinservs(host):
""" Given a hostname, connects to a remote and tunnels all coinserver ports
to local ports. Useful for development testing. """
args = [host, "-N"]
for currency in currencies.itervalues():
if not currency.coinserv:
continue
args.append("-L {0}:127.0.0.1:{0}"
.format(currency.coinserv.config['port']))
for pp in powerpools.itervalues():
parts = urlparse(pp.monitor_address)
if parts.hostname not in ['localhost', '127.0.0.1']:
continue
args.append("-L {0}:127.0.0.1:{0}".format(parts.port))
current_app.logger.info(("/usr/bin/ssh", "/usr/bin/ssh", args))
os.execl("/usr/bin/ssh", "/usr/bin/ssh", *args)
@manager.option('-ds', '--dont-simulate', default=False,
action="store_true")
def convert_unexchangeable(dont_simulate):
""" Converts Credit exchanges for unexchangeable currencies to payout the
pool.
XXX: Now broken due to config refactor """
unexchangeable = []
for currency in currencies.itervalues():
# Skip unused currencies
if not currency.coinserv:
continue
if not currency.exchangeable:
unexchangeable.append((currency.key, currency.pool_payout))
current_app.logger.info("Looking for CreditExchange's for currencies {}"
.format(unexchangeable))
for key, pool_payout in unexchangeable:
blocks = {}
hashes = set()
for ce in (CreditExchange.query.join(CreditExchange.block, aliased=True).
filter_by(currency=key)):
blocks.setdefault(ce.block, [0, []])
hashes.add(ce.block.hash)
blocks[ce.block][0] += ce.amount
blocks[ce.block][1].append(ce)
db.session.delete(ce)
# Sanity check, make sure block objs as keys is valid
assert len(hashes) == len(blocks)
for block, (amount, credits) in blocks.iteritems():
# Create a new credit for the pool to displace the deleted
# CreditExchanges. It will always be a credit since the currency is
# unexchangeable
pool_block = Credit(
source=0,
address=pool_payout['address'],
user=pool_payout['user'],
currency=pool_payout['currency'].key,
amount=amount,
block_id=block.id,
payable=block.mature)
db.session.add(pool_block)
current_app.logger.info(
"Block {} status {} value {} removed {} CreditExchanges of {} total amount"
.format(block, block.status, block.total_value, len(credits), amount))
current_app.logger.info("For currency {}, updated {} blocks"
.format(key, len(blocks)))
if dont_simulate is True:
current_app.logger.info("Committing transaction!")
db.session.commit()
else:
current_app.logger.info("Rolling back!")
db.session.rollback()
@manager.option('-t', '--txid', dest='transaction_id')
def confirm_trans(transaction_id):
""" Manually confirms a transaction. Shouldn't be needed in normal use. """
trans = Transaction.query.filter_by(txid=transaction_id).first()
trans.confirmed = True
db.session.commit()
def make_context():
""" Setup a coinserver connection fot the shell context """
app = _request_ctx_stack.top.app
import simplecoin.models as m
return dict(app=app, currencies=currencies, powerpools=powerpools, m=m, db=db)
manager.add_command("shell", Shell(make_context=make_context))
manager.add_command("runserver", Server())
manager.add_command('db', MigrateCommand)
manager.add_command('scheduler', SchedulerCommand)
manager.add_option('-c', '--config', dest='configs', action='append',
type=argparse.FileType('r'))
manager.add_option('-l', '--log-level',
choices=['DEBUG', 'INFO', 'WARN', 'ERROR'], default='INFO')
if __name__ == "__main__":
manager.run()