def extract(username): now = datetime.datetime.now(tz=pytz.UTC) if getPublicKeyFromUsername(username): param = loadJson("%s.json" % username) threshold = param.get("threshold", 0.2) share = param.get("share", 1.0) forgery = loadJson("%s.forgery" % username, os.path.join(zen.DATA, username)) data = OrderedDict(sorted([[a,w] for a,w in forgery.get("contributions", {}).items()], key=lambda e:e[-1], reverse=True)) tbw = OrderedDict([a,w*share] for a,w in data.items() if w*share >= threshold) totalDistributed = sum(tbw.values()) dumpJson( { "timestamp": "%s" % now, "delegate-share": round(forgery.get("blocks", 0.) * dposlib.rest.cfg.blockreward * (1.0 - share), 8), "undistributed": round(sum(w for w in data.values() if w < threshold), 8), "distributed": round(totalDistributed, 8), "fees": round(forgery.get("fees", 0.), 8), "weight": OrderedDict(sorted([[a,s/totalDistributed] for a,s in tbw.items()], key=lambda e:e[-1], reverse=True)) }, "%s.tbw" % now.strftime("%Y%m%d-%H%M"), folder=os.path.join(zen.ROOT, "app", ".tbw", username) ) # reset forgery keeping unpaind voters forgery["contributions"] = OrderedDict([a, 0. if a in tbw else w] for a,w in data.items()) forgery["blocks"] = 0 forgery["fees"] = 0. dumpJson(forgery, "%s.forgery" % username, os.path.join(zen.DATA, username))
def configure(**kwargs): if "username" in kwargs: username = kwargs.pop("username") if getPublicKeyFromUsername(username): if not os.path.exists( os.path.join(zen.JSON, "%s-webhook.json" % username)): if not setDelegate(username, peer=kwargs.get("webhook_peer", None)): return zen.logMsg("%s delegate webhook set" % username) else: # load update and save in a row zen.dumpJson( dict(zen.loadJson("%s.json" % username), **kwargs), "%s.json" % username) zen.logMsg("%s delegate set" % username) else: zen.logMsg("can not find delegate %s" % username) elif not len(kwargs): root = zen.loadJson("root.json") if "env" in root: delegates = zen.loadJson("delegates.json", os.path.dirname(root["env"])) for secret in delegates["secrets"]: setDelegate(dposlib.core.crypto.getKeys(secret)["publicKey"])
def updateRegistryNonces(username): folder = os.path.join(zen.DATA, username) registries = [n for n in os.listdir(folder) if n.endswith(".registry")] if not len(registries): return False KEYS01, KEYS02 = getKeys(username) wallet = rest.GET.api.wallets(username).get("data", {}) nonce = int(wallet["nonce"]) + 1 if KEYS01 and len(wallet): for name in registries: full_registry = loadJson(name, folder=folder) registry = loadJson(name+".milestone", folder=folder) if len(registry): logMsg("updating transaction nonces in %s..." % (name+".milestone")) for tx in list(registry.values()): old_id = tx["id"] new_tx = dposlib.core.Transaction(tx) new_tx.nonce = nonce nonce += 1 new_tx.signWithKeys(KEYS01["publicKey"], KEYS01["privateKey"]) if KEYS02 is not None: new_tx.signSignWithKey(KEYS02["privateKey"]) new_tx.identify() registry.pop(old_id) full_registry.pop(old_id) registry[new_tx["id"]] = new_tx full_registry[new_tx["id"]] = new_tx dumpJson(registry, name+".milestone", folder=folder) dumpJson(full_registry, name, folder=folder) return True
def _enableTask(delay, func, **params): if func not in ["enableTask", "disableTask"]: config = zen.loadJson("root.json") params.update(interval=delay) config["tasks-enabled"] = dict( config.get("tasks-enabled", {}), **{ func: dict([k.replace("_", "-"), v] for k, v in params.items()) }) zen.dumpJson(config, "root.json")
def setDelegate(uname_or_puk, peer=None): peer = zen.WEBHOOK_PEER if peer is None else peer account = dposlib.rest.GET.api.wallets(uname_or_puk).get("data", {}) attributes = account.get("attributes", {}) if attributes.get("delegate", False): username = attributes["delegate"]["username"] config = zen.loadJson("%s.json" % username) config["publicKey"] = account["publicKey"] zen.logMsg("%s configuration:" % username) if "#1" not in config: config["#1"] = askPrivateKey("enter first secret: ", config["publicKey"]) if not config.get("#1", False): zen.logMsg("\ndelegate identification failed") return False if "secondPublicKey" in attributes: config["#2"] = askPrivateKey("enter second secret: ", attributes["secondPublicKey"]) if not config.get("#2", False): zen.logMsg("\ndelegate identification failed") return False overwrite = input("Overwrite previous webhoook?[Y/n] ") \ if os.path.exists( os.path.join(zen.JSON, "%s-webhook.json" % username) ) else "y" if overwrite in "yY": if not waitForPeer(peer): zen.logMsg("%s peer unreachable" % peer) return False webhook = setWebhook(config["publicKey"]) if "token" in webhook: webhook["peer"] = peer zen.logMsg("webhook subscription succeded") zen.dumpJson(webhook, "%s-webhook.json" % username) else: zen.logMsg("an error occured with webhook subscription:\n%s" % webhook) zen.dumpJson(config, "%s.json" % username) else: zen.logMsg( "%s seems not to be a valid delegate username or public key" % uname_or_puk) return False return True
def checkIfForging(): config = zen.loadJson("root.json").get("tasks-enabled", {}).get("checkIfForging", {}) active_delegates = zen.biom.dposlib.rest.cfg.activeDelegates notification_delay = config.get("notification-delay", 10 * 60) monitored_peer = config.get("monitored-peer", zen.API_PEER) usernames = [ name.split("-")[0] for name in os.listdir(zen.JSON) if name.endswith("-webhook.json") ] for user in usernames: dlgt = _GET.api.delegates(user, peer=monitored_peer).get("data", {}) last_block = dlgt.get("blocks", {}).get("last", {}) last_computed_block = zen.loadJson("%s.last.block" % user, folder=zen.DATA) if dlgt and last_computed_block == {}: zen.dumpJson(last_block, "%s.last.block" % user, folder=zen.DATA) return False elif dlgt.get("rank", 52) <= active_delegates: blkchn = _GET.api.blockchain(peer=monitored_peer).get("data", {}) height = blkchn.get("block", {}).get("height", 0) current_round = (height - 1) // active_delegates dlgt_round = (last_block["height"] - 1) // active_delegates diff = current_round - dlgt_round missed = last_computed_block.get("missed", 0) if diff > 1: now = time.time() last_computed_block["missed"] = missed + 1 delay = now - last_computed_block.get("notification", 0) msg = ("%s just missed a block" % user) if missed < 1 else ( "%s is missing blocks (total %d)" % (user, missed + 1)) if delay > notification_delay: zen.misc.notify(msg) last_computed_block["notification"] = now elif missed > 0: msg = "%s is forging again" % user zen.misc.notify(msg) last_computed_block.pop("notification", False) last_computed_block.pop("missed", False) else: msg = "%s is forging" % user last_computed_block.update(last_block) zen.dumpJson(last_computed_block, "%s.last.block" % user, folder=zen.DATA) zen.logMsg("round check [delegate:%d | blockchain:%d] - %s" % (dlgt_round, current_round, msg)) return True
def pushBackKeys(): module = sys.modules[__name__] delegates = [] for key, value in [(k, v) for k, v in module.__dict__.items() if k[-2:] in "#1#2"]: username, num = key.replace("_", "").split("#") num = "#" + num config = zen.loadJson("%s.json" % username) config[num] = value zen.dumpJson(config, "%s.json" % username) delegates.append(username) for username in set(delegates): zen.logMsg("%s secrets pushed back" % username)
def regenerateUnapplied(username, filename): registry = zen.loadJson("%s.registry" % filename, os.path.join(zen.DATA, username)) tbw = zen.loadJson("%s.tbw" % filename, os.path.join(zen.TBW, username, "history")) for tx in registry.values(): if not transactionApplied(tx["id"]): zen.logMsg('tx %(id)s [%(amount)s --> %(recipientId)s] unapplied' % tx) else: tbw["weight"].pop(tx["recipientId"], False) zen.dumpJson(tbw, '%s-unapplied.tbw' % filename, os.path.join(zen.TBW, username))
def adjust(username, value): if getPublicKeyFromUsername(username): folder = os.path.join(zen.DATA, username) forgery = loadJson("%s.forgery" % username, folder=folder) total = sum(forgery["contributions"].values()) dumpJson( { "fees": forgery.get("fees", 0.), "blocks": forgery.get("blocks", 0), "contributions": OrderedDict(sorted([[a, v/total*value] for a,v in forgery["contributions"].items()], key=lambda e:e[-1], reverse=True)) }, "%s.forgery" % username, folder=folder ) else: logMsg("%s username does not exist" % username)
def pullKeys(): module = sys.modules[__name__] for username in [ n.replace("-webhook.json", "") for n in next(os.walk(zen.JSON))[-1] if n.endswith("-webhook.json") ]: config = zen.loadJson("%s.json" % username) hide = False if "#1" in config: setattr(module, "_%s_#1" % username, config.pop("#1")) hide = True if "#2" in config: setattr(module, "_%s_#2" % username, config.pop("#2")) hide = True if hide: zen.logMsg("%s secrets pulled" % username) zen.dumpJson(config, "%s.json" % username)
def deploy(host="127.0.0.1", port=5000): root = zen.loadJson("root.json") root["port"] = port root["host"] = host zen.dumpJson(root, "root.json") normpath = os.path.normpath executable = normpath(sys.executable) gunicorn_conf = os.path.normpath( os.path.abspath(os.path.expanduser("~/ark-zen/gunicorn.conf.py"))) with io.open("./zen.service", "w") as unit: unit.write( u"""[Unit] Description=Zen TBW server After=network.target [Service] User=%(usr)s WorkingDirectory=%(wkd)s Environment=PYTHONPATH=%(path)s:${HOME}/dpos ExecStart=%(bin)s/gunicorn zen.app:app \ --bind=%(host)s:%(port)d --workers=5 --timeout 10 --access-logfile - Restart=always [Install] WantedBy=multi-user.target """ % { "host": host, "port": port, "usr": os.environ.get("USER", "unknown"), "wkd": normpath(sys.prefix), "path": normpath(os.path.dirname(zen.__path__[0])), "bin": os.path.dirname(executable), }) if os.system("%s -m pip show gunicorn" % executable) != "0": os.system("%s -m pip install gunicorn" % executable) os.system("chmod +x ./zen.service") os.system("sudo cp %s %s" % (gunicorn_conf, normpath(sys.prefix))) os.system("sudo mv --force ./zen.service /etc/systemd/system") os.system("sudo systemctl daemon-reload") if not os.system("sudo systemctl restart zen"): os.system("sudo systemctl start zen")
def download_backup(): if flask.request.method == "POST": challenge = zen.loadJson("challenge.json") value = flask.request.form.get("value") if time.time() > challenge.get("expiration", 0): return "", 408 elif challenge.get("value", "") != value: return "", 403 else: return flask.send_file(os.path.abspath( os.path.join(zen.__path__[0], "data-bkp.tar.bz2")), as_attachment=True) challenge = "%s" % int( hashlib.sha256(binascii.hexlify(os.urandom(32))).hexdigest()[:5], 16) zen.dumpJson({ "value": challenge, "expiration": time.time() + 60 }, "challenge.json") zen.misc.notify("Your challenge code is:\n%s" % challenge) return flask.render_template("backup.html")
def setDelegate(pkey, webhook_peer, public=False): printNewLine() # for each publicKey, get account data (merge delegate and wallet info) req = rest.GET.api.delegates(pkey).get("data", {}) account = rest.GET.api.wallets(pkey).get("data", {}) account.update(req) if account != {}: username = account["username"] logMsg("setting up %s delegate..." % username) # load forger configuration and update with minimum data config = loadJson("%s.json" % username) config.update(**{"publicKey":pkey, "#2":askSecret(account, cmp_key="secondPublicKey")}) dumpJson(config, "%s.json" % username) # create a webhook if no one is set webhook = loadJson("%s-webhook.json" % username) if not webhook.get("token", False): data = rest.POST.api.webhooks( peer=webhook_peer, event="block.forged", target="http://%s:5000/block/forged" % (zen.PUBLIC_IP if public else "127.0.0.1"), conditions=[{"key": "generatorPublicKey", "condition": "eq", "value": pkey}] ) webhook = data.get("data", False) if webhook: webhook["peer"] = webhook_peer dumpJson(webhook, "%s-webhook.json" % username) logMsg("%s webhook set" % username) else: logMsg("error occur on webhook creation:\n%s" % data) else: logMsg("webhook already set for delegate %s" % username) logMsg("%s delegate set" % username) return account else: logMsg("%s: %s" % (req.get("error", "API Error"), req.get("message", "...")))
def start(): global DAEMON, IS_SYNCING, STATUS, SEED_STATUS sleep_time = zen.rest.cfg.blocktime * zen.rest.cfg.activeDelegates data = zen.loadJson("bg-marker.json") data["stop"] = False zen.dumpJson(data, "bg-marker.json") DAEMON = threading.Event() # check health status every minutes daemon_1 = setInterval(60)(checkNode)() # generate svg charts every round daemon_2 = setInterval(sleep_time)(generateCharts)() # check all registries daemon_3 = setInterval(sleep_time)(checkRegistries)() # check updates daemon_4 = setInterval(5 * sleep_time)(checkVersion)() # check forge daemon_5 = setInterval(30)(checkIfForging)() zen.logMsg("Background tasks started !") zen.misc.notify("Background tasks started !") try: while not DAEMON.is_set(): time.sleep(sleep_time) zen.logMsg("sleep time finished :\n%s" % zen.json.dumps(CHECK_RESULT)) except KeyboardInterrupt: zen.logMsg("Background tasks interrupted !") daemon_1.set() daemon_2.set() daemon_3.set() daemon_4.set() daemon_5.set() zen.misc.notify("Background tasks stoped !")
def checkApplied(username): folder = os.path.join(zen.DATA, username) sqlite = initDb(username) cursor = sqlite.cursor() for name in [n for n in os.listdir(folder) if n.endswith(".registry")]: full_registry = loadJson(name, folder=folder) # try to lad a milestone first, if no one exists registry = loadJson(name+".milestone", folder=folder) # if void dict returned by loadJson, then load registry file if not len(registry): registry = dict(full_registry) #loadJson(name, folder=folder) logMsg("starting transaction check from %s..." % name) else: logMsg("resuming transaction check from %s..." % (name+".milestone")) start = time.time() transactions = list(registry.values()) for tx in transactions: if zen.misc.transactionApplied(tx["id"]): logMsg("transaction %(id)s <type %(type)s> applied" % registry.pop(tx["id"])) if "payments" in tx.get("asset", {}): for record in tx["asset"]["payments"]: cursor.execute( "INSERT OR REPLACE INTO transactions(filename, timestamp, amount, address, id) VALUES(?,?,?,?,?);", (os.path.splitext(name)[0], tx["timestamp"], int(record["amount"])/100000000., record["recipientId"], tx["id"]) ) # set a milestone every 5 seconds if (time.time() - start) > 5.: sqlite.commit() dumpJson(registry, name+".milestone", folder=folder) logMsg("milestone set (%d transaction left to check)" % len(registry)) start = time.time() dumpJson(registry, name+".milestone", folder=folder) if len(registry) == 0: dumpJson(full_registry, name, folder=os.path.join(folder, "backup")) try: os.remove(os.path.join(folder, name)) os.remove(os.path.join(folder, name+".milestone")) except: pass checked_tx = full_registry.values() zen.misc.notify("Payroll successfully broadcasted !\n%.8f %s sent trough %d transactions" % ( sum([sum([int(rec["amount"]) for rec in tx["asset"].get("payments", [])]) for tx in checked_tx])/100000000., dposlib.rest.cfg.symbol, len(checked_tx) )) else: zen.misc.notify("Transactions are still to be checked (%d)..." % len(registry)) sqlite.commit()
def computeDelegateBlock(username, generatorPublicKey, block): # get reward and fees from block data rewards = float(block["reward"])/100000000. fees = float(block["totalFee"])/100000000. logMsg("%s forged %s : %.8f|%.8f" % (username, block["id"], rewards, fees)) blocks = 1 # Because sometime network is not in good health, the spread function # can exit with exception. So compare the ids of last forged blocks # to compute rewards and fees... filename = "%s.last.block" % username folder = os.path.join(zen.DATA, username) last_block = loadJson(filename, folder=folder) # if there is a <username>.last.block if last_block.get("id", False): logMsg("last known forged: %s" % last_block["id"]) # get last forged blocks from blockchain # TODO : get all blocks till the last forged (not the 100 last ones) req = rest.GET.api.delegates(generatorPublicKey, "blocks") last_blocks = req.get("data", []) # raise Exceptions if issues with API call if not len(last_blocks): raise Exception("No new block found in peer response") elif req.get("error", False) != False: raise Exception("Api error : %r" % req.get("error", "?")) # compute fees, blocs and rewards from the last saved block for blk in last_blocks: _id = blk["id"] # stop the loop when last forged block is reach in the last blocks list if _id == last_block["id"]: break # if bc is not synch and response is too bad, also check timestamp elif _id != block["id"] and blk["timestamp"]["epoch"] > last_block["timestamp"]: logMsg(" getting rewards and fees from block %s..." % _id) rewards += float(blk["forged"]["reward"])/100000000. fees += float(blk["forged"]["fee"])/100000000. blocks += 1 else: logMsg(" ignoring block %s (previously forged)" % _id) # else initiate <username>.last.block else: dumpJson(block, filename, folder=folder) raise Exception("First iteration for %s" % username) # find forger information using username forger = loadJson("%s.json" % username) forgery = loadJson("%s.forgery" % username, folder=folder) # compute the reward distribution excluding delegate address = dposlib.core.crypto.getAddress(generatorPublicKey) excludes = forger.get("excludes", [address]) if address not in excludes: excludes.append(address) contributions = distributeRewards( rewards, username, minvote=forger.get("minimum_vote", 0), excludes=excludes ) # dump true block weight data _ctrb = forgery.get("contributions", {}) dumpJson( { "fees": forgery.get("fees", 0.) + fees, "blocks": forgery.get("blocks", 0) + blocks, "contributions": OrderedDict(sorted([[a, _ctrb.get(a, 0.)+contributions[a]] for a in contributions], key=lambda e:e[-1], reverse=True)) }, "%s.forgery" % username, folder=folder ) # dump current forged block as <username>.last.block dumpJson(block, filename, folder=folder) # notify vote movements msg = "\n".join( ["%s removed from %s list [%.8f Arks]" % (zen.misc.shorten(wallet), username, _ctrb[wallet]) for wallet in [w for w in _ctrb if w not in contributions]] + \ ["%s added to %s list" % (zen.misc.shorten(wallet), username) for wallet in [w for w in contributions if w not in _ctrb]] ) logMsg("checking vote changes..." + (" nothing hapened !" if msg == "" else ("\n%s"%msg))) if msg != "": zen.misc.notify(msg)
def init(**kwargs): webhook_peer = kwargs.get("webhook_peer", zen.WEBHOOK_PEER) root = loadJson("root.json") # if no options given, initialize forgers set on the node if not len(kwargs): # find delegates secrets and generate publicKeys env_folder = os.path.dirname(root["env"]) if os.path.exists(os.path.join(env_folder, "delegates.json")): # ark core >= v2.1.x delegates = loadJson("delegates.json", env_folder) else: # ark core <= v2.0.x delegates = loadJson("delegates.json", os.path.join(env_folder, "config")) pkeys = [dposlib.core.crypto.getKeys(secret)["publicKey"] for secret in delegates["secrets"]] for pkey in set(pkeys): setDelegate(pkey, webhook_peer, webhook_peer != zen.WEBHOOK_PEER) elif "usernames" in kwargs: pkeys = [] for username in kwargs.pop("usernames", []): req = rest.GET.api.delegates(username).get("data", {}) if len(req): pkeys.append(req["publicKey"]) for pkey in pkeys: account = setDelegate(pkey, webhook_peer, webhook_peer != zen.WEBHOOK_PEER) if account: config = loadJson("%s.json" % account["username"]) config["#1"] = askSecret(account, cmp_key="publicKey") config.update(**kwargs) dumpJson(config, "%s.json" % account["username"]) elif "username" in kwargs: username = kwargs.pop("username") if getPublicKeyFromUsername(username): config = loadJson("%s.json" % username) config.update(**kwargs) if not(config.get("fee_level", True)): config.pop("fee_level", None) logMsg("Dynamic fees disabled") dumpJson(config, "%s.json" % username) logMsg("%s delegate set" % username) else: logMsg("can not find delegate %s" % username) else: tbw = loadJson("tbw.json") for key in [k for k in ["target_delegate", "fee_coverage"] if k in kwargs]: value = kwargs.pop(key) if value: if key in tbw: tbw.pop(key) logMsg("%s disabled" % key) else: tbw[key] = value logMsg("%s enabled" % key) max_per_sender = int(kwargs.pop("max_per_sender", False)) if max_per_sender != False: env = zen.loadEnv(root["env"]) env["CORE_TRANSACTION_POOL_MAX_PER_SENDER"] = max_per_sender zen.dumpEnv(env, root["env"]) zen.logMsg("env parameter CORE_TRANSACTION_POOL_MAX_PER_SENDER set to %d \n ark-core-relay have to be restarted" % (max_per_sender)) tbw.update(**kwargs) dumpJson(tbw, "tbw.json")
def _disableTask(func): config = zen.loadJson("root.json") tasks = config.get("tasks-enabled", {}) tasks.pop(func, None) config["tasks-enabled"] = tasks zen.dumpJson(config, "root.json")
def dumpRegistry(username, chunk_size=50): folder = os.path.join(zen.ROOT, "app", ".tbw", username) tbw_files = [n for n in os.listdir(folder) if n.endswith(".tbw")] if not len(tbw_files): return False KEYS01, KEYS02 = getKeys(username) wallet = rest.GET.api.wallets(username).get("data", {}) # for testing purpose # wallet = { # "address":"ARfDVWZ7Zwkox3ZXtMQQY1HYSANMB88vWE", # "publicKey":"030da05984d579395ce276c0dd6ca0a60140a3c3d964423a04e7abe110d60a15e9", # "nonce":"16973", # "balance":"160032168390", # "attributes":{ # "delegate":{ # "username":"******", # "voteBalance":"182267782242191", # "forgedFees":"447838954643", # "forgedRewards":"44064800000000", # "producedBlocks":221746, # "rank":8, # "lastBlock":{ # "version":0,"timestamp":93163312, # "height":11498689, # "previousBlockHex":"23ccc346f07fe6a22e2cb8420faf7ed53675e14b85ded3d55d5f3e118d987043", # "previousBlock":"23ccc346f07fe6a22e2cb8420faf7ed53675e14b85ded3d55d5f3e118d987043", # "numberOfTransactions":0, # "totalAmount":"0", # "totalFee":"0", # "reward":"200000000", # "payloadLength":0, # "payloadHash":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", # "generatorPublicKey":"030da05984d579395ce276c0dd6ca0a60140a3c3d964423a04e7abe110d60a15e9", # "blockSignature":"304402207026213f4376fa0270f257600b6825e559e1a43e1402182e1d6a9e2388573b1402202d631c2cc54ed8e9aeff0014d0d8efb31b706be1b38774be13a279ef239647da", # "idHex":"e3dbd0b2647647bc5cb853f78d82fc9949dd2dc669469f5973622ce04a27fc0c", # "id":"e3dbd0b2647647bc5cb853f78d82fc9949dd2dc669469f5973622ce04a27fc0c" # }, # "round":225466 # }, # "vote":"02b54f00d9de5a3ace28913fe78a15afcfe242926e94d9b517d06d2705b261f992" # }, # "isDelegate":True, # "isResigned":False, # "vote":"02b54f00d9de5a3ace28913fe78a15afcfe242926e94d9b517d06d2705b261f992", # "username":"******" # } if KEYS01 and len(wallet): config = loadJson("%s.json" % username) fee_level = config.get("fee_level", False) if fee_level: dposlib.core.Transaction.useDynamicFee(fee_level) else: dposlib.core.Transaction.useStaticFee() for name in tbw_files: data = loadJson(name, folder) amount = data["distributed"] totalFees = 0 registry = OrderedDict() timestamp = slots.getTime() weights = sorted(data["weight"].items(), key=lambda e:e[-1], reverse=True) nonce = int(wallet["nonce"]) + 1 while len(weights) % chunk_size <= 3: chunk_size -= 1 for chunk in [weights[i:i+chunk_size] for i in range(0, len(weights), chunk_size)]: transaction = dposlib.core.multiPayment( *[[round(amount*wght, 8), addr] for addr, wght in chunk], vendorField=config.get("vendorField", "%s reward" % username) ) if "vendorFieldHex" in config: transaction.vendorFieldHex = config["vendorFieldHex"] dict.__setitem__(transaction, "senderPublicKey", wallet["publicKey"]) transaction.nonce = nonce transaction.senderId = wallet["address"] transaction.timestamp = timestamp transaction.setFee() transaction.signWithKeys(KEYS01["publicKey"], KEYS01["privateKey"]) if KEYS02 is not None: transaction.signSignWithKey(KEYS02["privateKey"]) transaction.identify() registry[transaction["id"]] = transaction totalFees += transaction["fee"] nonce += 1 totalFees /= 100000000.0 if config.get("wallet", False): transaction0 = dposlib.core.transfer( round(data["delegate-share"] + data["fees"] - totalFees, 8), config["wallet"], "%s share" % username, ) dict.__setitem__(transaction0, "senderPublicKey", wallet["publicKey"]) transaction0.nonce = nonce transaction0.senderId = wallet["address"] transaction0.timestamp = timestamp transaction0.setFee() transaction0.feeIncluded() transaction0.signWithKeys(KEYS01["publicKey"], KEYS01["privateKey"]) if KEYS02 is not None: transaction0.signSignWithKey(KEYS02["privateKey"]) transaction0.identify() registry[transaction0["id"]] = transaction0 dumpJson(registry, "%s.registry" % os.path.splitext(name)[0], os.path.join(zen.DATA, username)) data["covered fees"] = totalFees dumpJson(data, name, os.path.join(folder, "history")) os.remove(os.path.join(folder, name)) return True
def checkIfForging(): try: usernames = [ name.split("-")[0] for name in os.listdir(zen.JSON) if name.endswith("-webhook.json") ] delegate_number = zen.dposlib.rest.cfg.activeDelegates notification_delay = 10 * 60 # 10 minutes in seconds for username in usernames: data = zen.dposlib.rest.GET.api.delegates(username) \ .get("data", {}).get("blocks", {}).get("last", {}) height = zen.dposlib.rest.GET.api.blockchain() \ .get("data", {}).get("block", {}).get("height", 0) last_block = zen.loadJson("%s.last.block" % username, folder=zen.DATA) last_round = (data["height"] - 1) // delegate_number current_round = (height - 1) // delegate_number # if last forged block not found if last_block == {}: zen.dumpJson(data, "%s.last.block" % username, folder=zen.DATA) else: # get current height # custom parameters missed = last_block.get("missed", 0) last_notification = last_block.get("notification", 0) # blockchain parameters diff = current_round - last_round now = time.time() delay = now - last_notification if diff > 1: rank = zen.dposlib.rest.GET.api.delegates(username) \ .get("data", {}).get("rank", -1) if not rank: zen.logMsg("delegate %s not found" % username) send_notification = (rank <= delegate_number) and \ (delay >= notification_delay) # do the possible checks if rank > delegate_number: msg = "%s is not in forging position" % username if delay >= notification_delay: zen.misc.notify(msg) last_block["notification"] = now else: msg = ("%s just missed a block" % username) if diff == 2 else \ ("%s is missing blocks (total %d)" % (username, missed + 1)) if send_notification: zen.misc.notify(msg) last_block["notification"] = now last_block["missed"] = missed + 1 elif missed > 0 or last_notification > 0: msg = "%s is forging again" % username zen.misc.notify(msg) last_block.pop("missed", False) last_block.pop("notification", False) else: msg = "%s is forging" % username # dump last forged block info last_block.update(data) zen.dumpJson(last_block, "%s.last.block" % username, folder=zen.DATA) zen.logMsg("check if forging: %d | %d - %s" % (last_round, current_round, msg)) except Exception as e: zen.logMsg("chart generation error:\n%r\n%s" % (e, traceback.format_exc()))
def stop(): data = zen.loadJson("bg-marker.json") data["stop"] = True zen.dumpJson(data, "bg-marker.json")
def setup(clear=False, extern=False): # load root.json file. If not exists, root is empty dict root = zen.loadJson("root.json") # delete all keys from root dict if asked if clear: root.clear() # if extern: root["config-folder"] = None elif "appnames" not in root: try: root["appnames"] = identify_app() except Exception: pass # first configuration if no config-folder, it is set to "" because None is # used when zen is not running on a blockchain node if root.get("config-folder", "") == "": # if ark config directory found if os.path.isdir(os.path.expanduser("~/.config/ark-core")): root["config-folder"] = os.path.abspath( os.path.expanduser("~/.config/ark-core")) else: ans = "None" try: while ans not in "nNyY": ans = input("Is zen installed on a running node ?[Y/n] ") except KeyboardInterrupt: zen.logMsg("\nconfiguration aborted...") return if ans in "yY": while not os.path.exists(root.get("config-folder", "")): try: root["config-folder"] = os.path.abspath( input("Enter config folder: ")) except KeyboardInterrupt: zen.logMsg("\nconfiguration aborted...") return else: root["config-folder"] = None # if zen is running on a blockchain node if root["config-folder"] is not None: # get folder containing node configuration network = zen.chooseItem("select network:", *list(os.walk(root["config-folder"]))[0][1]) if not network: zen.logMsg("configuration aborted...") return root["name"] = network root["env"] = os.path.join(root["config-folder"], network, ".env") root["blockchain"] = zen.loadJson("config.json", folder=os.path.join( root["config-folder"], network)).get("token", None) # load .env file and enable webhooks if disabled if not os.path.exists(root["env"]): zen.logMsg("no env file available...") return zen.ENV = loadEnv(root["env"]) if not zen.ENV.get("CORE_WEBHOOKS_ENABLED", "") == "true": zen.ENV["CORE_WEBHOOKS_ENABLED"] = "true" zen.ENV["CORE_WEBHOOKS_HOST"] = "0.0.0.0" zen.ENV["CORE_WEBHOOKS_PORT"] = "4004" dumpEnv(zen.ENV, root["env"]) if input( "webhooks are now enabled, restart relay ?[Y/n] ") in "yY": start_pm2_app(identify_app().get("relay")) # if zen is not running on a blockchain node else: # get webhook subscription peer address try: root["webhook"] = input("Peer address for webhook submition: ") while dposlib.rest.GET.api.webhooks( peer=root.get("webhook", "http://127.0.0.1:4004"), timeout=2).get("status", False) != 200: root["webhook"] = input("Peer address for webhook submition: ") except KeyboardInterrupt: zen.logMsg("\nconfiguration aborted...") return root["webhook"] = root["webhook"] # get monitored node api address try: root["api"] = input("Peer address for API requests: ") while "data" not in dposlib.rest.GET.api.blockchain(peer=root.get( "api", "http://127.0.0.1:4003"), timeout=2): root["api"] = input("Peer address for API requests: ") except KeyboardInterrupt: zen.logMsg("\nconfiguration aborted...") return root["api"] = root["api"] # final check if root.get("blockchain", "") not in dir(dposlib.net): root["blockchain"] = zen.chooseItem( "Select blockchain running on node:", *[ name for name in dir(dposlib.net) if not name.startswith("_") and getattr( dposlib.net, name, {}).get("familly", None) == "ark" ]) if not root["blockchain"]: zen.logMsg("blockchain can not be determined...") return else: zen.logMsg("configuration done") zen.dumpJson(root, "root.json")