def do_batch(dat): """ Make a batch of transfers :param dat: :return: """ import config snap_info = pickle.loads(Redisdb.get("snapshot_info")) try: Bitshares = config.BitShares(node=config.WSS_NODE, wif=dat['form']['key']) oaccount = Bitshares.wallet.getAccounts()[0] except Exception as err: print(err.__repr__()) Redisdb.rpush("messages", "*|launch_error|key not found") print(0) return False for job in dat['job']: try: wif = dat['key'] pub = format(account.PrivateKey(wif).pubkey, config.PREFIX) to_account_id = job[0].decode('utf8') amount = 10 asset_id = dat['asset']['id'] message = "abcdefgABCDEFG0123456789" nonce = "5862723643998573708" fee = objects.Asset(amount=0, asset_id="1.3.0") amount = objects.Asset(amount=int(amount), asset_id=asset_id) encrypted_memo = memo.encode_memo( account.PrivateKey(wif), account.PublicKey(pub, prefix=config.PREFIX), nonce, message) memoStruct = { "from": pub, "to": pub, "nonce": nonce, "message": encrypted_memo, } memoObj = objects.Memo(**memoStruct) tmp = operations.Transfer( **{ "fee": fee, "from": dat['from'], "to": to_account_id, "amount": amount, "memo": memoObj, "prefix": config.PREFIX }) print() #test = Bitshares.transfer(job[0].decode('utf8'), float(job[1]), dat['asset'], "", dat['from']) except Exception as err: print(err.__repr__()) print(0) Redisdb.decr("batch_jobs", int(1)) Redisdb.rpush( "messages", datetime.datetime.now().isoformat() + " Jobs queue: {}".format(int(Redisdb.get("batch_jobs"))))
def do_batch(dat): """ Make a batch of transfers :param dat: :return: """ import config snap_info = pickle.loads(Redisdb.get("snapshot_info")) # TODO: obtener la cuenta a partir de la clave pública # TODO: obtener balance de la moneda a repartir y de la moneda del fee # try: Bitshares = config.BitShares(node=config.WSS_NODE, wif=dat['key']) except Exception as err: print(err.__repr__()) Redisdb.rpush("messages", "*|launch_error|key not found") print(0) return False for job in dat['job']: try: test = Bitshares.transfer(job[0].decode('utf8'), float(job[1]), dat['asset'], "", "1.2.4411") except Exception as err: print(err.__repr__()) print(0) Redisdb.decr("batch_jobs", int(1)) Redisdb.rpush( "messages", datetime.datetime.now().isoformat() + " Jobs queue: {}".format(int(Redisdb.get("batch_jobs"))))
def snapshot_end(): # persist db to disk Redisdb.rpush("operations", pickle.dumps({'operation': 'db_save'})) Redisdb.rpush("messages", datetime.datetime.now().isoformat() + " Snapshot end") Redisdb.rpush("messages", "--------------") Redisdb.rpush("operations", pickle.dumps({'operation': 'csv_generation'}))
def snapshot(data): """ Signals the start of snapshot :param data: :return: """ Redisdb.rpush("messages", "---------------") msg = datetime.datetime.now().isoformat() + " Snapshot Starts" Redisdb.rpush("messages", msg) data['operation'] = 'launch_snapshot' Redisdb.rpush("operations", pickle.dumps(data))
def launch(data): """ Signals start distribution :param data: :return: """ Redisdb.rpush("messages", "---------------") Redisdb.rpush("messages", datetime.datetime.now().isoformat() + " Distribution Starts") data['operation'] = 'launch_distribution' Redisdb.rpush("operations", pickle.dumps(data))
def csv_gen(): """ Generate CSV files for download TOP 100 holders TAIL 100 holders :return: """ import csv from distribution import distribution_setup Redisdb.rpush( "messages", datetime.datetime.now().isoformat() + " CSV generate list...") lst = distribution_setup() # top 1000 to csv lst = lst[:1000] snap_info = pickle.loads(Redisdb.get("snapshot_info")) # write to file fname = 'distribution_{}_{}.csv'.format( snap_info['asset_hold_id'], datetime.datetime.now().isoformat()) with open('./web/tmp/' + fname, 'w', newline='') as fcsv: cw = csv.writer(fcsv, delimiter=';', quotechar='"', quoting=csv.QUOTE_NONNUMERIC) cw.writerow([ "Account", "Date", "Amount of " + snap_info['asset_hold']['symbol'], "Will take of " + snap_info['asset_distribute']['symbol'] ]) for l in lst: cw.writerow(l) msg = "|".join([datetime.datetime.now().isoformat(), './tmp/' + fname]) Redisdb.rpush("messages", "*|csv_exported|" + msg) msg = datetime.datetime.now().isoformat() + "|" + \ "Account;Date;Amount of " + snap_info['asset_hold']['symbol'] + ";Will take of " + snap_info['asset_distribute']['symbol'] + "|" msg2 = "" for l in lst[:10]: for f in l: msg2 += f + ";" msg2 += "#" Redisdb.rpush("messages", "*|csv_data|" + msg + msg2) print(0)
def do_snapshot(dat): if 'form' not in dat: return False if dat['form']['asset_id'] is None: Redisdb.rpush("messages", " Missing distribution asset") return False if dat['form']['assethold_id'] is None: Redisdb.rpush("messages", " Missing holding asset") return False asset_id = dat['form']['assethold_id'] Redisdb.delete("balance:" + asset_id) Redisdb.delete("distribute:" + asset_id) Redisdb.set("total_balance:" + asset_id, 0) Redisdb.set("total_owners:" + asset_id, 0) if asset_id is None or Redisdb.get("snapshot_started") is not None: return False snap_info = {} snap_info['total_balance'] = 0 snap_info['total_owners'] = 0 snap_info['asset_hold_id'] = asset_id snap_info['asset_distribute_id'] = dat['form']['asset_id'] snap_info['asset_hold'] = Bitshares.rpc.get_assets([asset_id])[0] snap_info['asset_hold_minimum'] = float(dat['form']['hold_minimum']) snap_info['asset_distribute'] = Bitshares.rpc.get_assets( [dat['form']['asset_id']])[0] snap_info['distribution_amount'] = int(dat['form']['amount']) snap_info['distribution_ratio'] = float(dat['form']['ratio']) snap_info['distribution_minimum'] = float(dat['form']['minimum']) snap_info['transfer_fee'] = float(dat['form']['transfer_fee']) # query fees fees = Bitshares.rpc.get_global_properties()['parameters']['current_fees'] Redisdb.set("snapshot_info", pickle.dumps(snap_info)) Redisdb.rpush("messages", "*|snapshot_start") obtain_balances(asset_id) Redisdb.rpush("operations", pickle.dumps({'operation': 'snapshot_end'}))
def end_check(): """ Check for the finalization of snapshot tasks :param dat: :param q: :return: """ while True: print("snap_end", Redisdb.get("batch_jobs")) if int(Redisdb.get("batch_jobs")) == 0: # persist db to disk Redisdb.rpush("operations", pickle.dumps({'operation': 'db_save'})) Redisdb.rpush( "messages", datetime.datetime.now().isoformat() + " Distribution end") Redisdb.rpush("messages", "--------------") break sleep(2)
def distribution_setup(): """ Makes a preview of the distribution :return: """ snap_info = pickle.loads(Redisdb.get("snapshot_info")) holders = Redisdb.hgetall("balance:" + snap_info['asset_hold_id']) lst = [] cnt = 0 balance = 0 for h in holders: hdata = pickle.loads(holders[h]) lst.append([hdata['account'], hdata['datetime'], hdata['amount'], 0]) balance += hdata['amount'] cnt += 1 if cnt % 1000 == 0: Redisdb.rpush( "messages", datetime.datetime.now().isoformat() + " CSV list " + str(cnt)) # refresh snapshot info snap_info['total_balance'] = balance snap_info['total_owners'] = int( Redisdb.get("total_owners:" + snap_info['asset_hold_id'])) Redisdb.set("snapshot_info", pickle.dumps(snap_info)) msg = "|".join([datetime.datetime.now().isoformat(), str(int(snap_info['total_balance'])), \ str(int(snap_info['total_owners'])), str(int(snap_info['total_owners']*snap_info['transfer_fee']))]) Redisdb.rpush("messages", "*|snapshot_end|" + msg) # The list is sorted from more to less holding Redisdb.rpush("messages", datetime.datetime.now().isoformat() + " CSV list sorting") lst.sort(key=lambda x: x[2], reverse=True) Redisdb.rpush("messages", datetime.datetime.now().isoformat() + " CSV list sorting OK") # calculus of the distribution tmplt_distr = "{0:." + str( snap_info['asset_distribute']['precision']) + "f}" tmplt_hold = "{0:." + str(snap_info['asset_hold']['precision']) + "f}" total_distr_amount = 0 for l in enumerate(lst): if snap_info['distribution_amount'] > 0: amount = round( l[1][2] / balance * snap_info['distribution_amount'], snap_info['asset_distribute']['precision']) elif snap_info['distribution_ratio'] > 0: amount = round(l[1][2] * snap_info['distribution_ratio'], snap_info['asset_distribute']['precision']) if amount < snap_info['distribution_minimum']: # truncate the list lst = lst[:l[0]] break total_distr_amount += amount lst[l[0]][3] = tmplt_distr.format(amount) lst[l[0]][2] = tmplt_hold.format(l[1][2]) # feed the distribution container for l in lst: Redisdb.hset("distribute:" + snap_info['asset_hold_id'], l[0], l[3]) if snap_info['distribution_amount'] > 0: rest = snap_info['distribution_amount'] - total_distr_amount else: rest = 0 # TODO: what to do with the rest? return lst
def do_distribution(dat): """ Makes the actual transfer of tokens. The snapshoting step have to be done before this. :return: """ import config try: wallet = config.BitShares(node=config.WSS_NODE, wif=dat['form']['key']) ga = wallet.wallet.getAccounts() print(ga) except Exception as err: print(err.__repr__()) Redisdb.rpush("messages", "*|launch_error|key not found") print(0) return False snap_info = pickle.loads(Redisdb.get("snapshot_info")) Redisdb.rpush("messages", "--------------") Redisdb.rpush("messages", datetime.datetime.now().isoformat() + "Distribution started") batch_count = 0 batch = [] lst = list( Redisdb.hgetall("distribute:" + snap_info['asset_hold_id']).items()) while True: distr = lst.pop(0) batch_count += 1 batch.append(distr) if batch_count > 100 or len(lst) == 0: op_dat = { 'operation': 'distribution_batch', 'job': batch, 'key': dat['form']['key'], 'asset': snap_info['asset_distribute']['symbol'] } Redisdb.rpush("operations", pickle.dumps(op_dat)) Redisdb.incr("batch_jobs", 1) if len(lst) == 0: break Redisdb.rpush("messages", datetime.datetime.now().isoformat() + "Jobs generated") Redisdb.rpush("operations", pickle.dumps({'operation': 'distribution_check'}))
def obtain_balances(asset_id): snap_datetime = datetime.datetime.now().isoformat() info = pickle.loads(Redisdb.get("snapshot_info")) n_from = 0 n_call = 0 total_balance = 0 batch_balance = 0 total_holders = 0 while True: data = { "jsonrpc": "2.0", "params": [ "asset", "get_asset_holders", [info['asset_hold_id'], n_from, 100] ], "method": "call", "id": n_call } try: rtn = requests.post('http://209.188.21.157:8090/rpc', data=json.dumps(data)) if rtn.status_code != 200: return False rst = rtn.json()['result'] except Exception as err: print(err.__repr__()) print() return False bal = [] to_exit = False for r in rst: amount = round( int(r['amount']) / 10**info['asset_hold']['precision'], info['asset_hold']['precision']) if amount < info['asset_hold_minimum']: to_exit = True break else: total_balance += amount batch_balance += amount total_holders += 1 Redisdb.hset( "balance:" + info['asset_hold_id'], r['account_id'], pickle.dumps({ 'account': r['account_id'], 'datetime': snap_datetime, 'amount': amount })) if len(rst) < 100 or to_exit: Redisdb.rpush( "messages", datetime.datetime.now().isoformat() + " Total count:" + str(total_holders)) Redisdb.set("total_balance:" + info['asset_hold_id'], total_balance) Redisdb.set("total_owners:" + info['asset_hold_id'], total_holders) break if total_holders % 1000 == 0: Redisdb.rpush( "messages", datetime.datetime.now().isoformat() + " Holders: {} Balance: {} Median: {}".format( total_holders, total_balance, round( batch_balance / 1000, 2))) batch_balance = 0 n_from += 100 n_call += 1 print(n_call) return True