# Copyright 2014-2024 Vincent Texier # # DuniterPy is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # DuniterPy is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import getpass import os from typing import Optional from duniterpy.api import bma from duniterpy.api.client import Client from duniterpy.documents import Identity, Revocation from duniterpy.key import SigningKey if "XDG_CONFIG_HOME" in os.environ: home_path = os.environ["XDG_CONFIG_HOME"] elif "HOME" in os.environ: home_path = os.environ["HOME"] elif "APPDATA" in os.environ: home_path = os.environ["APPDATA"] else: home_path = os.path.dirname(__file__) # CONFIG ####################################### # You can either use a complete defined endpoint : [NAME_OF_THE_API] [DOMAIN] [IPv4] [IPv6] [PORT] [PATH] # or the simple definition : [NAME_OF_THE_API] [DOMAIN] [PORT] [PATH] # Here we use the secure BASIC_MERKLED_API (BMAS) BMAS_ENDPOINT = "BMAS g1-test.duniter.org 443" # WARNING : Hide this file in a safe and secure place # If one day you forget your credentials, # you'll have to use your private key instead REVOCATION_DOCUMENT_FILE_PATH = os.path.join( home_path, "duniter_account_revocation_document.txt" ) ################################################ def get_identity_document( client: Client, current_block: dict, pubkey: str ) -> Optional[Identity]: """ Get the identity document of the pubkey :param client: Client to connect to the api :param current_block: Current block data :param pubkey: UID/Public key :rtype: Identity """ # Here we request for the path wot/lookup/pubkey lookup_response = client(bma.wot.lookup, pubkey) return Identity.from_bma_lookup_response( current_block["currency"], pubkey, lookup_response ) def get_signed_raw_revocation_document( identity: Identity, salt: str, password: str ) -> str: """ Generate account revocation document for given identity :param identity: Self Certification of the identity :param salt: Salt :param password: Password :rtype: str """ key = SigningKey.from_credentials(salt, password) revocation = Revocation(identity, key, currency=identity.currency) return revocation.signed_raw() def save_revoke_document(): """ Main code """ # Create Client from endpoint string in Duniter format client = Client(BMAS_ENDPOINT) # Get the node summary infos to test the connection response = client(bma.node.summary) print(response) # prompt hidden user entry salt = getpass.getpass("Enter your passphrase (salt): ") # prompt hidden user entry password = getpass.getpass("Enter your password: ") # prompt public key pubkey = input("Enter your public key: ") # init signer instance signer = SigningKey.from_credentials(salt, password) # check public key if signer.pubkey != pubkey: print("Bad credentials!") return # capture current block to get currency name current_block = client(bma.blockchain.current) # create our Identity document to sign the revocation document identity = get_identity_document(client, current_block, pubkey) if identity is None: print(f"Identity not found for pubkey {pubkey}") # Close client aiohttp session return # get the revoke document revocation_signed_raw_document = get_signed_raw_revocation_document( identity, salt, password ) # save revoke document in a file with open(REVOCATION_DOCUMENT_FILE_PATH, "w", encoding="utf-8") as fp: fp.write(revocation_signed_raw_document) # document saved print(f"Revocation document saved in {REVOCATION_DOCUMENT_FILE_PATH}") if __name__ == "__main__": save_revoke_document()