From bc2097709d10a0d882069bda65a51df60433947c Mon Sep 17 00:00:00 2001 From: Marco van Dijk Date: Tue, 27 Dec 2022 17:02:53 +0100 Subject: [PATCH] Rewrite --- .gitignore | 30 --------- .vscode/settings.json | 3 - README.md | 105 +++++++----------------------- RewardCall.py | 104 ++++++++++++++++++++++++++++++ better-call-reward.py | 144 ------------------------------------------ 5 files changed, 128 insertions(+), 258 deletions(-) delete mode 100644 .gitignore delete mode 100644 .vscode/settings.json create mode 100755 RewardCall.py delete mode 100755 better-call-reward.py diff --git a/.gitignore b/.gitignore deleted file mode 100644 index 755a39a..0000000 --- a/.gitignore +++ /dev/null @@ -1,30 +0,0 @@ - -# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode -# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode - -### VisualStudioCode ### -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -!.vscode/*.code-snippets - -# Local History for Visual Studio Code -.history/ - -# Built Visual Studio Code Extensions -*.vsix - -### VisualStudioCode Patch ### -# Ignore all local history of files -.history -.ionide - -# Support for Project snippet scope -.vscode/*.code-snippets - -# Ignore code-workspaces -*.code-workspace - -# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 3516cb9..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "python.formatting.provider": "autopep8" -} \ No newline at end of file diff --git a/README.md b/README.md index 6975d78..6e18e95 100644 --- a/README.md +++ b/README.md @@ -1,88 +1,31 @@ -# better call reward +## better call reward -#### Script to call `reward` for Livepeer orchestrator. +This script uses a list of configured URL's Orchestrator CLI URL's to get info on the last reward call round and current Livepeer round +Then compare these numbers and if they are not the same calls `reward`. -What this do? -This script uses livepeer API to get info on the last reward call round and current round. -Then compare these numbers and if they are not the same just call to `reward`. -Script in default is checking numbers every 1h. You can change this value by editing `retryTimeReward` +Modify the `ORCH_TARGETS` variable in `RewardCall.py` to set your Orchestrator URL's -My Orchestrator has many missing `reward` calls and this was the main motivation to create this script. +### Dependencies -#### help: +Requires python 3 and pip: `sudo pacman -S python python-pip` + +Then install the requests library using pip `python -m pip install requests` + +### Running the script + +Recommended to run `better-call-reward.py` as a systemd service: +Move the script to an accessible location, like `/usr/local/bin` +`sudo nano /etc/systemd/system/rewardCaller.service` ``` -usage: better-call-reward.py [-h] [-url [URL]] [-delay [DELAY]] +[Unit] +Description=Reward Caller +After=multi-user.target -Optional app description - -optional arguments: - -h, --help show this help message and exit - -url [URL] URL for your Orchestrator -``` - - -#### example usage: -`./better-call-reward.py -url http://localhost:7935` - -`./better-call-reward.py ` - this use default url `http://localhost:7935` - -#### script output on succes: -``` -Orchestrator URL: http://192.168.137.103:7935 -Connection success ----Info--- -Orchestrator Version: 0.5.31-ec920c67 -GolangRuntimeVersion: go1.18.1 - -Transcoders: -[1] Address: 127.0.0.1:54396 Capacity:14 - -[ 2022-06-02 10:40:36.105142 ] Orchestrator status: online -[ 2022-06-02 10:40:36.105176 ] Last reward round: 2584 -[ 2022-06-02 10:40:36.105190 ] Current round: 2585 -[ 2022-06-02 10:40:36.105195 ] Call to reward! -[ 2022-06-02 10:42:32.865710 ] -[ 2022-06-02 10:42:32.865755 ] Call reward success. -. Next call: 3534s -``` - -#### example output from O: -``` -I0602 10:40:35.750595 417331 handlers.go:845] Calling reward -2022/06/02 10:41:30 http: TLS handshake error from 208.115.199.25:41434: EOF -I0602 10:42:31.336083 417331 transactionManager.go:119] -******************************Eth Transaction****************************** - -Invoking transaction: "rewardWithHint". Inputs: "_newPosPrev: 0x86c5A8231712CC8aaa23409B5ad315f304C09531 _newPosNext: 0x22Ae24C2D1f489906266609d14c4C0387909A38a" Hash: "0x410696c59c24527e9c34323be46470f96694cc870982d674ea1b222ae25c59b5". - -*************************************************************************** -I0602 10:42:32.508922 417331 handlers.go:855] Call to reward successful -``` - - - -#### some error: -``` -Orchestrator URL: http://192.168.137.103:7935 -Connection success ----Info--- -Orchestrator Version: 0.5.31-ec920c67 -GolangRuntimeVersion: go1.18.1 - -Transcoders: -[1] Address: 127.0.0.1:54396 Capacity:14 - -[ 2022-06-02 10:33:26.291112 ] Orchestrator status: online -[ 2022-06-02 10:33:26.291156 ] Last reward round: 2584 -[ 2022-06-02 10:33:26.291175 ] Current round: 2585 -[ 2022-06-02 10:33:26.291189 ] Call to reward! -[ 2022-06-02 10:33:26.292445 ] -[ 2022-06-02 10:33:26.292469 ] Call to reward fail. Error: -``` - -Buy me a coffee:
-LPT (Arbitrum): `0xE32971e1a55152A94Fa55DFb80ACdC4bA55679C3`
-AETH (Arbitrum): `0xE32971e1a55152A94Fa55DFb80ACdC4bA55679C3`
-ETH: `0xE32971e1a55152A94Fa55DFb80ACdC4bA55679C3`
-DOGE: `D8mJBFdSQscQKce2vnPsKr4dC4sFvypBfU`
+[Service] +Type=simple +Restart=always +ExecStart=/usr/bin/python3 /usr/local/bin/RewardCall.py +[Install] +WantedBy=multi-user.target +``` \ No newline at end of file diff --git a/RewardCall.py b/RewardCall.py new file mode 100755 index 0000000..8352e12 --- /dev/null +++ b/RewardCall.py @@ -0,0 +1,104 @@ +#!/bin/python3 +import requests +import time +import sys +from datetime import datetime + + +# EDIT THIS to set your O cliAddr targets which this script should call reward on +ORCH_TARGETS = ['http://127.0.0.1:7935'] + + +# Global Constants +sleepTimeActive = 60 # Wait time when there is any O which has to call reward this round +sleepTimeIdle = 60 * 60 * 4 # Wait time between round checks + +# Logs `info` to the terminal with an attached datetime +def log(info): + print("[", datetime.now(), "] - ", info) + sys.stdout.flush() + +# Gets the last round the orch called reward +def getLastRewardRound(url): + try: + r = requests.get(url + '/orchestratorInfo') + except requests.exceptions.RequestException as e: + return 0 + if r.status_code != 200: + return 0 + return r.json()['Transcoder']['LastRewardRound'] + +# Return the current active Livepeer round +def getCurrentRound(url): + try: + r = requests.get(url + '/currentRound') + except requests.exceptions.RequestException as e: + return 0 + if r.status_code != 200: + return 0 + return int(r.content.hex(), 16) + +class Orchestrator: + def __init__(self, uri): + self.uri = uri + self.rewardRound = 0 + self.currentRound = 0 + self.hasReward = False + +latestRound = 0 +orchestrators = [] +# Init orch objecs +for url in ORCH_TARGETS: + log("Adding Orchestrator with URL '" + url + "'") + orchestrators.append(Orchestrator(url)) +# Main loop +while True: + # Init delay to a long sleep time. Override later if an O requires reward calling + delay = sleepTimeIdle + for i in range(len(orchestrators)): + # Update last round if we have a new one + currentRound = getCurrentRound(orchestrators[i].uri) + if currentRound == 0: + log("Error: can't get current round info on '" + url + "'") + orchestrators[i].hasReward = False + delay = sleepTimeActive + continue + if currentRound > latestRound: + latestRound = currentRound + # Reset hasReward flags for all O's since the latest round has changed + for j in range(len(orchestrators)): + orchestrators[j].hasReward = False + # If we are behind in rounds, notify user + if currentRound < latestRound: + log("Orchestrator '" + orchestrators[i].uri + "' is behind in block syncing!") + # We can continue now if the latest round has not changed + if orchestrators[i].hasReward: + log("Skipping Orchestrator '" + orchestrators[i].uri + "' since they have called reward this round") + continue + # Check the last reward round + lastRewardRound = getLastRewardRound(orchestrators[i].uri) + if lastRewardRound == 0: + log("Error: can't get last reward round info on '" + url + "'") + orchestrators[i].hasReward = False + delay = sleepTimeActive + continue + log("Latest reward round for Orchestrator '" + orchestrators[i].uri + "' is " + str(lastRewardRound) + " and the latest livepeer round is " + str(latestRound)) + if lastRewardRound < latestRound: + log("Calling reward for '" + orchestrators[i].uri + "'") + r = requests.get(orchestrators[i].uri + '/reward') + log(r) + if r.status_code == 200: + log('Call to reward success.') + orchestrators[i].hasReward = True + else: + log('Call to reward fail. Error: ' + str(r)) + orchestrators[i].hasReward = False + delay = sleepTimeActive + else: + orchestrators[i].hasReward = True + log("Orchestrator '" + orchestrators[i].uri + "' has already called reward in round " + str(latestRound)) + # Sleep a long or short time based on whether all Orchestrators have been handled + while delay > 0: + log("Next check in " + str(delay) + " seconds...") + delay = delay - 30 + time.sleep(30) diff --git a/better-call-reward.py b/better-call-reward.py deleted file mode 100755 index ceb78e5..0000000 --- a/better-call-reward.py +++ /dev/null @@ -1,144 +0,0 @@ -#!/bin/python3 -from asyncio import constants -from pyclbr import Function -import requests -import time -import sys -import argparse -from datetime import datetime - -# set your Orchestrator url -URL_DEFAULT = 'http://localhost:7935' # default - -retryTimeOffline = 60 # delay when O is offline, in seconds -retryTimeReward = 60 * 60 # time retry call "reward ", in seconds - - -def parseArgs(): - # Instantiate the parser - parser = argparse.ArgumentParser( - description='Script to resolve problem with livepeer "reward" call. ') - parser.add_argument('-url', type=str, nargs='?', - help='URL for your Orchestrator') - # todo: ? add delay params - # parser.add_argument('-delay', type=int, nargs='?', - # help='delay between next try of call "reward"') - # Parse the argument - args = parser.parse_args() - if args.url is None: - url = URL_DEFAULT - else: - url = args.url - print("Orchestrator URL: " + url) - return url -# end parseArgs() - - -def statusOrch(url): # /status , check O status and get some info - try: - r = requests.get(url + '/status') - responseStatus = r.status_code - except requests.exceptions.RequestException as e: # This is the correct syntax - print("Error during connection to '" + url + - "'. Please check your Orchestrator url.") - exit(0) - print("Connection success") - print("---Info---") - print("Orchestrator Version: " + r.json()['Version']) - print("GolangRuntimeVersion: " + r.json()['GolangRuntimeVersion']) - print("") - - # transcoders info - print("Transcoders:") - i = 1 - for t in r.json()['RegisteredTranscoders']: - print("["+str(i) + "] Address: " + t['Address'] + - " Capacity:" + str(t['Capacity'])) -# end statusOrch(url) ---------------------------------------------- - - -def pingOrch(url): # /status - try: - requests.get(url + '/status') - except requests.exceptions.RequestException as e: # This is the correct syntax - print("Error during connection to '" + url + - "'. Please check your Orchestrator url.") - return False - return True -# end pingOrch() ---------------------------------------------- - - -def getLastRewardRound(url): # /orchestratorInfo - try: - r = requests.get(url + '/orchestratorInfo') - except requests.exceptions.RequestException as e: # This is the correct syntax - print("Error: can't get 'Last Reward Round' round info.") - return -1 - - if r.status_code != 200: - print("Can not get last reward round info. Error: " + str(r)) - return -2 - return r.json()['Transcoder']['LastRewardRound'] -# end getLastRewardRound() ---------------------------------------------- - - -def getCurrentRound(url): # /currentRound - # get current Round value - try: - r = requests.get(url + '/currentRound') - except requests.exceptions.RequestException as e: # This is the correct syntax - print("Error: can't get 'current Round' info.") - return -1 - - if r.status_code != 200: - print("Error: can't get 'current Round' info."+" ERROR: " + str(r)) - return -2 - - currentRound = int(r.content.hex(), 16) - return currentRound -# end getLastRewardRound() ---------------------------------------------- - - -def log(info): - print("[", datetime.now(), "]", info) -# end log(info) ---------------------------------------------- - - -url = parseArgs() -statusOrch(url) -print("") - -while True: - lastRewardRound = getLastRewardRound(url) - currentRound = getCurrentRound(url) - - log('Orchestrator status: ' + ('online' if pingOrch(url) else 'offline')) - if (currentRound < 0 or lastRewardRound < 0): - log("Is Orchestrator online ?") - delay = retryTimeOffline - else: - log('Last reward round: ' + str(lastRewardRound)) - log('Current round: ' + str(currentRound)) - - if currentRound != lastRewardRound: - log('Call reward!') - r = requests.get(url + '/reward') # call reward - log(r) - if r.status_code == 200: - log('Call to reward success.') - else: - log('Call to reward fail. Error: ' + str(r)) - - else: - log('Do not call reward. ' + 'Reward for current round ' + - str(currentRound) + ' already called.') - delay = retryTimeReward - - while delay > 0: - print(". Next call: " + str(delay) + "s " + "\r", end="") - sys.stdout.flush() - delay = delay - 1 - time.sleep(1) - print(" ") - sys.stdout.flush() - print("----------------------------")