From 8c3ae67fe22950030f506ee5e64beb170f26cf1c Mon Sep 17 00:00:00 2001 From: Tortue95 Date: Tue, 21 Mar 2017 11:40:23 +0100 Subject: [PATCH 1/3] [enh] add transaction feature: - authentication feature: - scrypt - seed - authfile - pubkey money amount --- README.md | 6 +- requirements.txt | 2 + src/auth.py | 64 ++++++++++++++ src/commands.py | 79 +++++++++++++++++ src/network_tools.py | 15 ++++ src/silkaj.py | 74 ++++++++++++---- src/tools.py | 172 ++++++++++++++++++++++++++++++++++-- src/tx.py | 201 +++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 588 insertions(+), 25 deletions(-) create mode 100644 src/auth.py create mode 100644 src/tx.py diff --git a/README.md b/README.md index 525c2fc..ae5923b 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ Silkaj is based on Python dependencies: - [Tabulate](https://bitbucket.org/astanin/python-tabulate/overview): to display charts. - [Commandlines](https://github.com/chrissimpkins/commandlines): to parse command and sub-commands. +- [PyNaCl](https://github.com/pyca/pynacl/): Cryptography (NaCl) library. +- [scrypt](https://bitbucket.org/mhallin/py-scrypt): scrypt key derivation function. #### From pip ```bash @@ -28,7 +30,7 @@ sudo (apt-get or dnf) install python-tabulate python-ipaddress ``` ### Integrate it on a dropdown menu to the panel -Under GNOME Shell, with [Argos](https://github.com/p-e-w/argos) extension: +Under GNOME Shell, with [Argos](https://github.com/p-e-w/argos) extension: - [Install Argos](https://github.com/p-e-w/argos#installation) - Put inside `~/.config/argos/silkaj.30s.sh`: @@ -60,6 +62,8 @@ Argos should the script and reload it every 30 seconds. ## Features - Currency information +- Show amount of account +- Send transaction - Proof-of-Work difficulties: Σ diffi, Π diffi, match - network information tab: - endpoints, ip6, ip4, domain name and port. Handle nodes sharing same pubkey. diff --git a/requirements.txt b/requirements.txt index ecd38c5..9ef73f8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ commandlines ipaddress tabulate +pynacl +scrypt diff --git a/src/auth.py b/src/auth.py new file mode 100644 index 0000000..ea488e0 --- /dev/null +++ b/src/auth.py @@ -0,0 +1,64 @@ +from tools import * +import getpass +import os + +def auth_method(c): + if c.contains_switches('auth-scrypt'): + return auth_by_scrypt() + if c.contains_switches('auth-seed'): + return auth_by_seed() + if c.contains_switches('auth-file'): + return auth_by_auth_file(c) + print("Error: no authentication method") + exit() + +def generate_auth_file(c): + if c.contains_definitions('file'): + file = c.get_definition('file') + else: + file = "authfile" + seed = auth_method(c) + with open(file, "w") as f: + f.write(seed) + print("Authfile generated for the public key: ", get_publickey_from_seed(seed)) + +def auth_by_auth_file(c): + if c.contains_definitions('file'): + file = c.get_definition('file') + else: + file = "authfile" + if not os.path.isfile(file): + print("Error: the file \""+file+"\" does not exist") + exit() + with open(file) as f: + seed = f.read() + regex = re.compile('^[0-9a-fA-F]{64}$') + if not re.search(regex, seed): + print("Error: the format of the file is invalid") + exit() + return seed + +def auth_by_seed(): + seed = input("Please enter your seed on hex format:") + regex = re.compile('^[0-9a-fA-F]{64}$') + if not re.search(regex, seed): + print("Error: the format of the seed is invalid") + exit() + return seed + + +def auth_by_scrypt(): + salt = input("Please enter your Scrypt Salt (Secret identifier):") + password = getpass.getpass("Please enter your Scrypt password (masked):") + scrypt_param = input("Please enter your Scrypt parameters (N,r,p): [4096,16,1]") + if not scrypt_param: + scrypt_param = "4096,16,1" + scrypt_param_splited= scrypt_param.split(",") + n=int(scrypt_param_splited[0]) + r=int(scrypt_param_splited[1]) + p=int(scrypt_param_splited[2]) + if(n <= 0 or n > 65536 or r <= 0 or r > 512 or p <= 0 or p > 32): + print("Error: the values of Scrypt parameters are not good") + + seed = get_seed_from_scrypt(salt,password, n, r, p) + return seed diff --git a/src/commands.py b/src/commands.py index 91a57c7..e2709e6 100644 --- a/src/commands.py +++ b/src/commands.py @@ -5,6 +5,8 @@ from operator import itemgetter from network_tools import * +from tx import * +from auth import * from tools import * def currency_info(ep): @@ -165,6 +167,83 @@ def list_issuers(ep, nbr, last): print("from {0} issuers\n{1}".format(len(list_issued), tabulate(sorted_list, headers="keys", tablefmt="orgtbl", floatfmt=".1f", stralign="center"))) + +def cmd_amount(ep, c): + if c.contains_definitions('pubkey'): + pubkey = c.get_definition('pubkey') + pubkey = check_public_key(pubkey) + else: + seed = auth_method(c) + pubkey = get_publickey_from_seed(seed) + + show_amount_from_pubkey(ep, pubkey) + + + +def cmd_transaction(ep, c): + seed = auth_method(c) + + if not (c.contains_definitions('amount') or c.contains_definitions('amountDU')): + print("--amount or --amountDU is not set") + exit() + if not c.contains_definitions('output'): + print("--output is not set") + exit() + + if c.contains_definitions('amount'): + amount = int(float(c.get_definition('amount'))*100) + if c.contains_definitions('amountDU'): + du = get_last_du_value(ep) + amount = int(float(c.get_definition('amountDU'))*du) + + output = c.get_definition('output') + + if c.contains_definitions('comment'): + comment = c.get_definition('comment') + else: + comment = "" + + if c.contains_switches('allSources'): + allSources = True + else: + allSources = False + + if c.contains_definitions('outputBackChange'): + outputBackChange = c.get_definition('outputBackChange') + else: + outputBackChange = None + + generate_and_send_transaction(ep, seed, amount, output, comment, allSources, outputBackChange) + + +def show_amount_from_pubkey(ep, pubkey): + + value = get_amount_from_pubkey(ep, pubkey) + totalAmountInput = value[0] + amount = value[1] + #output + DUvalue = get_last_du_value(ep) + current_blk = get_current_block(ep) + currency_name = str(current_blk["currency"]) + + + if totalAmountInput-amount != 0: + print("Blockchain:") + print("-----------") + print("Relative =", round(amount/DUvalue,2), "DU", currency_name) + print("Quantitative =", round(amount/100,2), currency_name+"\n") + + print("Pending Transaction:") + print("--------------------") + print("Relative =", round((totalAmountInput-amount)/DUvalue,2), "DU", currency_name) + print("Quantitative =", round((totalAmountInput-amount)/100,2), currency_name +"\n") + + + print("Total Ammount of : " + pubkey) + print("----------------------------------------------------------------") + print("Total Relative =", round(totalAmountInput/DUvalue,2), "DU", currency_name) + print("Total Quantitative =", round(totalAmountInput/100,2), currency_name+"\n") + def argos_info(ep): info_type = ["newcomers", "certs", "actives", "leavers", "excluded", "ud", "tx"] pretty_names = {'g1': 'Ğ1', 'gtest': 'Ğtest'} diff --git a/src/network_tools.py b/src/network_tools.py index 010cdae..8363c51 100644 --- a/src/network_tools.py +++ b/src/network_tools.py @@ -93,6 +93,21 @@ def request(ep, path): encoding = response.info().get_content_charset('utf8') return (json.loads(response.read().decode(encoding))) +def post_request(ep, path, postdata): + address = best_node(ep, 0) + if address is None: return (address) + url = "http://" + ep[address] + ":" + ep["port"] + "/" + path + if ep["port"] == "443": + url = "https://" + ep[address] + "/" + path + request = urllib.request.Request(url,bytes(postdata, 'utf-8')) + try: + response = urllib.request.urlopen(request) + except urllib.error.URLError as e: + print(e) + exit() + encoding = response.info().get_content_charset('utf8') + return (json.loads(response.read().decode(encoding))) + def best_node(ep, main): s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) addresses, port = {"ip6", "ip4", "domain"}, int(ep["port"]) diff --git a/src/silkaj.py b/src/silkaj.py index 9c5d67d..56c262c 100755 --- a/src/silkaj.py +++ b/src/silkaj.py @@ -1,51 +1,89 @@ #!/usr/bin/env python3 +# -*- coding: utf-8 -*- import sys from commandlines import Command - from commands import * + +def usage(): + print("Silkaj: command line Duniter client \ + \n\nhelp: -h, --help, --usage \ + \nversion: -v, --version \ + \n \ + \nCustom endpoint with option `-p` and :\ + \n \ + \nCommands: \ + \n - info: Display information about currency \ + \n \ + \n - amount: Get amount of one account \ + \n --pubkey= | --auth-scrypt | --auth-seed | --auth-file\ + \n \ + \n - transaction: Send transaction\ + \n --auth-scrypt | --auth-seed | --auth-file [--file=]\ + \n --amountDU= | --amount=\ + \n --output=[:checksum] \ + \n [--comment=] \ + \n [--allSources] \ + \n [--outputBackChange=] \ + \n \ + \n - network: Display current network with many information \ + \n --discover Discover all network (could take a while) \ + \n \ + \n - diffi: list proof-of-work difficulty to generate next block \ + \n \ + \n - issuers n: display last n issuers (`0` for all blockchain) \ + \n last issuers are displayed under n <= 30. To force display last ones, use `--last` option\ + \n \ + \n - argos: display information about currency formated for Argos or BitBar \ + \n \ + \n - generate_auth_file : Generate file to store the seed of the account\ + \n --auth-scrypt | --auth-seed \ + \n [--file=]") + exit() + + def cli(): # ep: endpoint, node's network interface ep, c = dict(), Command() - subcmd = ["info", "diffi", "network", "issuers", "argos"] + subcmd = ["info", "diffi", "network", "issuers", "argos", "amount", "transaction", "generate_auth_file"] if c.is_help_request() or c.is_usage_request() or c.subcmd not in subcmd: usage(); exit() if c.is_version_request(): print("silkaj 0.1.0"); exit() ep["domain"], ep["port"] = "duniter.org", "10901" try: ep["domain"], ep["port"] = c.get_definition('p').rsplit(':', 1) except: - print("Fallback to default node {}:{}\nCause: no specifed node, node not reachable or parsing issue." + print("Fallback to default node: <{}:{}>" .format(ep["domain"], ep["port"]), file=sys.stderr) if ep["domain"].startswith('[') and ep["domain"].endswith(']'): ep["domain"] = ep["domain"][1:-1] return (ep, c) + def manage_cmd(ep, c): if c.subcmd == "info": currency_info(ep) + elif c.subcmd == "diffi": difficulties(ep) + elif c.subcmd == "network": network_info(ep, c.contains_switches("discover")) + elif c.subcmd == "issuers" and c.subsubcmd and int(c.subsubcmd) >= 0: list_issuers(ep, int(c.subsubcmd), c.contains_switches('last')) + elif c.subcmd == "argos": - argos_info(ep) + argos_info(ep) + + elif c.subcmd == "amount" and c.subsubcmd: + cmd_amount(ep, c) + + elif c.subcmd == "transaction": + cmd_transaction(ep, c) + + elif c.subcmd == "generate_auth_file": + generate_auth_file(c) -def usage(): - print("Silkaj: command line Duniter client \ - \n\nhelp: -h, --help, --usage \ - \nversion: -v, --version \ - \ncommands: \ - \n - info: display information about currency \ - \n - diffi: list proof-of-work difficulty to generate next block \ - \n - network: display current network with many information \ - \n - `--discover` option to discover all network (could take a while) \ - \n - issuers n: display last n issuers (`0` for all blockchain) \ - \n - last issuers are displayed under n ≤ 30. To force display last ones, use `--last` option \ - \n - argos: display information about currency formated for Argos or BitBar \ - \ncustom endpoint with option `-p` and :") - exit() if __name__ == '__main__': ep, c = cli() diff --git a/src/tools.py b/src/tools.py index e1ebc1a..aeb36f1 100644 --- a/src/tools.py +++ b/src/tools.py @@ -1,4 +1,10 @@ import datetime +import nacl.encoding +import nacl.signing +import nacl.hash +import scrypt +import re +import sys from network_tools import * @@ -8,17 +14,171 @@ def convert_time(timestamp, kind): elif kind == "hour": if ts >= 3600: pattern = "%H:%M:%S" else: pattern = "%M:%S" - return (datetime.datetime.fromtimestamp(ts).strftime(pattern)) + return datetime.datetime.fromtimestamp(ts).strftime(pattern) + def get_uid_from_pubkey(ep, pubkey): i, results = 0, request(ep, "wot/lookup/" + pubkey)["results"] - if results == None: return(None) - while (i < len(results)): + while i < len(results): if results[i]["uids"][0]["uid"] != pubkey: - return (results[i]["uids"][0]["uid"]) + return results[i]["uids"][0]["uid"] i+=1 + def get_current_block(ep): current_blk = request(ep, "blockchain/current") - if current_blk is None: return (None) - else: return (current_blk) + return current_blk + + +def get_seed_from_scrypt(salt,password, N=4096, r=16, p=1): + seed = scrypt.hash(password,salt,N,r,p,32) + seedhex = nacl.encoding.HexEncoder.encode(seed).decode("utf-8") + return seedhex + + +def sign_document_from_seed(document,seed): + seed = bytes(seed, 'utf-8') + signing_key = nacl.signing.SigningKey(seed,nacl.encoding.HexEncoder) + signed = signing_key.sign(bytes(document, 'utf-8')) + signed_b64 = nacl.encoding.Base64Encoder.encode(signed.signature).decode("utf-8") + return signed_b64 + + +def get_publickey_from_seed(seed): + seed = bytes(seed, 'utf-8') + seed = nacl.encoding.HexEncoder.decode(seed) + public_key, secret_key = nacl.bindings.crypto_sign_seed_keypair(seed) + return b58_encode(public_key) + + +def check_public_key(pubkey): + regex = re.compile('^[1-9A-HJ-NP-Za-km-z]{43,44}$') + regex_checksum = re.compile('^[1-9A-HJ-NP-Za-km-z]{43,44}:[1-9A-HJ-NP-Za-km-z]{3}$') + if re.search(regex, pubkey): + return pubkey + if re.search(regex_checksum, pubkey): + pubkey, checksum = pubkey.split(":") + pubkey_byte=b58_decode(pubkey) + checksum_calculed = b58_encode(nacl.hash.sha256(nacl.hash.sha256(pubkey_byte,nacl.encoding.RawEncoder),nacl.encoding.RawEncoder))[:3] + if checksum_calculed == checksum: + return pubkey + else: + print("error: bad checksum of the public key") + exit() + + print("Error: the format of the public key is invalid") + exit() + + +def get_amount_from_pubkey(ep, pubkey): + sources = request(ep, "tx/sources/" + pubkey)["sources"] + + listinput = [] + amount = 0 + for source in sources: + amount += source["amount"]*10**source["base"] + listinput.append(str(source["amount"])+":"+str(source["base"])+":"+str(source["type"])+":"+str(source["identifier"])+":"+str(source["noffset"])) + + + #pending source + history = request(ep, "tx/history/"+ pubkey+"/pending")["history"] + pendings = history["sending"] + history["receiving"] + history["pending"] + #print(pendings) + + + current_blk = get_current_block(ep) + last_block_number = int(current_blk["number"]) + + #add pending output + for pending in pendings: + blockstamp = pending["blockstamp"] + block_number = int(blockstamp.split("-")[0]) + #if it's not an old transaction (bug in mirror node) + if block_number >= last_block_number-3: + identifier = pending["hash"] + i = 0 + for output in pending["outputs"]: + outputsplited = output.split(":") + if outputsplited[2] == "SIG("+pubkey+")": + inputgenerated=str(outputsplited[0])+":"+str(outputsplited[1])+":T:"+identifier+":"+str(i) + if inputgenerated not in listinput: + listinput.append(inputgenerated) + i += 1 + + #remove input already used + for pending in pendings: + blockstamp = pending["blockstamp"] + block_number = int(blockstamp.split("-")[0]) + #if it's not an old transaction (bug in mirror node) + if block_number >= last_block_number-3: + for input in pending["inputs"]: + if input in listinput: + listinput.remove(input) + + + + totalAmountInput = 0 + for input in listinput: + inputsplit = input.split(":") + totalAmountInput += int(inputsplit[0])*10**int(inputsplit[1]) + + return int(totalAmountInput),int(amount) + + +def get_last_du_value(ep): + blockswithud = request(ep, "blockchain/with/ud")["result"] + NBlastDUblock=blockswithud["blocks"][-1] + lastDUblock = request(ep, "blockchain/block/" + str(NBlastDUblock)) + return lastDUblock["dividend"]*10**lastDUblock["unitbase"] + + + +b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' +def b58_encode(b): + + # Convert big-endian bytes to integer + n = int('0x0' + nacl.encoding.HexEncoder.encode(b).decode('utf8'), 16) + + # Divide that integer into bas58 + res = [] + while n > 0: + n, r = divmod (n, 58) + res.append(b58_digits[r]) + res = ''.join(res[::-1]) + + # Encode leading zeros as base58 zeros + czero = b'\x00' + if sys.version > '3': + # In Python3 indexing a bytes returns numbers, not characters. + czero = 0 + pad = 0 + for c in b: + if c == czero: pad += 1 + else: break + return b58_digits[0] * pad + res + +def b58_decode(s): + if not s: + return b'' + + # Convert the string to an integer + n = 0 + for c in s: + n *= 58 + if c not in b58_digits: + raise InvalidBase58Error('Character %r is not a valid base58 character' % c) + digit = b58_digits.index(c) + n += digit + + # Convert the integer to bytes + h = '%x' % n + if len(h) % 2: + h = '0' + h + res = nacl.encoding.HexEncoder.decode(h.encode('utf8')) + + # Add padding back. + pad = 0 + for c in s[:-1]: + if c == b58_digits[0]: pad += 1 + else: break + return b'\x00' * pad + res diff --git a/src/tx.py b/src/tx.py new file mode 100644 index 0000000..2581cf0 --- /dev/null +++ b/src/tx.py @@ -0,0 +1,201 @@ +from network_tools import * +from tools import * + +import re +import math +import time + +def generate_and_send_transaction(ep,seed,AmountTransfered,outputAddr,Comment = "",all_input = False,OutputbackChange = None): + + issuers = get_publickey_from_seed(seed) + + outputAddr = check_public_key(outputAddr) + if OutputbackChange: + OutputbackChange = check_public_key(OutputbackChange) + + totalamount = get_amount_from_pubkey(ep,issuers)[0] + if totalamount < AmountTransfered: + print("the account: "+ issuers +" don't have enough money for this transaction") + exit() + + while True: + listinput_and_amount = get_list_input_for_transaction(ep,issuers,AmountTransfered,all_input) + transactionintermediaire = listinput_and_amount[2] + + if transactionintermediaire: + totalAmountInput = listinput_and_amount[1] + print("Generate Change Transaction") + print(" - From: " + issuers) + print(" - To: " + issuers) + print(" - Amount: "+ str(totalAmountInput/100)) + transaction = generate_transaction_document(ep,issuers,totalAmountInput,listinput_and_amount,issuers,"Change operation") + transaction += sign_document_from_seed(transaction,seed)+"\n" + retour = post_request(ep, "tx/process", "transaction="+urllib.parse.quote_plus(transaction) ) + print("Change Transaction successfully sent.") + time.sleep(1) #wait 1 second before sending a new transaction + + else: + print("Generate Transaction:") + print(" - From: " + issuers) + print(" - To: " + outputAddr) + if all_input: + print(" - Amount: "+ str(listinput_and_amount[1]/100)) + else: + print(" - Amount: "+ str(AmountTransfered/100)) + transaction = generate_transaction_document(ep,issuers,AmountTransfered,listinput_and_amount,outputAddr,Comment,OutputbackChange) + transaction += sign_document_from_seed(transaction,seed)+"\n" + + retour = post_request(ep, "tx/process", "transaction="+urllib.parse.quote_plus(transaction)) + print("Transaction successfully sent.") + break + + +def generate_transaction_document(ep,issuers,AmountTransfered,listinput_and_amount,outputaddr,Comment = "",OutputbackChange = None): + + #check comment + checkComment(Comment) + outputAddr = check_public_key(outputaddr) + if OutputbackChange: + OutputbackChange = check_public_key(OutputbackChange) + + listinput = listinput_and_amount[0] + totalAmountInput = listinput_and_amount[1] + + current_blk = get_current_block(ep) + currency_name = str(current_blk["currency"]) + blockstamp_current = str(current_blk["number"])+"-"+str(current_blk["hash"]) + curentUnitBase = current_blk["unitbase"] + + if not OutputbackChange: + OutputbackChange = issuers + + #if it's not a foreign exchange transaction, we remove units after 2 digits after the decimal point. + if issuers != outputaddr: + AmountTransfered = (AmountTransfered // 10**curentUnitBase) * 10**curentUnitBase + + #Generate output + ################ + listoutput = [] + #Outputs to receiver (if not himself) + rest = AmountTransfered + unitbase = curentUnitBase + while rest > 0: + outputAmount = truncBase(rest,unitbase) + rest -= outputAmount + if outputAmount > 0: + outputAmount = int(outputAmount / math.pow(10, unitbase)) + listoutput.append(str(outputAmount)+":"+str(unitbase)+":SIG("+outputaddr+")") + unitbase = unitbase - 1 + + #Outputs to himself + unitbase = curentUnitBase + rest = totalAmountInput - AmountTransfered + while rest > 0: + outputAmount = truncBase(rest,unitbase) + rest -= outputAmount + if outputAmount > 0: + outputAmount = int(outputAmount / math.pow(10, unitbase)) + listoutput.append(str(outputAmount)+":"+str(unitbase)+":SIG("+OutputbackChange+")") + unitbase = unitbase - 1 + + #Generate transaction document + ############################## + + transaction_document = "Version: 10\n" + transaction_document += "Type: Transaction\n" + transaction_document += "Currency: "+currency_name+"\n" + transaction_document += "Blockstamp: "+blockstamp_current+"\n" + transaction_document += "Locktime: 0\n" + transaction_document += "Issuers:\n" + transaction_document += issuers+"\n" + transaction_document += "Inputs:\n" + for input in listinput: + transaction_document += input+"\n" + transaction_document += "Unlocks:\n" + for i in range(0,len(listinput)): + transaction_document += str(i)+":SIG(0)\n" + transaction_document += "Outputs:\n" + for output in listoutput: + transaction_document += output+"\n" + transaction_document += "Comment: "+Comment+"\n" + + return transaction_document + + +def get_list_input_for_transaction(ep,pubkey,TXamount,allinput = False): + #real source in blockchain + sources = request(ep, "tx/sources/" + pubkey)["sources"] + if sources == None: return None + listinput = [] + + for source in sources: + listinput.append(str(source["amount"])+":"+str(source["base"])+":"+str(source["type"])+":"+str(source["identifier"])+":"+str(source["noffset"])) + + #pending source + history = request(ep, "tx/history/"+ pubkey+"/pending")["history"] + pendings = history["sending"] + history["receiving"] + history["pending"] + + current_blk = get_current_block(ep) + last_block_number = int(current_blk["number"]) + + #add pending output + for pending in pendings: + blockstamp = pending["blockstamp"] + block_number = int(blockstamp.split("-")[0]) + #if it's not an old transaction (bug in mirror node) + if block_number >= last_block_number-3: + identifier = pending["hash"] + i = 0 + for output in pending["outputs"]: + outputsplited = output.split(":") + if outputsplited[2] == "SIG("+pubkey+")": + inputgenerated=str(outputsplited[0])+":"+str(outputsplited[1])+":T:"+identifier+":"+str(i) + if inputgenerated not in listinput: + listinput.append(inputgenerated) + i += 1 + + #remove input already used + for pending in pendings: + blockstamp = pending["blockstamp"] + block_number = int(blockstamp.split("-")[0]) + #if it's not an old transaction (bug in mirror node) + if block_number >= last_block_number-3: + for input in pending["inputs"]: + if input in listinput: + listinput.remove(input) + + #generate final list source + listinputfinal = [] + totalAmountInput = 0 + transactionintermediaire = False + for input in listinput: + listinputfinal.append(input) + inputsplit = input.split(":") + totalAmountInput += int(inputsplit[0])*10**int(inputsplit[1]) + TXamount -= int(inputsplit[0])*10**int(inputsplit[1]) + #if more 40 sources, it's an intermediate transaction + if len(listinputfinal) >= 40: + transactionintermediaire = True + break + if TXamount <= 0 and not allinput: + break + if TXamount > 0 and not transactionintermediaire: + print("you don't have enough money") + exit() + return(listinputfinal,totalAmountInput, transactionintermediaire) + + +def checkComment(Comment): + if len(Comment) > 255: + print("Error: Comment is too long") + exit() + regex = re.compile('^[0-9a-zA-Z\ \-\_\:\/\;\*\[\]\(\)\?\!\^\+\=\@\&\~\#\{\}\|\\\<\>\%\.]*$') + if not re.search(regex, Comment): + print("Error: the format of the comment is invalid") + exit() + +def truncBase(amount, base): + pow = math.pow(10, base) + if amount < pow: + return 0 + return math.trunc(amount / pow) * pow From 2ea744c4c9c6269fd34ea44c71bfc9ff18af6f33 Mon Sep 17 00:00:00 2001 From: Moul Date: Mon, 27 Mar 2017 10:11:15 +0200 Subject: [PATCH 2/3] [mod] code typo review. - add space after imput prompt. --- README.md | 2 +- src/auth.py | 18 ++++++------- src/commands.py | 19 +++++++------- src/tools.py | 35 +++++++++++-------------- src/tx.py | 70 ++++++++++++++++++++++++------------------------- 5 files changed, 70 insertions(+), 74 deletions(-) diff --git a/README.md b/README.md index ae5923b..f3f320f 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ sudo (apt-get or dnf) install python-tabulate python-ipaddress ``` ### Integrate it on a dropdown menu to the panel -Under GNOME Shell, with [Argos](https://github.com/p-e-w/argos) extension: +Under GNOME Shell, with [Argos](https://github.com/p-e-w/argos) extension: - [Install Argos](https://github.com/p-e-w/argos#installation) - Put inside `~/.config/argos/silkaj.30s.sh`: diff --git a/src/auth.py b/src/auth.py index ea488e0..f8a46ae 100644 --- a/src/auth.py +++ b/src/auth.py @@ -28,7 +28,7 @@ def auth_by_auth_file(c): else: file = "authfile" if not os.path.isfile(file): - print("Error: the file \""+file+"\" does not exist") + print("Error: the file \"" + file + "\" does not exist") exit() with open(file) as f: seed = f.read() @@ -39,7 +39,7 @@ def auth_by_auth_file(c): return seed def auth_by_seed(): - seed = input("Please enter your seed on hex format:") + seed = input("Please enter your seed on hex format: ") regex = re.compile('^[0-9a-fA-F]{64}$') if not re.search(regex, seed): print("Error: the format of the seed is invalid") @@ -48,17 +48,17 @@ def auth_by_seed(): def auth_by_scrypt(): - salt = input("Please enter your Scrypt Salt (Secret identifier):") - password = getpass.getpass("Please enter your Scrypt password (masked):") + salt = input("Please enter your Scrypt Salt (Secret identifier): ") + password = getpass.getpass("Please enter your Scrypt password (masked): ") scrypt_param = input("Please enter your Scrypt parameters (N,r,p): [4096,16,1]") if not scrypt_param: scrypt_param = "4096,16,1" scrypt_param_splited= scrypt_param.split(",") - n=int(scrypt_param_splited[0]) - r=int(scrypt_param_splited[1]) - p=int(scrypt_param_splited[2]) - if(n <= 0 or n > 65536 or r <= 0 or r > 512 or p <= 0 or p > 32): + n = int(scrypt_param_splited[0]) + r = int(scrypt_param_splited[1]) + p = int(scrypt_param_splited[2]) + if (n <= 0 or n > 65536 or r <= 0 or r > 512 or p <= 0 or p > 32): print("Error: the values of Scrypt parameters are not good") - seed = get_seed_from_scrypt(salt,password, n, r, p) + seed = get_seed_from_scrypt(salt, password, n, r, p) return seed diff --git a/src/commands.py b/src/commands.py index e2709e6..fe7a55a 100644 --- a/src/commands.py +++ b/src/commands.py @@ -179,7 +179,6 @@ def cmd_amount(ep, c): show_amount_from_pubkey(ep, pubkey) - def cmd_transaction(ep, c): seed = auth_method(c) @@ -191,10 +190,10 @@ def cmd_transaction(ep, c): exit() if c.contains_definitions('amount'): - amount = int(float(c.get_definition('amount'))*100) + amount = int(float(c.get_definition('amount')) * 100) if c.contains_definitions('amountDU'): du = get_last_du_value(ep) - amount = int(float(c.get_definition('amountDU'))*du) + amount = int(float(c.get_definition('amountDU')) * du) output = c.get_definition('output') @@ -230,19 +229,19 @@ def show_amount_from_pubkey(ep, pubkey): if totalAmountInput-amount != 0: print("Blockchain:") print("-----------") - print("Relative =", round(amount/DUvalue,2), "DU", currency_name) - print("Quantitative =", round(amount/100,2), currency_name+"\n") + print("Relative =", round(amount / DUvalue, 2), "DU", currency_name) + print("Quantitative =", round(amount / 100, 2), currency_name+"\n") print("Pending Transaction:") print("--------------------") - print("Relative =", round((totalAmountInput-amount)/DUvalue,2), "DU", currency_name) - print("Quantitative =", round((totalAmountInput-amount)/100,2), currency_name +"\n") + print("Relative =", round((totalAmountInput - amount) / DUvalue, 2), "DU", currency_name) + print("Quantitative =", round((totalAmountInput - amount) / 100, 2), currency_name + "\n") - print("Total Ammount of : " + pubkey) + print("Total amount of: " + pubkey) print("----------------------------------------------------------------") - print("Total Relative =", round(totalAmountInput/DUvalue,2), "DU", currency_name) - print("Total Quantitative =", round(totalAmountInput/100,2), currency_name+"\n") + print("Total Relative =", round(totalAmountInput / DUvalue, 2), "DU", currency_name) + print("Total Quantitative =", round(totalAmountInput / 100, 2), currency_name + "\n") def argos_info(ep): info_type = ["newcomers", "certs", "actives", "leavers", "excluded", "ud", "tx"] diff --git a/src/tools.py b/src/tools.py index aeb36f1..917fbba 100644 --- a/src/tools.py +++ b/src/tools.py @@ -30,8 +30,8 @@ def get_current_block(ep): return current_blk -def get_seed_from_scrypt(salt,password, N=4096, r=16, p=1): - seed = scrypt.hash(password,salt,N,r,p,32) +def get_seed_from_scrypt(salt, password, N=4096, r=16, p=1): + seed = scrypt.hash(password, salt, N, r, p, 32) seedhex = nacl.encoding.HexEncoder.encode(seed).decode("utf-8") return seedhex @@ -76,12 +76,12 @@ def get_amount_from_pubkey(ep, pubkey): listinput = [] amount = 0 for source in sources: - amount += source["amount"]*10**source["base"] - listinput.append(str(source["amount"])+":"+str(source["base"])+":"+str(source["type"])+":"+str(source["identifier"])+":"+str(source["noffset"])) + amount += source["amount"] * 10 ** source["base"] + listinput.append(str(source["amount"]) + ":" + str(source["base"]) + ":" + str(source["type"]) + ":" + str(source["identifier"]) + ":" + str(source["noffset"])) #pending source - history = request(ep, "tx/history/"+ pubkey+"/pending")["history"] + history = request(ep, "tx/history/" + pubkey + "/pending")["history"] pendings = history["sending"] + history["receiving"] + history["pending"] #print(pendings) @@ -94,43 +94,40 @@ def get_amount_from_pubkey(ep, pubkey): blockstamp = pending["blockstamp"] block_number = int(blockstamp.split("-")[0]) #if it's not an old transaction (bug in mirror node) - if block_number >= last_block_number-3: + if block_number >= last_block_number - 3: identifier = pending["hash"] i = 0 for output in pending["outputs"]: outputsplited = output.split(":") - if outputsplited[2] == "SIG("+pubkey+")": - inputgenerated=str(outputsplited[0])+":"+str(outputsplited[1])+":T:"+identifier+":"+str(i) + if outputsplited[2] == "SIG(" + pubkey + ")": + inputgenerated = str(outputsplited[0]) + ":" + str(outputsplited[1]) + ":T:" + identifier + ":" + str(i) if inputgenerated not in listinput: listinput.append(inputgenerated) i += 1 - #remove input already used - for pending in pendings: + #remove input already used + for pending in pendings: blockstamp = pending["blockstamp"] block_number = int(blockstamp.split("-")[0]) #if it's not an old transaction (bug in mirror node) - if block_number >= last_block_number-3: + if block_number >= last_block_number - 3: for input in pending["inputs"]: if input in listinput: listinput.remove(input) - - totalAmountInput = 0 for input in listinput: inputsplit = input.split(":") - totalAmountInput += int(inputsplit[0])*10**int(inputsplit[1]) + totalAmountInput += int(inputsplit[0]) * 10 ** int(inputsplit[1]) - return int(totalAmountInput),int(amount) + return int(totalAmountInput), int(amount) def get_last_du_value(ep): blockswithud = request(ep, "blockchain/with/ud")["result"] - NBlastDUblock=blockswithud["blocks"][-1] + NBlastDUblock = blockswithud["blocks"][-1] lastDUblock = request(ep, "blockchain/block/" + str(NBlastDUblock)) - return lastDUblock["dividend"]*10**lastDUblock["unitbase"] - + return lastDUblock["dividend"] * 10 ** lastDUblock["unitbase"] b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' @@ -139,7 +136,7 @@ def b58_encode(b): # Convert big-endian bytes to integer n = int('0x0' + nacl.encoding.HexEncoder.encode(b).decode('utf8'), 16) - # Divide that integer into bas58 + # Divide that integer into base58 res = [] while n > 0: n, r = divmod (n, 58) diff --git a/src/tx.py b/src/tx.py index 2581cf0..04095fd 100644 --- a/src/tx.py +++ b/src/tx.py @@ -5,7 +5,7 @@ import math import time -def generate_and_send_transaction(ep,seed,AmountTransfered,outputAddr,Comment = "",all_input = False,OutputbackChange = None): +def generate_and_send_transaction(ep, seed, AmountTransfered, outputAddr, Comment = "", all_input = False, OutputbackChange = None): issuers = get_publickey_from_seed(seed) @@ -15,11 +15,11 @@ def generate_and_send_transaction(ep,seed,AmountTransfered,outputAddr,Comment = totalamount = get_amount_from_pubkey(ep,issuers)[0] if totalamount < AmountTransfered: - print("the account: "+ issuers +" don't have enough money for this transaction") + print("the account: " + issuers + " don't have enough money for this transaction") exit() while True: - listinput_and_amount = get_list_input_for_transaction(ep,issuers,AmountTransfered,all_input) + listinput_and_amount = get_list_input_for_transaction(ep, issuers, AmountTransfered, all_input) transactionintermediaire = listinput_and_amount[2] if transactionintermediaire: @@ -27,10 +27,10 @@ def generate_and_send_transaction(ep,seed,AmountTransfered,outputAddr,Comment = print("Generate Change Transaction") print(" - From: " + issuers) print(" - To: " + issuers) - print(" - Amount: "+ str(totalAmountInput/100)) - transaction = generate_transaction_document(ep,issuers,totalAmountInput,listinput_and_amount,issuers,"Change operation") - transaction += sign_document_from_seed(transaction,seed)+"\n" - retour = post_request(ep, "tx/process", "transaction="+urllib.parse.quote_plus(transaction) ) + print(" - Amount: " + str(totalAmountInput / 100)) + transaction = generate_transaction_document(ep, issuers, totalAmountInput, listinput_and_amount,issuers, "Change operation") + transaction += sign_document_from_seed(transaction,seed) + "\n" + retour = post_request(ep, "tx/process", "transaction=" + urllib.parse.quote_plus(transaction)) print("Change Transaction successfully sent.") time.sleep(1) #wait 1 second before sending a new transaction @@ -39,18 +39,18 @@ def generate_and_send_transaction(ep,seed,AmountTransfered,outputAddr,Comment = print(" - From: " + issuers) print(" - To: " + outputAddr) if all_input: - print(" - Amount: "+ str(listinput_and_amount[1]/100)) + print(" - Amount: " + str(listinput_and_amount[1] / 100)) else: - print(" - Amount: "+ str(AmountTransfered/100)) - transaction = generate_transaction_document(ep,issuers,AmountTransfered,listinput_and_amount,outputAddr,Comment,OutputbackChange) - transaction += sign_document_from_seed(transaction,seed)+"\n" + print(" - Amount: " + str(AmountTransfered / 100)) + transaction = generate_transaction_document(ep, issuers, AmountTransfered, listinput_and_amount,outputAddr, Comment, OutputbackChange) + transaction += sign_document_from_seed(transaction,seed) + "\n" - retour = post_request(ep, "tx/process", "transaction="+urllib.parse.quote_plus(transaction)) + retour = post_request(ep, "tx/process", "transaction=" + urllib.parse.quote_plus(transaction)) print("Transaction successfully sent.") break -def generate_transaction_document(ep,issuers,AmountTransfered,listinput_and_amount,outputaddr,Comment = "",OutputbackChange = None): +def generate_transaction_document(ep, issuers, AmountTransfered, listinput_and_amount, outputaddr, Comment = "", OutputbackChange = None): #check comment checkComment(Comment) @@ -63,7 +63,7 @@ def generate_transaction_document(ep,issuers,AmountTransfered,listinput_and_amou current_blk = get_current_block(ep) currency_name = str(current_blk["currency"]) - blockstamp_current = str(current_blk["number"])+"-"+str(current_blk["hash"]) + blockstamp_current = str(current_blk["number"]) + "-" + str(current_blk["hash"]) curentUnitBase = current_blk["unitbase"] if not OutputbackChange: @@ -71,7 +71,7 @@ def generate_transaction_document(ep,issuers,AmountTransfered,listinput_and_amou #if it's not a foreign exchange transaction, we remove units after 2 digits after the decimal point. if issuers != outputaddr: - AmountTransfered = (AmountTransfered // 10**curentUnitBase) * 10**curentUnitBase + AmountTransfered = (AmountTransfered // 10 ** curentUnitBase) * 10 ** curentUnitBase #Generate output ################ @@ -84,7 +84,7 @@ def generate_transaction_document(ep,issuers,AmountTransfered,listinput_and_amou rest -= outputAmount if outputAmount > 0: outputAmount = int(outputAmount / math.pow(10, unitbase)) - listoutput.append(str(outputAmount)+":"+str(unitbase)+":SIG("+outputaddr+")") + listoutput.append(str(outputAmount) + ":" + str(unitbase) + ":SIG(" + outputaddr + ")") unitbase = unitbase - 1 #Outputs to himself @@ -95,7 +95,7 @@ def generate_transaction_document(ep,issuers,AmountTransfered,listinput_and_amou rest -= outputAmount if outputAmount > 0: outputAmount = int(outputAmount / math.pow(10, unitbase)) - listoutput.append(str(outputAmount)+":"+str(unitbase)+":SIG("+OutputbackChange+")") + listoutput.append(str(outputAmount) + ":" + str(unitbase) + ":SIG(" + OutputbackChange + ")") unitbase = unitbase - 1 #Generate transaction document @@ -103,36 +103,36 @@ def generate_transaction_document(ep,issuers,AmountTransfered,listinput_and_amou transaction_document = "Version: 10\n" transaction_document += "Type: Transaction\n" - transaction_document += "Currency: "+currency_name+"\n" - transaction_document += "Blockstamp: "+blockstamp_current+"\n" + transaction_document += "Currency: " + currency_name + "\n" + transaction_document += "Blockstamp: " + blockstamp_current + "\n" transaction_document += "Locktime: 0\n" transaction_document += "Issuers:\n" - transaction_document += issuers+"\n" + transaction_document += issuers + "\n" transaction_document += "Inputs:\n" for input in listinput: - transaction_document += input+"\n" + transaction_document += input + "\n" transaction_document += "Unlocks:\n" for i in range(0,len(listinput)): - transaction_document += str(i)+":SIG(0)\n" + transaction_document += str(i) + ":SIG(0)\n" transaction_document += "Outputs:\n" for output in listoutput: - transaction_document += output+"\n" - transaction_document += "Comment: "+Comment+"\n" + transaction_document += output + "\n" + transaction_document += "Comment: " + Comment + "\n" return transaction_document -def get_list_input_for_transaction(ep,pubkey,TXamount,allinput = False): +def get_list_input_for_transaction(ep, pubkey, TXamount, allinput = False): #real source in blockchain sources = request(ep, "tx/sources/" + pubkey)["sources"] if sources == None: return None listinput = [] for source in sources: - listinput.append(str(source["amount"])+":"+str(source["base"])+":"+str(source["type"])+":"+str(source["identifier"])+":"+str(source["noffset"])) + listinput.append(str(source["amount"]) + ":" + str(source["base"]) + ":" + str(source["type"]) + ":" + str(source["identifier"]) + ":" + str(source["noffset"])) #pending source - history = request(ep, "tx/history/"+ pubkey+"/pending")["history"] + history = request(ep, "tx/history/" + pubkey + "/pending")["history"] pendings = history["sending"] + history["receiving"] + history["pending"] current_blk = get_current_block(ep) @@ -143,23 +143,23 @@ def get_list_input_for_transaction(ep,pubkey,TXamount,allinput = False): blockstamp = pending["blockstamp"] block_number = int(blockstamp.split("-")[0]) #if it's not an old transaction (bug in mirror node) - if block_number >= last_block_number-3: + if block_number >= last_block_number - 3: identifier = pending["hash"] i = 0 for output in pending["outputs"]: outputsplited = output.split(":") if outputsplited[2] == "SIG("+pubkey+")": - inputgenerated=str(outputsplited[0])+":"+str(outputsplited[1])+":T:"+identifier+":"+str(i) + inputgenerated = str(outputsplited[0]) + ":" + str(outputsplited[1]) + ":T:" + identifier + ":" + str(i) if inputgenerated not in listinput: - listinput.append(inputgenerated) + listinput.append(inputgenerated) i += 1 #remove input already used - for pending in pendings: + for pending in pendings: blockstamp = pending["blockstamp"] block_number = int(blockstamp.split("-")[0]) #if it's not an old transaction (bug in mirror node) - if block_number >= last_block_number-3: + if block_number >= last_block_number - 3: for input in pending["inputs"]: if input in listinput: listinput.remove(input) @@ -171,8 +171,8 @@ def get_list_input_for_transaction(ep,pubkey,TXamount,allinput = False): for input in listinput: listinputfinal.append(input) inputsplit = input.split(":") - totalAmountInput += int(inputsplit[0])*10**int(inputsplit[1]) - TXamount -= int(inputsplit[0])*10**int(inputsplit[1]) + totalAmountInput += int(inputsplit[0]) * 10 ** int(inputsplit[1]) + TXamount -= int(inputsplit[0]) * 10 ** int(inputsplit[1]) #if more 40 sources, it's an intermediate transaction if len(listinputfinal) >= 40: transactionintermediaire = True @@ -182,7 +182,7 @@ def get_list_input_for_transaction(ep,pubkey,TXamount,allinput = False): if TXamount > 0 and not transactionintermediaire: print("you don't have enough money") exit() - return(listinputfinal,totalAmountInput, transactionintermediaire) + return (listinputfinal, totalAmountInput, transactionintermediaire) def checkComment(Comment): From 229ccdcb685c8ee0b86ef7ab49bbb5fbe46d2415 Mon Sep 17 00:00:00 2001 From: Moul Date: Mon, 27 Mar 2017 11:24:24 +0200 Subject: [PATCH 3/3] [mod] use english name for variable. --- src/tx.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tx.py b/src/tx.py index 04095fd..f6028bf 100644 --- a/src/tx.py +++ b/src/tx.py @@ -20,9 +20,9 @@ def generate_and_send_transaction(ep, seed, AmountTransfered, outputAddr, Commen while True: listinput_and_amount = get_list_input_for_transaction(ep, issuers, AmountTransfered, all_input) - transactionintermediaire = listinput_and_amount[2] + intermediatetransaction = listinput_and_amount[2] - if transactionintermediaire: + if intermediatetransaction: totalAmountInput = listinput_and_amount[1] print("Generate Change Transaction") print(" - From: " + issuers) @@ -167,7 +167,7 @@ def get_list_input_for_transaction(ep, pubkey, TXamount, allinput = False): #generate final list source listinputfinal = [] totalAmountInput = 0 - transactionintermediaire = False + intermediatetransaction = False for input in listinput: listinputfinal.append(input) inputsplit = input.split(":") @@ -175,14 +175,14 @@ def get_list_input_for_transaction(ep, pubkey, TXamount, allinput = False): TXamount -= int(inputsplit[0]) * 10 ** int(inputsplit[1]) #if more 40 sources, it's an intermediate transaction if len(listinputfinal) >= 40: - transactionintermediaire = True + intermediatetransaction = True break if TXamount <= 0 and not allinput: break - if TXamount > 0 and not transactionintermediaire: + if TXamount > 0 and not intermediatetransaction: print("you don't have enough money") exit() - return (listinputfinal, totalAmountInput, transactionintermediaire) + return listinputfinal, totalAmountInput, intermediatetransaction def checkComment(Comment):