├── 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 |
--------------------------------------------------------------------------------