├── LICENSE ├── README.md ├── api.py ├── functions.py ├── register_config.json ├── requirements.txt └── worker_config.json /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Anarkrypto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Delegated Proof of Work - API 2 | Outdated worker API for P2P delegated Proof of Work, Nano cryptocurrency 3 | 4 | # Notice! 5 | This project was rebranded to P2PoW and moved to the following directory: https://github.com/anarkrypto/P2PoW 6 | 7 | This repository will no longer receive updates 8 | 9 | 10 |
11 | 12 | 13 |
14 | 15 | 16 |
17 | 18 |
19 | 20 | 21 |
22 | 23 | 24 |
25 | 26 | ## Introduction 27 | 28 | This project has the ability to make Nano instantaneous for any type of low computing device by delegating PoW to Workers on a P2P network, requiring no central servers / coordinators. 29 | 30 | The only way a user can have their transaction completed by Worker is to attach an extra transaction block as a reward after their transactions. The only way for a Worker to receive his reward is by completing PoW and broadcasting the user's transaction first to be able to validate his reward block. Read more in this Medium article 31 | 32 | ## Configuring 33 | First you will need to have a Nano node >= v.19.0 installed and synchronized. 34 | - Edit the worker_config.json file. Enter: 35 | - Your Nano Account where the rewards will be deposited. It must have a small amount of Nano as it will be used to register your network node within a transaction. 36 | - The Private Key of this account (attention, do not confuse with the master seed). Required to sign the decentralized registration transaction. Don't worry, the registration transaction signature is made directly from your computer and no one else will have access to your private keys. 37 | - Your preferred Representative 38 | - The Minimum Fee you prefer for each job, represented in mNano. Current suggestion: 0.0001 39 | - Your Node address with RPC port. Probably 127.0.0.1:7076 or [::1]:7076 40 | - Your Worker Node Address (can be the same as node). 41 | - Active Difficulty can be ignored for now, I recommend you leaving false 42 | - Max Multiplier will be the maximum PoW difficulty your worker will accept when using active difficulty in dynamic PoW 43 | - API running Port. 7090 by default. 44 | Example: 45 | 46 |

47 | 48 | 49 | { 50 | "reward_account": "nano_3k8it4efod5hw194axu1m483qee7pryfrfung9pyr8x9pysfpz1bothqmo1y", 51 | "private_key": "79E937E6A24B2ABC7DD3894F03BF62303BE8CBA936CBEC7CA3BACEC763AF3BB2", 52 | "representative": "nano_3x7cjioqahgs5ppheys6prpqtb4rdknked83chf97bot1unrbdkaux37t31b", 53 | "fee": 0.0001, 54 | "node": "127.0.0.1:7076", 55 | "worker": "127.0.0.1:7076", 56 | "use_active_difficulty": false, 57 | "max_multiplier": 10.0, 58 | "port": 7090 59 | } 60 | 61 | 62 | 63 | ## How to run 64 | 65 | Install python dependencies 66 | pip3 install -r requirements.txt 67 | run: 68 | python3 api.py 69 | 70 | 71 | ## How it works 72 | 73 | The user requests Workers confirmation at the endpoint worker_ip_address:7090/open_request 74 | And get the following kind of answer: 75 | 76 | { 77 | "fee": 100000000000000000000000000, 78 | "max_multiplier":10.0, 79 | "min_multiplier":1.0, 80 | "reward_account":"nano_3k8it4efod5hw194axu1m483qee7pryfrfung9pyr8x9pysfpz1bothqmo1y", 81 | "version":"0.0.1" 82 | } 83 | 84 | 85 | This is the worker's reward address and his minimum fee per work (in raws). 86 | This fee must be multiplied by the (number of transactions sent - transaction reward) 87 | 88 | Then users will use this information to sign the worker reward transaction. 89 | 90 | 91 | Users can request the PoW by calling the endpoint (/request_pow) sending a json data like the following one: 92 | 93 | 94 | curl -s --header "Content-Type: application/json" --request POST --data '{ 95 | "user_transaction": { "block_type": "state", 96 | "account": "nano_1bca3mirn8aauzy5m5o984bfphsxsbwsixc47d535rkg75nbzd3w737n6iya", 97 | "previous": "11B1CCBBD2CFDDF46D0DE6D96D447431940C79DC90EB4F10E8271CD1BBB43ABD", 98 | "representative": "nano_1hza3f7wiiqa7ig3jczyxj5yo86yegcmqk3criaz838j91sxcckpfhbhhra1", 99 | "balance": "1999999999999999999997172", 100 | "link": "F4D9B075403D7325A9F0775B7FFE2E9B7AE14936AA953388949E1751DD996007", 101 | "link_as_account": "nano_3x8sp3tn1hdm6pnz1xtuhzz4x8utw76mfcno8g6bb9iqc9gskr191tq8eaat", 102 | "signature": "69CAEA32BC8E9CF8E47CF4453493929C6BDC11373FD4C228FDBE11A5D76F32A4FF5CFC5D7AD33A67CF3AE9014434B39CDFE83BB9F5F2BF08F5ED0EFBC391870D" 103 | }, 104 | "worker_transaction": { 105 | "block_type": "state", 106 | "account": "nano_1bca3mirn8aauzy5m5o984bfphsxsbwsixc47d535rkg75nbzd3w737n6iya", 107 | "previous": "1BD2525DE8E4E439A50322A04C77031D3D0F747C53839F20DB3716DEFC6E2D4D", 108 | "representative": "nano_1hza3f7wiiqa7ig3jczyxj5yo86yegcmqk3criaz838j91sxcckpfhbhhra1", 109 | "balance": "999999999999999999997172", 110 | "link": "BABB6F016CA3B94DDFB978335610516FE6940B392524C5B76E81C62A73014294", 111 | "link_as_account": "nano_3goufw1psaxsbqhuky3mcra74uz8ki7mkbb6rpupx1g87bsi4innftt4sckk", 112 | "signature": "E40DD1FAB27161FF2DD73FAB20082A04F86632ECFC8FC60707D2B8221B1CFE7C3B01A8D718AC04C3F6F5EC764C8EBD9905CC756FE2DB381D81A0AC7D2A974D00" 113 | } 114 | }' worker_address:7090/request_pow 115 | 116 | 117 | 118 | The first transaction is the user_transaction. The second one (worker_transaction) is responsible for the payment of the worker. 119 | 120 | 121 | If all goes well, the output by the worker API will look like this: 122 | 123 | 124 | 125 | ## Decentralized Registration 126 | Instead of a Nano-like peering system or protocols like torrent, this project introduced a new way for peers to exchange their IP addresses. 127 | This API automatically saves the worker IP (ipv4 or ipv6) in a Nano transaction, after encoding to nano account format, and associates it with the default register account (1de1egated1proof1ofwork1themain1registration1accountp46rpyr6) with other transaction. 128 | That's why you need to put your account private key in worker_config.json and have a small balance there. 129 | 130 | 131 | Customers of users interested in hiring workers read the transaction history of the registration account. There they find the workers account. And in the workers account they will find their IP addresses, through which P2P user<->worker connections are made. The interesting thing about this system is that besides saving bandwidth and avoiding certain attacks, it avoids the need for main peers and also allows us to associate and recognize worker accounts. 132 | 133 | 134 | ## GPU 135 | Although you can use your CPU for testing, if you want to be a worker it is recommended to use a good GPU, as in worker competition the best hardware solves PoW first. 136 | For this you need to have your GPU drivers properly installed and OpenCL. With this your node will use its GPU for PoWs. 137 | 138 | 139 | The next version will also allow the user to request PoW for multiple transactions at once. Reward block only needs to be 1, last in chain. 140 | 141 | ## Attention!!! 142 | This version is not much more than a Proof of Concept yet. Use for small transactions only. 143 | -------------------------------------------------------------------------------- /api.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify, request 2 | from functions import block_create, balance, frontier, broadcast, get_difficulty, solve_work, get_my_ip, pending_filter, receive, check_history, register, encode_ip, worker, register_config 3 | from nanolib import Block, validate_account_id, validate_private_key, get_account_id 4 | import requests 5 | import json 6 | import waitress 7 | 8 | app = Flask(__name__) 9 | 10 | #Check config 11 | try: 12 | validate_account_id(worker["account"]) 13 | validate_private_key(worker["private_key"]) 14 | validate_account_id(worker["representative"]) 15 | except Exception as e: 16 | print ("Invalid worker_config.json settings found! Details: ") 17 | print (e) 18 | quit() 19 | 20 | #Check if key pair is valid 21 | if worker["account"] != get_account_id(private_key=worker["private_key"], prefix="nano_"): 22 | print ("Invalid key pair") 23 | quit() 24 | print ("Configurations okay") 25 | 26 | #Check if Node is online 27 | try: 28 | r = requests.post(worker["node"], json={"action": "version"}) 29 | except: 30 | print ("Node " + worker["node"] + " Offline! Exiting") 31 | quit() 32 | else: 33 | if "node_vendor" in r.json(): 34 | print ("Node (" + r.json()["node_vendor"] + ") is online on " + worker["node"]) 35 | else: 36 | print ("Node " + worker["node"] + " error! Exiting") 37 | quit() 38 | 39 | #Registration Process 40 | print ("Checking register") 41 | history = check_history(worker["account"], register_config["account"]) 42 | if history is not None: 43 | if int(history["amount"]) == register_config["sign_new_account_code"]: 44 | print ("Found your worker account registration: " + worker["account"]) 45 | myIP = get_my_ip() #check IP register 46 | ip_account = encode_ip(myIP) 47 | history = check_history(worker["account"], ip_account) 48 | if history is not None: 49 | print ("Found your actuall IP address registration: " + myIP) 50 | if int(history["amount"]) != register_config["sign_new_ip_code"]: 51 | print ("Incorrect amount in your IP address register") 52 | try_r = register (worker["account"], worker["representative"], frontier(worker["account"]), ip_account, register_config["sign_new_ip_code"], 1.0) 53 | if (try_r is False): 54 | quit() 55 | else: 56 | print ("Not found your actuall IP address registration: " + myIP) 57 | try_r = register (worker["account"], worker["representative"], frontier(worker["account"]), ip_account, register_config["sign_new_ip_code"], 1.0) 58 | if (try_r is False): 59 | quit() 60 | else: 61 | print ("Incorrect amount in register") 62 | try_r = register (worker["account"], worker["representative"], frontier(worker["account"]), register_config["account"], register_config["sign_new_account_code"], 1.0) 63 | if (try_r is False): 64 | quit() 65 | else: 66 | print ("Not found your worker registration: " + worker["account"]) 67 | try_r = register (worker["account"], worker["representative"], frontier(worker["account"]), register_config["account"], register_config["sign_new_account_code"], 1.0) 68 | if (try_r is False): 69 | quit() 70 | else: 71 | myIP = get_my_ip() #check IP register 72 | ip_account = encode_ip(myIP) 73 | history = check_history(worker["account"], ip_account) 74 | if history is not None: 75 | print ("Found your actuall IP address registration: " + myIP) 76 | if int(history["amount"]) != register_config["sign_new_ip_code"]: 77 | print ("Incorrect amount in your IP address register") 78 | try_r = register (worker["account"], worker["representative"], frontier(worker["account"]), ip_account, register_config["sign_new_ip_code"], 1.0) 79 | if (try_r is False): 80 | quit() 81 | else: 82 | print ("Not found your actuall IP address registration: " + myIP) 83 | try_r = register (worker["account"], worker["representative"], frontier(worker["account"]), ip_account, register_config["sign_new_ip_code"], 1.0) 84 | if (try_r is False): 85 | quit() 86 | 87 | #convert mNano fee to raws 88 | worker["fee"] = int(worker["fee"] * 1000000000000000000000000000000) 89 | 90 | #Listen /open_request 91 | @app.route('/open_request', methods=['GET', 'POST']) 92 | def opening_request(): 93 | print ("Open Request") 94 | header = {"version": "0.0.1", "reward_account": worker["account"], "fee": worker["fee"], "max_multiplier": worker["max_multiplier"]} 95 | if worker["use_active_difficulty"] == True: 96 | header["min_multiplier"] = get_difficulty() 97 | else: 98 | header["min_multiplier"] = 1.0 99 | return header 100 | 101 | #Listen /request_work 102 | @app.route('/request_work', methods=['GET', 'POST']) 103 | def request_work(): 104 | #request transactions 105 | data = request.get_json() 106 | #global fee 107 | 108 | #check if user and worker transaction are present 109 | if "user_transaction" in data: 110 | user = data.get("user_transaction") 111 | else: 112 | print ("User transaction missing") 113 | return {"Error": "User transaction missing"} 114 | 115 | if "worker_transaction" in data: 116 | worker = data.get("worker_transaction") 117 | else: 118 | print ("Worker transaction missing") 119 | return {"Error": "Worker transaction missing"} 120 | 121 | #Read transaction and check if is valid 122 | user_block = block_create(user.get('block_type'), user.get('account').replace("xrb_", "nano_"), user.get('representative'), user.get('previous'), user.get("link_as_account"), int(user.get('balance')), user.get("signature")) 123 | if user_block == "invalid": 124 | print ("Invalid user transaction") 125 | return '{"Error": "Invalid user transaction"}' 126 | worker_block = block_create(worker.get('block_type'), worker.get('account').replace("xrb_", "nano_"), worker.get('representative'), worker.get('previous'), worker.get("link_as_account"), int(worker.get('balance')), worker.get("signature")) 127 | if worker_block == "invalid": 128 | print ("Invalid worker transaction") 129 | return {"Error": "Invalid worker transaction"} 130 | 131 | #If account source in both transactions is different 132 | if user_block.account != worker_block.account : 133 | print ("Different source accounts ") 134 | return '{"Error": "Different Accounts source"}' 135 | 136 | #If worker account is wrong 137 | if worker_block.link_as_account != worker["account"]: 138 | print("Worker account is incorrect") 139 | return {"Error": "Worker account is incorrect"} 140 | 141 | #Check if previous block in worker hash is correct 142 | if worker_block.previous != user_block.block_hash: 143 | print ("Incorrect previous block in worker block") 144 | return {"Error": "Incorrect previous block in worker block" } 145 | 146 | #Recalculate the Fee with active_difficulty with 10% tolerance 147 | if worker["use_active_difficulty"] == True: 148 | multiplier = get_difficulty() 149 | if (multiplier > worker["max_multiplier"]): 150 | return {"Error": "Maximum difficulty exceded"} 151 | else: 152 | print ("Using active difficulty: " + str(multiplier)) 153 | if multiplier * 0.9 > 1.0: 154 | worker["fee"] *= (multiplier * 0.9) #multiplier fee with tolerance 155 | else: 156 | worker["fee"] *= 1.0 157 | else: 158 | multiplier = 1.0 159 | 160 | #Check if fee is right 161 | user_fee = user_block.balance - worker_block.balance 162 | if user_fee < worker["fee"]: 163 | print ( "Fee " + str(user_fee) + " is less than minimum " + str(worker["fee"])) 164 | return {"Error": "Fee is less than minimum"} 165 | 166 | #Check previous block 167 | if frontier(user_block.account) == user_block.previous: 168 | print ("Block is valid: " + user_block.block_hash) 169 | else: 170 | print ("Gap previous block") 171 | return {"Error": "Gap previous block"} 172 | 173 | #Check if account source has sufficient funds for both transactions 174 | if balance(user_block.account) < int(worker_block.balance): 175 | print ("Insufficient funds") 176 | return {"Error": "Insuficient funds"} 177 | else: 178 | print ("Account has sufficient funds") 179 | 180 | #If all is right, check active_difficulty and PoW both transactions 181 | print ("Solving Works...") 182 | user_block.work = solve_work(user_block.previous, multiplier) 183 | print ("User transaction: work done") 184 | worker_block.work = solve_work(worker_block.previous, multiplier) 185 | print ("Worker transaction: work done") 186 | 187 | #Broadcast 188 | r_user = broadcast(user_block.json()) 189 | r_worker = broadcast(worker_block.json()) 190 | 191 | #response 192 | if 'hash' in r_user: 193 | print ("User transaction successful! Block:" + r_user.get("hash")) 194 | response = {"Successful": r_user["hash"]} 195 | 196 | if 'hash' in r_worker: 197 | print ("Worker transaction successful! Block:" + r_worker["hash"]) 198 | print ("Your reward has arrived!") 199 | else: 200 | print ("Worker transaction fail. Details: " + json.dumps(r_worker)) 201 | else: 202 | print ("User transaction fail. Details: " + json.dumps(r_user) + "\n") 203 | response = {"Error": "User transaction fail. Details: " + json.dumps(r_user)} 204 | 205 | print ("\n") 206 | return response 207 | 208 | #Serving API 209 | waitress.serve(app, host='0.0.0.0', port=worker["service_port"]) 210 | -------------------------------------------------------------------------------- /functions.py: -------------------------------------------------------------------------------- 1 | from nanolib import Block, get_account_id 2 | import json 3 | import requests 4 | 5 | #Convert IP address to valid url format 6 | def toURL(address): 7 | if not address.startswith("http"): 8 | return "http://" + address 9 | else: 10 | return address 11 | 12 | #Import config 13 | with open('worker_config.json') as worker_config: 14 | data = json.load(worker_config) 15 | worker = { 16 | "account": data['reward_account'].replace("xrb_", "nano_"), 17 | "private_key": data['private_key'], 18 | "representative": data['representative'].replace("xrb_", "nano_"), 19 | "fee": data['fee'], 20 | "node": toURL(data['node']), 21 | "worker_node": toURL(data['worker']), 22 | "use_active_difficulty": data['use_active_difficulty'], 23 | "max_multiplier": data['max_multiplier'], 24 | "service_port": data['port'], 25 | } 26 | 27 | 28 | with open('register_config.json') as register_config: 29 | data_register = json.load(register_config) 30 | register_config = { 31 | "account": data_register['registration_account'].replace("xrb_", "nano_"), 32 | "sign_new_ip_code": int(data_register['sign_new_ip_code']), 33 | "sign_new_account_code": int(data_register['sign_new_account_code']), 34 | "get_ip": toURL(data_register['get_ip']) 35 | } 36 | 37 | #Read transaction Block 38 | def block_create(block_type, account, representative, previous, link_as_account, balance, signature): 39 | try: 40 | block = Block( 41 | block_type=block_type, 42 | account=account, 43 | representative=representative, 44 | previous=previous, 45 | link_as_account=link_as_account, 46 | balance=balance, 47 | ) 48 | except: 49 | return "invalid" 50 | else: 51 | if signature is not None: 52 | try: 53 | block.signature = signature 54 | except: 55 | return "invalid" 56 | return block 57 | 58 | #check if the account has the balance 59 | def balance(account): 60 | resp = requests.post(worker["node"], json={"action": "account_balance", "account": account}) 61 | return int(resp.json()['balance']) 62 | 63 | #account history 64 | def check_history(account, destination): 65 | request = requests.post(worker["node"], json={"action": "account_history", "account": account, "count": -1}) 66 | history = request.json()["history"] 67 | for b in history: 68 | if destination == b["account"].replace("xrb_", "nano_"): 69 | return b 70 | return (None) 71 | 72 | #check if block already exists 73 | def frontier(account): 74 | request = requests.post(worker["node"], json={"action": "accounts_frontiers", "accounts": [account]}) 75 | if account in request.json()["frontiers"]: 76 | return request.json()["frontiers"][account] 77 | else: 78 | request = requests.post(worker["node"], json={ "action": "account_key", "account" : account }) 79 | return request.json()["key"] 80 | 81 | #active difficulty network 82 | def get_difficulty(): 83 | request = requests.post(worker["node"], json={"action": "active_difficulty", "include_trend": "true"}) 84 | return float(request.json()["multiplier"]) 85 | 86 | #solve work 87 | def solve_work (hash, multiplier): 88 | request = requests.post(worker["node"], json={"action": "work_generate", "hash": hash, "multiplier": multiplier }) 89 | return request.json()["work"] 90 | 91 | #broadcast transaction 92 | def broadcast(transaction): 93 | request = requests.post(worker["node"], json={"action": "process", "json_block": "true", "block": json.loads(transaction)}) 94 | return request.json() 95 | 96 | #Find pending transactions and return those that complete the required amount. 97 | def pending_filter (account, threshold): 98 | request = requests.post(worker["node"], json={"action": "pending", "account": account, "count": 1, "threshold": threshold}) 99 | blocks = request.json()["blocks"] 100 | if blocks != "": 101 | return blocks 102 | else: 103 | request = requests.post(worker["node"], json={"action": "pending", "account": account, "threshold": 1}) 104 | blocks = request.json()["blocks"] 105 | if blocks == "": 106 | return None 107 | else: 108 | for key in blocks: 109 | blocks[key] = int(blocks[key]) 110 | sorted_blocks = dict(sorted(blocks.items(), key=lambda kv: kv[1], reverse=True)) 111 | receive_blocks = {}; acumulated = 0 112 | for key in sorted_blocks: 113 | receive_blocks[key] = sorted_blocks[key] 114 | acumulated += sorted_blocks[key] 115 | if acumulated >= threshold: 116 | return receive_blocks 117 | return None 118 | 119 | #Receive pending transactions 120 | def receive(account, private_key, representative, amount, link, difficulty): 121 | request = requests.post(worker["node"], json={"action": "accounts_frontiers", "accounts": [account]}) 122 | if account in request.json()["frontiers"]: 123 | previous = request.json()["frontiers"][account] 124 | else: 125 | previous = "0000000000000000000000000000000000000000000000000000000000000000" 126 | block = Block( 127 | block_type="state", 128 | account=account, 129 | representative=representative, 130 | previous=previous, 131 | balance=balance(worker["account"]) + amount, 132 | link=link 133 | ) 134 | block.work = solve_work(frontier(account), difficulty) 135 | block.sign(private_key) 136 | r = broadcast(block.json()) 137 | return r 138 | 139 | #Send transactions (Used in the registration process) 140 | def send (account, representative, previous, link_as_account, amount, difficulty): 141 | block = block_create("state", account, representative, previous, link_as_account, balance(worker["account"]) - amount, None) 142 | block.sign(worker["private_key"]) 143 | block.work = solve_work(block.previous, difficulty) 144 | r = broadcast(block.json()) 145 | return r 146 | 147 | #Get external IPv4 or IPv6 148 | def get_my_ip(): 149 | myIP = requests.get(register_config["get_ip"]) 150 | return myIP.text 151 | 152 | #encode IPv4 or IPv6 into Nano Account format 153 | def encode_ip (ip): 154 | ip_as_bytes = bytes(map(int, ip.split('.'))) #convert to bytes 155 | ip_as_bytes += (32 - len(ip_as_bytes)) * b'\0' #padding 156 | ip_account = get_account_id(public_key=ip_as_bytes.hex(), prefix="nano_") ##convert to nano account format 157 | return ip_account 158 | 159 | #Registration Function. This process uses Nano transactions to save worker IP and account and associate it with the main registration account 160 | def register (account, representative, previous, register_account, code, multiplier): 161 | if code == register_config["sign_new_account_code"]: 162 | what = "your worker account" 163 | if code == register_config["sign_new_ip_code"]: 164 | what = "your IP address" 165 | if balance(account) >= code: 166 | print ("You have sufficient funds to register " + what) 167 | print ("Registering " + what + " now") 168 | r = send(account, representative, previous, register_account, code, multiplier) 169 | if 'hash' in r: 170 | print ("Successfully registred " + what + " Block: " + r["hash"]) 171 | else: 172 | print ("Register transaction fail. Details: " + json.dumps(r)) 173 | else: 174 | print ("Insufficient funds, checking for unpocketed transactions...") 175 | pending = pending_filter(account, code) 176 | if pending == None: 177 | print ("You have not sufficient funds to register " + what + "! Please send at least " + str((register_config["sign_new_account_code"] + register_config["sign_new_ip_code"])) + " raws to your worker account") 178 | return False 179 | else: 180 | for block in pending: 181 | print ("Receiving pending block: " + str(pending[block]) + " raws") 182 | r = receive(account, worker["private_key"], representative, int(pending[block]), block, multiplier) 183 | if 'hash' in r: 184 | print ("Transaction received!" + "! Block: " + r["hash"]) 185 | else: 186 | print ("Transaction receiving fail. Details: " + json.dumps(r)) 187 | print ("Ok, registering " + what + " now") 188 | r = send(account, representative, frontier(account), register_account, code, multiplier) 189 | if 'hash' in r: 190 | print ("Successfully registred " + what + "! Block: " + r["hash"]) 191 | else: 192 | print ("Register transaction fail. Details: " + json.dumps(r)) 193 | -------------------------------------------------------------------------------- /register_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "registration_account": "nano_1de1egated1proof1ofwork1themain1registration1accountp46rpyr6", 3 | "sign_new_ip_code": 1330480815, 4 | "sign_new_account_code": 133048002828, 5 | "get_ip": "https://api.ipify.org" 6 | } 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aioconsole==0.1.15 2 | aiomonitor==0.4.4 3 | bitarray==1.0.1 4 | certifi==2019.9.11 5 | chardet==3.0.4 6 | Click==7.0 7 | ecdsa==0.13.3 8 | ed25519-blake2b==1.4 9 | expiringdict==1.1.4 10 | Flask==1.1.1 11 | waitress==1.3.1 12 | idna==2.8 13 | itsdangerous==1.1.0 14 | Jinja2==2.10.3 15 | MarkupSafe==1.1.1 16 | msgpack==0.6.2 17 | nanolib==0.3 18 | p2p-python==3.0.4 19 | py-cpuinfo==5.0.0 20 | pycryptodomex==3.9.0 21 | PySocks==1.7.1 22 | requests==2.22.0 23 | terminaltables==3.1.0 24 | urllib3==1.25.6 25 | Werkzeug==0.16.0 26 | xmltodict==0.12.0 27 | -------------------------------------------------------------------------------- /worker_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "reward_account": "nano_3k8it4efod5hw194axu1m483qee7pryfrfung9pyr8x9pysfpz1bothqmo1y", 3 | "private_key": "79E937E6A24B2ABC7DD3894F03BF62303BE8CBA936CBEC7CA3BACEC763AF3BB2", 4 | "representative": "nano_3x7cjioqahgs5ppheys6prpqtb4rdknked83chf97bot1unrbdkaux37t31b", 5 | "fee": 0.001, 6 | "node": "127.0.0.1:7076", 7 | "worker": "127.0.0.1:7076", 8 | "use_active_difficulty": false, 9 | "max_multiplier": 10.0, 10 | "port": 7090 11 | } 12 | --------------------------------------------------------------------------------