From df8c5d7f26f17ed62d0f7cfc464d8a9eddefc4bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20Eng=C3=A9libert?= <tuxmain@zettascript.org> Date: Sat, 7 Sep 2019 14:25:22 +0200 Subject: [PATCH] Fixes, client config --- README.md | 4 +-- client.py | 73 +++++++++++++++++++++++++++++++++++++++++++++---------- server.py | 16 +++++++++--- utils.py | 9 ++++--- 4 files changed, 81 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index e39036a..4afa5b3 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,10 @@ Install the following packages: `libleveldb-dev` `libsodium-dev` `python3` `pyth ## How to use it -Create config & data dir: (change `~/.gmixer-g1`, so you can run different +Create config & data dir: (change `~/.config/gmixer-g1`, so you can run different servers with different configs) (note that each server must have different dir) - python3 server.py -i -d ~/.gmixer-g1 + python3 server.py -i -d ~/.config/gmixer-g1 Edit config and set your values. If you are not using any proxy, `bind_` and `public_` addresses should be the same. If `salt` or `password` are empty, they will be asked at runtime. For security reasons, it is not a good idea to write them in commandline. diff --git a/client.py b/client.py index 702503a..b72594f 100644 --- a/client.py +++ b/client.py @@ -22,14 +22,55 @@ Sources: https://www.ietf.org/rfc/rfc3092.txt <- Very important RFC, please read it """ -import sys, os, asyncio, getpass, random, time, secrets, socket +import sys, os, asyncio, getpass, random, time, secrets, socket, json import ubjson import plyvel import libnacl.sign from duniterpy.key import SigningKey, PublicKey import utils -DIR = "~/.gmixer" +DIR = "~/.config/gmixer-client" +BMA_HOSTS = ["g1.duniter.fr 443", "g1.duniter.org 443", "g1.presles.fr 443", "g1.cgeek.fr 443", "ts.g1.librelois.fr 443"] +PEER_SIG_AGE_MAX = 604800 # max age of a peer document signature +IDTY_SIG_AGE_MAX = 2592000 # max age of a idty document signature +CURRENCY = "g1" + +# Read json config file +def read_config(cdir, conf_overwrite={}): + if not os.path.isfile(cdir+"/config.json"): + configfile = open(cdir+"/config.json", "w") + configfile.write("{}") + configfile.close() + + with open(cdir+"/config.json", "r") as configfile: + try: + conf = json.load(configfile) + except json.JSONDecodeError: + utils.logprint("Config: bad JSON => abort", utils.LOG_ERROR) + exit(1) + + conf.setdefault("currency", CURRENCY) + conf.setdefault("server", {}) + conf["server"].setdefault("peer_sig_age_max", PEER_SIG_AGE_MAX) + conf["server"].setdefault("idty_sig_age_max", IDTY_SIG_AGE_MAX) + conf.setdefault("client", {}) + conf["client"].setdefault("bma_hosts", BMA_HOSTS) + conf["client"].setdefault("proxy", None) + conf["client"].setdefault("proxy_onion_only", False) + conf.setdefault("idty", {}) + conf["idty"].setdefault("needed", True) + + for key in conf_overwrite: + c = conf + k = key.split(".") + for i in k[:len(k)-1]: + c = conf[i] + c[k[len(k)-1]] = conf_overwrite[key] + + with open(cdir+"/config.json", "w") as configfile: + json.dump(conf, configfile, indent=1) + + return conf class Confirmation(): def __init__(self, client_pubkey, node_pubkey, raw): @@ -63,7 +104,8 @@ class Confirmation(): "out_base": self.out_base } -def get_peers(host, proxy=None, proxy_onion_only=False): +def get_peers(conf, host, proxy=None, proxy_onion_only=False): + header, content = utils.sdata(host, "GET", "/peers/info", proxy=proxy, proxy_onion_only=proxy_onion_only) try: @@ -74,7 +116,7 @@ def get_peers(host, proxy=None, proxy_onion_only=False): print("Error: bad UBJSON") return - peers = [utils.Peer(data["info"]), *[utils.Peer(p) for p in data["peers"]]] + peers = [utils.Peer(conf, data["info"]), *[utils.Peer(conf, p["raw"]) for p in data["peers"]]] #peers = [utils.Peer(p) for p in data["peers"]] print([p.pubkey for p in peers]) @@ -239,7 +281,7 @@ async def mix(db_txs, amount, base, sender, path, host, proxy=None, proxy_onion_ if not send_tx: print("Remind: no-tx mode") - if input("OK? [yn]: ").lower() == "y": + if input("OK? [yN]: ").lower() == "y": message = { "sender": sender.pubkey, "path": path, @@ -252,7 +294,10 @@ async def mix(db_txs, amount, base, sender, path, host, proxy=None, proxy_onion_ if send_tx: try: - await utils.send_transaction(sender, path[0], amount, utils.gen_comment(comment_seeds[0])) + authfile = "/tmp/gmixer-client-authfile-"+secrets.token_urlsafe(8) + sender.save_seedhex_file(authfile) + #await utils.send_transaction(sender, path[0], amount, utils.gen_comment(comment_seeds[0])) + utils.send_transaction(authfile, path[0], amount, utils.gen_comment(comment_seeds[0])) message["sent"] = True db_txs.put(comment_seeds[0][1], PublicKey(sender.pubkey).encrypt_seal(ubjson.dumpb(message))) @@ -262,10 +307,10 @@ async def mix(db_txs, amount, base, sender, path, host, proxy=None, proxy_onion_ print("Error when sending tx: " + str(e)) return -async def main(db_txs, host, receiver, amount=1000, layers=3, proxy=None, proxy_onion_only=False, send_tx=True): +async def main(conf, db_txs, host, receiver, amount=1000, layers=3, proxy=None, proxy_onion_only=False, send_tx=True): if amount < 100: print("!! Warning !!\nYou are going to send less than 1.00, all this money will be destroyed by Duniter.\nPlease always send 1.00 or more.") - if input("Do it anyway? [yn]: ").lower() != "y": + if input("Do it anyway? [yN]: ").lower() != "y": return print("IDs of the expeditor account:") @@ -273,10 +318,10 @@ async def main(db_txs, host, receiver, amount=1000, layers=3, proxy=None, proxy_ password = getpass.getpass("Psw: ") keys = SigningKey.from_credentials(salt, password) # sender print(keys.pubkey) - if input("Is that the right pubkey? [yn]: ").lower() != "y": + if input("Is that the right pubkey? [yN]: ").lower() != "y": return - peers = get_peers(host, proxy, proxy_onion_only) + peers = get_peers(conf, host, proxy, proxy_onion_only) if peers == None: print("Error getting peer list") exit(1) @@ -287,7 +332,7 @@ async def main(db_txs, host, receiver, amount=1000, layers=3, proxy=None, proxy_ exit(1) for peer in path: print(peer) - if input("OK? [yn]: ").lower() == "y": + if input("OK? [yN]: ").lower() == "y": break await mix(db_txs, amount, 0, keys, path, host1, proxy, proxy_onion_only, send_tx) @@ -303,7 +348,7 @@ Options: -h <host> <port> host for sync peer list -p <host> <port> SOCKS5 proxy --onion use proxy only when connecting to .onion - -d <path> config directory (default ~/.gmixer) + -d <path> config directory (default ~/.config/gmixer-client) --no-tx Do not send transaction (for debug) Example: @@ -317,6 +362,8 @@ python3 client.py -h svetsae7j3usrycn.onion 10951 -r 78ZwwgpgdH5uLZLbThUQH7LKwPg DIR = DIR[:len(DIR)-1] # Remove last slash os.makedirs(DIR, exist_ok=True) + conf = read_config(DIR) + receiver = utils.getargv("-r", "") if receiver == "": print("Error: No receiver set (try option --help)") @@ -336,4 +383,4 @@ python3 client.py -h svetsae7j3usrycn.onion 10951 -r 78ZwwgpgdH5uLZLbThUQH7LKwPg db_txs = plyvel.DB(DIR+"/client_db_txs", create_if_missing=True) - asyncio.get_event_loop().run_until_complete(main(db_txs, (host, port), receiver, amount, layers, proxy, proxy_onion_only, send_tx)) + asyncio.get_event_loop().run_until_complete(main(conf, db_txs, (host, port), receiver, amount, layers, proxy, proxy_onion_only, send_tx)) diff --git a/server.py b/server.py index f4e6e43..0324834 100644 --- a/server.py +++ b/server.py @@ -38,7 +38,7 @@ in_ = something that a node sent to me out_ = something I'm sending to a node """ -DIR = "~/.gmixer" +DIR = "~/.config/gmixer" BIND_HOST = socket.gethostname() try: BIND_HOST = socket.gethostbyname(BIND_HOST) @@ -716,7 +716,8 @@ class ClientThread(Thread): random.shuffle(txs) for tx in txs: try: - await utils.send_transaction(self.keys, tx.receiver_pubkey, tx.out_amount, utils.gen_comment(tx.out_seeds)) + #await utils.send_transaction(self.keys, tx.receiver_pubkey, tx.out_amount, utils.gen_comment(tx.out_seeds)) + utils.send_transaction(DIR+"/authfile", tx.receiver_pubkey, tx.out_amount, utils.gen_comment(tx.out_seeds)) tx.tx_sent = True tx.export_ubjson(self.db_txs) except socket.timeout: @@ -881,6 +882,9 @@ def main(): keys = SigningKey.from_credentials(salt, password) utils.logprint("Pubkey: "+keys.pubkey, utils.LOG_INFO) + # Generate authfile + keys.save_seedhex_file(DIR+"/authfile") + # Generate peer info local_peer = utils.Peer.generate(conf, keys, {}) @@ -904,6 +908,12 @@ def main(): serverThread.join() clientThread.join() + # Wipe authfile + f = open(DIR+"/authfile", "w") + f.write(secrets.token_hex(32)) + f.close() + os.remove(DIR+"/authfile") + # Save utils.save_peers(db_peers, peers) db_peers.close() @@ -1033,7 +1043,7 @@ Options:\n\ --help Display help\n\ \n\ -d <path> Change config & data dir\n\ - default: ~/.gmixer\n\ + default: ~/.config/gmixer\n\ -v Verbose\n\ -P Auto set public address (overwrites config)\n\ -g (with -i or -I only) Generate identity signature\n\ diff --git a/utils.py b/utils.py index 17f5088..91b68cd 100644 --- a/utils.py +++ b/utils.py @@ -17,7 +17,7 @@ along with ÄžMixer-py. If not, see <https://www.gnu.org/licenses/>. """ -import sys, os, re, socket, time, secrets, hashlib, base64, asyncio +import sys, os, re, socket, time, secrets, hashlib, base64, asyncio, subprocess import socks import ubjson import libnacl.sign @@ -275,8 +275,11 @@ async def check_idty(bma_endpoints:list, pubkey:str): await client.close() return "identities" in result and len(result["identities"]) > 0 and result["identities"][0]["pubkey"] == pubkey and result["identities"][0]["membershipExpiresIn"] > 0 -async def send_transaction(sender_keys:SigningKey, receiver_pubkey:str, amount:int, comment:str): +#async def send_transaction(sender_keys:SigningKey, receiver_pubkey:str, amount:int, comment:str): #sender_amount = silkaj.money.get_amount_from_pubkey(sender_keys.pubkey)[0] #assert sender_amount >= amount, "not enough money" - await silkaj.tx.handle_intermediaries_transactions(sender_keys, sender_keys.pubkey, amount, [receiver_pubkey], comment) + #await silkaj.tx.handle_intermediaries_transactions(sender_keys, sender_keys.pubkey, amount, [receiver_pubkey], comment) + +def send_transaction(authfile:str, receiver_pubkey:str, amount:int, comment:str): + subprocess.Popen(["silkaj", "--auth-file", "--file", authfile, "tx", "--output", receiver_pubkey, "--amount", str(amount/100), "--comment", comment, "-y"]) -- GitLab