async def cmd_amount(ctx, pubkeys): client = ClientInstance().client if not has_auth_method(): if not pubkeys: message_exit("You should specify one or many pubkeys") pubkey_list = list() for pubkey in pubkeys: if check_pubkey_format(pubkey): pubkey_list.append(validate_checksum(pubkey)) else: pubkey_list.append(pubkey) total = [0, 0] for pubkey in pubkey_list: inputs_balance = await get_amount_from_pubkey(pubkey) await show_amount_from_pubkey(pubkey, inputs_balance) total[0] += inputs_balance[0] total[1] += inputs_balance[1] if len(pubkey_list) > 1: await show_amount_from_pubkey("Total", total) else: key = auth_method() pubkey = key.pubkey await show_amount_from_pubkey(pubkey, await get_amount_from_pubkey(pubkey)) await client.close()
async def id_pubkey_correspondence(id_pubkey): client = ClientInstance().client # determine if id_pubkey is a pubkey checked_pubkey = is_pubkey_and_check(id_pubkey) if checked_pubkey: id_pubkey = checked_pubkey try: idty = await identity_of(id_pubkey) print( "{} public key corresponds to identity: {}".format( display_pubkey_and_checksum(id_pubkey), idty["uid"] ) ) except: message_exit("No matching identity") # if not ; then it is a uid else: pubkeys = await wot_lookup(id_pubkey) print("Public keys found matching '{}':\n".format(id_pubkey)) for pubkey in pubkeys: print("→", display_pubkey_and_checksum(pubkey["pubkey"]), end=" ") try: corresponding_id = await client(wot.identity_of, pubkey["pubkey"]) print("↔ " + corresponding_id["uid"]) except: print("") await client.close()
def auth_by_auth_file(ctx): """ Uses an authentication file to generate the key Authfile can either be: * A seed in hexadecimal encoding * PubSec format with public and private key in base58 encoding """ file = ctx.obj["AUTH_FILE_PATH"] authfile = Path(file) if not authfile.is_file(): message_exit('Error: the file "' + file + '" does not exist') filetxt = authfile.open("r").read() # two regural expressions for the PubSec format regex_pubkey = re.compile(PUBSEC_PUBKEY_PATTERN, re.MULTILINE) regex_signkey = re.compile(PUBSEC_SIGNKEY_PATTERN, re.MULTILINE) # Seed hexadecimal format if re.search(re.compile(SEED_HEX_PATTERN), filetxt): return SigningKey.from_seedhex_file(file) # PubSec format elif re.search(regex_pubkey, filetxt) and re.search( regex_signkey, filetxt): return SigningKey.from_pubsec_file(file) else: message_exit("Error: the format of the file is invalid")
def cmd_transaction(cli_args): """ Retrieve values from command line interface """ if not ( cli_args.contains_definitions("amount") or cli_args.contains_definitions("amountUD") ): message_exit("--amount or --amountUD is not set") if not cli_args.contains_definitions("output"): message_exit("--output is not set") if cli_args.contains_definitions("amount"): tx_amount = float(cli_args.get_definition("amount")) * 100 if cli_args.contains_definitions("amountUD"): tx_amount = float(cli_args.get_definition("amountUD")) * UDValue().ud_value output = cli_args.get_definition("output") comment = ( cli_args.get_definition("comment") if cli_args.contains_definitions("comment") else "" ) allSources = cli_args.contains_switches("allSources") if cli_args.contains_definitions("outputBackChange"): outputBackChange = cli_args.get_definition("outputBackChange") else: outputBackChange = None return tx_amount, output, comment, allSources, outputBackChange
def auth_by_auth_file(cli_args): if cli_args.contains_definitions("file"): file = cli_args.get_definition("file") else: file = "authfile" if not path.isfile(file): message_exit('Error: the file "' + file + '" does not exist') with open(file) as f: filetxt = f.read() regex_seed = compile("^[0-9a-fA-F]{64}$") regex_gannonce = compile( "^pub: [1-9A-HJ-NP-Za-km-z]{43,44}\nsec: [1-9A-HJ-NP-Za-km-z]{88,90}.*$" ) # Seed Format if search(regex_seed, filetxt): seed = filetxt[0:64] # gannonce.duniter.org Format elif search(regex_gannonce, filetxt): private_key = filetxt.split("sec: ")[1].split("\n")[0] seed = encoding.HexEncoder.encode( b58_decode(private_key))[0:64].decode("utf-8") else: message_exit("Error: the format of the file is invalid") return seed
def set_network_sort_keys(some_keys): global network_sort_keys if some_keys.endswith(","): message_exit( "Argument 'sort' ends with a comma, you have probably inserted a space after the comma, which is incorrect." ) network_sort_keys = some_keys.split(",")
def auth_by_scrypt(cli_args): salt = getpass("Please enter your Scrypt Salt (Secret identifier): ") password = getpass("Please enter your Scrypt password (masked): ") if (cli_args.contains_definitions("n") and cli_args.contains_definitions("r") and cli_args.contains_definitions("p")): n, r, p = ( cli_args.get_definition("n"), cli_args.get_definition("r"), cli_args.get_definition("p"), ) if n.isnumeric() and r.isnumeric() and p.isnumeric(): n, r, p = int(n), int(r), int(p) if n <= 0 or n > 65536 or r <= 0 or r > 512 or p <= 0 or p > 32: message_exit( "Error: the values of Scrypt parameters are not good") else: message_exit("one of n, r or p is not a number") else: print( "Using default values. Scrypt parameters not specified or wrong format" ) n, r, p = 4096, 16, 1 print("Scrypt parameters used: N: {0}, r: {1}, p: {2}".format(n, r, p)) return get_seed_from_scrypt(salt, password, n, r, p)
def checkComment(Comment): if len(Comment) > 255: message_exit("Error: Comment is too long") regex = compile( "^[0-9a-zA-Z\\ \\-\\_\\:\\/\\;\\*\\[\\]\\(\\)\\?\\!\\^\\+\\=\\@\\&\\~\\#\\{\\}\\|\\\\<\\>\\%\\.]*$" ) if not search(regex, Comment): message_exit("Error: the format of the comment is invalid")
def checkComment(Comment): if len(Comment) > 255: message_exit("Error: Comment is too long") regex = compile( "^[0-9a-zA-Z\ \-\_\:\/\;\*\[\]\(\)\?\!\^\+\=\@\&\~\#\{\}\|\\\<\>\%\.]*$" ) if not search(regex, Comment): message_exit("Error: the format of the comment is invalid")
def auth_by_wif(): wif_hex = getpass("Enter your WIF or Encrypted WIF address (masked): ") password = getpass( "(Leave empty in case WIF format) Enter the Encrypted WIF password (masked): " ) try: return SigningKey.from_wif_or_ewif_hex(wif_hex, password) except Exception as error: message_exit(error)
async def send_transaction(amounts, amountsud, allsources, recipients, comment, outputbackchange, yes): """ Main function """ if not (amounts or amountsud or allsources): message_exit("Error: amount, amountUD or allSources is not set.") if allsources and len(recipients) > 1: message_exit( "Error: the --allSources option can only be used with one recipient." ) # compute amounts and amountsud if not allsources: tx_amounts = await transaction_amount(amounts, amountsud, recipients) key = auth_method() issuer_pubkey = key.pubkey pubkey_amount = await money.get_amount_from_pubkey(issuer_pubkey) if allsources: tx_amounts = [pubkey_amount[0]] recipients = list(recipients) outputbackchange = check_transaction_values( comment, recipients, outputbackchange, pubkey_amount[0] < sum(tx_amounts), issuer_pubkey, ) if (yes or input( tabulate( await transaction_confirmation( issuer_pubkey, pubkey_amount[0], tx_amounts, recipients, outputbackchange, comment, ), tablefmt="fancy_grid", ) + "\nDo you confirm sending this transaction? [yes/no]: ") == "yes"): await handle_intermediaries_transactions( key, issuer_pubkey, tx_amounts, recipients, comment, outputbackchange, ) else: client = ClientInstance().client await client.close()
def check_pubkey_format(pubkey, display_error=True): """ Checks if a pubkey has a checksum. Exits if the pubkey is invalid. """ if re.search(re.compile(PUBKEY_DELIMITED_PATTERN), pubkey): return False elif re.search(re.compile(PUBKEY_CHECKSUM_PATTERN), pubkey): return True elif display_error: message_exit("Error: bad format for following public key: " + pubkey) return
def get_seed_from_ewifv1(ewif, password): regex = compile("^[1-9A-HJ-NP-Za-km-z]*$") if not search(regex, ewif): message_exit("Error: the format of EWIF is invalid") wif_bytes = b58_decode(ewif) if len(wif_bytes) != 39: message_exit("Error: the size of EWIF is invalid") wif_no_checksum = wif_bytes[0:-2] checksum_from_ewif = wif_bytes[-2:] fi = wif_bytes[0:1] salt = wif_bytes[1:5] encryptedhalf1 = wif_bytes[5:21] encryptedhalf2 = wif_bytes[21:37] if fi != b"\x02": message_exit("Error: It's not a EWIF format") # Checksum Control checksum = nacl.hash.sha256( nacl.hash.sha256(wif_no_checksum, encoding.RawEncoder), encoding.RawEncoder)[0:2] if checksum_from_ewif != checksum: message_exit("Error: bad checksum of EWIF address") # SCRYPT password = password.encode("utf-8") scrypt_seed = hash(password, salt, 16384, 8, 8, 64) derivedhalf1 = scrypt_seed[0:32] derivedhalf2 = scrypt_seed[32:64] # AES aes = pyaes.AESModeOfOperationECB(derivedhalf2) decryptedhalf1 = aes.decrypt(encryptedhalf1) decryptedhalf2 = aes.decrypt(encryptedhalf2) # XOR seed1 = xor_bytes(decryptedhalf1, derivedhalf1[0:16]) seed2 = xor_bytes(decryptedhalf2, derivedhalf1[16:32]) seed = seed1 + seed2 seedhex = encoding.HexEncoder.encode(seed).decode("utf-8") # Password Control salt_from_seed = nacl.hash.sha256( nacl.hash.sha256(b58_decode(get_publickey_from_seed(seedhex)), encoding.RawEncoder), encoding.RawEncoder, )[0:4] if salt_from_seed != salt: message_exit("Error: bad Password of EWIF address") return seedhex
def validate_checksum(pubkey_checksum): """ Check pubkey checksum after the pubkey, delimited by ":". If check pass: return pubkey Else: exit. """ pubkey, checksum = pubkey_checksum.split(":") if checksum == gen_checksum(pubkey): return pubkey message_exit("Error: public key '" + pubkey + "' does not match checksum '" + checksum + "'.\nPlease verify the public key.")
async def check_passed_blocks_range(client, from_block, to_block): head_number = (await client(bma.blockchain.current))["number"] if to_block == 0: to_block = head_number if to_block > head_number: await client.close() message_exit( "Passed TO_BLOCK argument is bigger than the head block: " + str(head_number)) if from_block > to_block: await client.close() message_exit("TO_BLOCK should be bigger or equal to FROM_BLOCK") return to_block
def get_informations_for_identity(id): """ Check that the id is present on the network many identities could match return the one searched """ certs_req = get_informations_for_identities(id) if certs_req == NO_MATCHING_ID: message_exit(NO_MATCHING_ID) for certs_id in certs_req: if certs_id["uids"][0]["uid"].lower() == id.lower(): return certs_id message_exit(NO_MATCHING_ID)
async def wot_lookup(identifier): """ :identifier: identity or pubkey in part or whole Return received and sent certifications lists of matching identities if one identity found """ client = ClientInstance().client try: results = await client(wot.lookup, identifier) return results["results"] except DuniterError as e: message_exit(e.message) except ValueError as e: pass
def get_seed_from_wifv1(wif): regex = compile("^[1-9A-HJ-NP-Za-km-z]*$") if not search(regex, wif): message_exit("Error: the format of WIF is invalid") wif_bytes = b58_decode(wif) if len(wif_bytes) != 35: message_exit("Error: the size of WIF is invalid") checksum_from_wif = wif_bytes[-2:] fi = wif_bytes[0:1] seed = wif_bytes[1:-2] seed_fi = wif_bytes[0:-2] if fi != b"\x01": message_exit("Error: It's not a WIF format") # checksum control checksum = nacl.hash.sha256(nacl.hash.sha256(seed_fi, encoding.RawEncoder), encoding.RawEncoder)[0:2] if checksum_from_wif != checksum: message_exit("Error: bad checksum of the WIF") seedhex = encoding.HexEncoder.encode(seed).decode("utf-8") return seedhex
def compute_amounts(amounts, multiplicator): """ Computes the amounts(UD) and returns a list. Multiplicator should be either CENT_MULT_TO_UNIT or UD_Value. If relative amount, check that amount is superior to minimal amount. """ # Create amounts list amounts_list = list() for amount in amounts: computed_amount = amount * multiplicator # check if relative amounts are high enough if (multiplicator != CENT_MULT_TO_UNIT) and ( computed_amount < (MINIMAL_TX_AMOUNT * CENT_MULT_TO_UNIT)): message_exit("Error: amount {0} is too low.".format(amount)) amounts_list.append(round(computed_amount)) return amounts_list
def auth_by_wif(): wif = input("Please enter your WIF or Encrypted WIF address: ") regex = compile("^[1-9A-HJ-NP-Za-km-z]*$") if not search(regex, wif): message_exit("Error: the format of WIF is invalid") wif_bytes = b58_decode(wif) fi = wif_bytes[0:1] if fi == b"\x01": return get_seed_from_wifv1(wif) elif fi == b"\x02": password = getpass("Please enter the " + "password of WIF (masked): ") return get_seed_from_ewifv1(wif, password) message_exit("Error: the format of WIF is invalid or unknown")
async def transaction_amount(amounts, UDs_amounts, outputAddresses): """ Check that the number of passed amounts(UD) and recipients are the same Returns a list of amounts. """ # Create amounts list if amounts: amounts_list = compute_amounts(amounts, CENT_MULT_TO_UNIT) elif UDs_amounts: UD_value = await money.UDValue().ud_value amounts_list = compute_amounts(UDs_amounts, UD_value) if len(amounts_list) != len(outputAddresses) and len(amounts_list) != 1: message_exit( "Error: The number of passed recipients is not the same as the passed amounts." ) # In case one amount is passed with multiple recipients # generate list containing multiple time the same amount if len(amounts_list) == 1 and len(outputAddresses) > 1: amounts_list = [amounts_list[0]] * len(outputAddresses) return amounts_list
def check_transaction_values(comment, outputAddresses, outputBackChange, enough_source, issuer_pubkey): """ Check the comment format Check the pubkeys and the checksums of the recipients and the outputbackchange In case of a valid checksum, assign and return the pubkey without the checksum Check the balance is big enough for the transaction """ checkComment(comment) for i, outputAddress in enumerate(outputAddresses): if check_pubkey_format(outputAddress): outputAddresses[i] = validate_checksum(outputAddress) if outputBackChange: if check_pubkey_format(outputBackChange): outputBackChange = validate_checksum(outputBackChange) if enough_source: message_exit( display_pubkey_and_checksum(issuer_pubkey) + " pubkey doesn’t have enough money for this transaction.") return outputBackChange
async def get_list_input_for_transaction(pubkey, TXamount): listinput, amount = await money.get_sources(pubkey) # generate final list source listinputfinal = [] totalAmountInput = 0 intermediatetransaction = False for input in listinput: listinputfinal.append(input) totalAmountInput += money.amount_in_current_base(input) TXamount -= money.amount_in_current_base(input) # if more than 40 sources, it's an intermediate transaction if len(listinputfinal) >= SOURCES_PER_TX: intermediatetransaction = True break if TXamount <= 0: break if TXamount > 0 and not intermediatetransaction: message_exit("Error: you don't have enough money") return listinputfinal, totalAmountInput, intermediatetransaction
def get_list_input_for_transaction(pubkey, TXamount, allinput=False): listinput, amount = get_sources(pubkey) # generate final list source listinputfinal = [] totalAmountInput = 0 intermediatetransaction = 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: intermediatetransaction = True break if TXamount <= 0 and not allinput: break if TXamount > 0 and not intermediatetransaction: message_exit("Error: you don't have enough money") return listinputfinal, totalAmountInput, intermediatetransaction
def send_certification(cli_args): id_to_certify = get_informations_for_identity(cli_args.subsubcmd) main_id_to_certify = id_to_certify["uids"][0] # Display license and ask for confirmation license_approval(HeadBlock().head_block["currency"]) # Authentication seed = auth_method(cli_args) # Check whether current user is member issuer_pubkey = get_publickey_from_seed(seed) issuer_id = get_uid_from_pubkey(issuer_pubkey) if not is_member(issuer_pubkey, issuer_id): message_exit("Current identity is not member.") if issuer_pubkey == id_to_certify["pubkey"]: message_exit("You can’t certify yourself!") for certifier in main_id_to_certify["others"]: if certifier["pubkey"] == issuer_pubkey: message_exit("Identity already certified by " + issuer_id) # Certification confirmation if not certification_confirmation(issuer_id, issuer_pubkey, id_to_certify, main_id_to_certify): return cert_doc = generate_certification_document(issuer_pubkey, id_to_certify, main_id_to_certify) cert_doc += sign_document_from_seed(cert_doc, seed) + "\n" # Send certification document post_request("wot/certify", "cert=" + urllib.parse.quote_plus(cert_doc)) print("Certification successfully sent.")
async def generate_and_send_transaction( key, issuers, tx_amounts, listinput_and_amount, outputAddresses, Comment, OutputbackChange=None, ): """ Display sent transaction Generate, sign, and send transaction document """ intermediate_tx = listinput_and_amount[2] if intermediate_tx: print("Generate Change Transaction") else: print("Generate Transaction:") print(" - From: " + display_pubkey_and_checksum(issuers)) for tx_amount, outputAddress in zip(tx_amounts, outputAddresses): display_sent_tx(outputAddress, tx_amount) print(" - Total: " + str(sum(tx_amounts) / 100)) client = ClientInstance().client transaction = await generate_transaction_document( issuers, tx_amounts, listinput_and_amount, outputAddresses, Comment, OutputbackChange, ) transaction.sign([key]) response = await client(process, transaction.signed_raw()) if response.status == 200: print("Transaction successfully sent.") else: message_exit("Error while publishing transaction: {0}".format( await response.text()))
def check_transaction_values( comment, outputAddresses, outputBackChange, enough_source, issuer_pubkey ): checkComment(comment) for outputAddress in outputAddresses: if check_public_key(outputAddress, True) is False: message_exit(outputAddress) if outputBackChange: outputBackChange = check_public_key(outputBackChange, True) if check_public_key(outputBackChange, True) is False: message_exit(outputBackChange) if enough_source: message_exit( issuer_pubkey + " pubkey doesn’t have enough money for this transaction." )
def auth_by_scrypt(ctx): salt = getpass("Please enter your Scrypt Salt (Secret identifier): ") password = getpass("Please enter your Scrypt password (masked): ") if ctx.obj["AUTH_SCRYPT_PARAMS"]: n, r, p = ctx.obj["AUTH_SCRYPT_PARAMS"].split(",") if n.isnumeric() and r.isnumeric() and p.isnumeric(): n, r, p = int(n), int(r), int(p) if n <= 0 or n > 65536 or r <= 0 or r > 512 or p <= 0 or p > 32: message_exit( "Error: the values of Scrypt parameters are not good") scrypt_params = ScryptParams(n, r, p) else: message_exit("one of n, r or p is not a number") else: scrypt_params = None try: return SigningKey.from_credentials(salt, password, scrypt_params) except ValueError as error: message_exit(error)
def auth_by_seed(): seed = input("Please enter your seed on hex format: ") regex = compile("^[0-9a-fA-F]{64}$") if not search(regex, seed): message_exit("Error: the format of the seed is invalid") return seed
def auth_by_seed(): seedhex = getpass("Please enter your seed on hex format: ") try: return SigningKey.from_seedhex(seedhex) except Exception as error: message_exit(error)