diff --git a/.gitignore b/.gitignore index 6b1948ec442a8446d8c751e6f670c7b48e2d24cb..7475829168126a8e3d73e1bf2135362365ae451c 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ nosetests.xml .mr.developer.cfg .project .pydevproject +.idea diff --git a/README.md b/README.md index 2288cbc94ecd1557c7824f413e8a218135dbee4d..c7f5ec52f41a0d8e40725a76e1d2642fac72db77 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,12 @@ A python implementation of [duniter](https://github.com/duniter/duniter) API * [libnacl](https://pypi.python.org/pypi/libnacl "libnacl") * [base58](https://pypi.python.org/pypi/base58 "base58") -##Installation +## Installation You can install duniter-python-api and all its dependencies via the following pip install : `pip install duniterpy` Please take a look at the document [HTTP API](https://github.com/duniter/duniter/blob/master/doc/HTTP_API.md) to learn about the API. + +## Documentation + +- [Outdated documentation](https://ucoin-python-api.readthedocs.io/en/latest) diff --git a/docs/conf.py b/docs/conf.py index 8213b7e27e45886aed4af8f7762c80a0f5744ff5..5c664349ff241e5c63bc8626d69e4ae0d1b1bacb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -15,7 +15,6 @@ import sys import os -import shlex from unittest.mock import MagicMock # If extensions (or modules to document with autodoc) are in another directory, @@ -23,6 +22,7 @@ from unittest.mock import MagicMock # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) sys.path.append(os.path.join("..")) + # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. @@ -43,9 +43,10 @@ class Mock(MagicMock): def __getattr__(cls, name): return Mock() -MOCK_MODULES = ['libnacl', 'libnacl.sign'] -sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) - +# BUG: trigger a recursion error when run with python 3... +# MOCK_MODULES = ['libnacl', 'libnacl.sign'] +# sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) +# # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -71,9 +72,9 @@ author = 'caner & inso' # built documents. # # The short X.Y version. -version = '0.12' +version = '0.30.5' # The full version, including alpha/beta/rc tags. -release = '0.12' +release = '0.30.5' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -124,7 +125,7 @@ todo_include_todos = True # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'alabaster' +#html_theme = 'alabaster' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the @@ -153,7 +154,7 @@ html_theme = 'alabaster' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +#html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied diff --git a/docs/documents.rst b/docs/documents.rst deleted file mode 100644 index 4c248c8b73ac7d132b46bbdef6027ba112a901f2..0000000000000000000000000000000000000000 --- a/docs/documents.rst +++ /dev/null @@ -1,37 +0,0 @@ -Documents methods -================= - -.. automodule:: duniterpy.documents.block - :members: - :special-members: - :exclude-members: __dict__,__weakref__ - -.. automodule:: duniterpy.documents.certification - :members: - :special-members: - :exclude-members: __dict__,__weakref__ - -.. automodule:: duniterpy.documents.document - :members: - :special-members: - :exclude-members: __dict__,__weakref__ - -.. automodule:: duniterpy.documents.membership - :members: - :special-members: - :exclude-members: __dict__,__weakref__ - -.. automodule:: duniterpy.documents.peer - :members: - :special-members: - :exclude-members: __dict__,__weakref__ - -.. automodule:: duniterpy.documents.status - :members: - :special-members: - :exclude-members: __dict__,__weakref__ - -.. automodule:: duniterpy.documents.transaction - :members: - :special-members: - :exclude-members: __dict__,__weakref__ \ No newline at end of file diff --git a/docs/duniterpy.api.bma.network.rst b/docs/duniterpy.api.bma.network.rst new file mode 100644 index 0000000000000000000000000000000000000000..050fbae5416cc8505f5556cb7ad3a78e86136093 --- /dev/null +++ b/docs/duniterpy.api.bma.network.rst @@ -0,0 +1,22 @@ +duniterpy.api.bma.network package +================================= + +Submodules +---------- + +duniterpy.api.bma.network.peering module +---------------------------------------- + +.. automodule:: duniterpy.api.bma.network.peering + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: duniterpy.api.bma.network + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/duniterpy.api.bma.rst b/docs/duniterpy.api.bma.rst new file mode 100644 index 0000000000000000000000000000000000000000..74e1d6fd8867f2710aba1f56055ac67bc23b946a --- /dev/null +++ b/docs/duniterpy.api.bma.rst @@ -0,0 +1,70 @@ +duniterpy.api.bma package +========================= + +Subpackages +----------- + +.. toctree:: + + duniterpy.api.bma.network + duniterpy.api.bma.tx + +Submodules +---------- + +duniterpy.api.bma.api module +---------------------------- + +.. automodule:: duniterpy.api.bma.api + :members: + :undoc-members: + :show-inheritance: + +duniterpy.api.bma.blockchain module +----------------------------------- + +.. automodule:: duniterpy.api.bma.blockchain + :members: + :undoc-members: + :show-inheritance: + +duniterpy.api.bma.node module +----------------------------- + +.. automodule:: duniterpy.api.bma.node + :members: + :undoc-members: + :show-inheritance: + +duniterpy.api.bma.ud module +--------------------------- + +.. automodule:: duniterpy.api.bma.ud + :members: + :undoc-members: + :show-inheritance: + +duniterpy.api.bma.wot module +---------------------------- + +.. automodule:: duniterpy.api.bma.wot + :members: + :undoc-members: + :show-inheritance: + +duniterpy.api.bma.ws module +--------------------------- + +.. automodule:: duniterpy.api.bma.ws + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: duniterpy.api.bma + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/duniterpy.api.bma.tx.rst b/docs/duniterpy.api.bma.tx.rst new file mode 100644 index 0000000000000000000000000000000000000000..e16eb3f30b5d342adbe767c4f83df90447ed06e7 --- /dev/null +++ b/docs/duniterpy.api.bma.tx.rst @@ -0,0 +1,22 @@ +duniterpy.api.bma.tx package +============================ + +Submodules +---------- + +duniterpy.api.bma.tx.history module +----------------------------------- + +.. automodule:: duniterpy.api.bma.tx.history + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: duniterpy.api.bma.tx + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/duniterpy.api.rst b/docs/duniterpy.api.rst new file mode 100644 index 0000000000000000000000000000000000000000..67feec7f1a241e1f7d9b6e5ca933a4e4ea54fe9f --- /dev/null +++ b/docs/duniterpy.api.rst @@ -0,0 +1,29 @@ +duniterpy.api package +===================== + +Subpackages +----------- + +.. toctree:: + + duniterpy.api.bma + +Submodules +---------- + +duniterpy.api.errors module +--------------------------- + +.. automodule:: duniterpy.api.errors + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: duniterpy.api + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/duniterpy.documents.rst b/docs/duniterpy.documents.rst new file mode 100644 index 0000000000000000000000000000000000000000..750392d22646dc87004590ca6c06c3f2384ace74 --- /dev/null +++ b/docs/duniterpy.documents.rst @@ -0,0 +1,70 @@ +duniterpy.documents package +=========================== + +Submodules +---------- + +duniterpy.documents.block module +-------------------------------- + +.. automodule:: duniterpy.documents.block + :members: + :undoc-members: + :show-inheritance: + +duniterpy.documents.certification module +---------------------------------------- + +.. automodule:: duniterpy.documents.certification + :members: + :undoc-members: + :show-inheritance: + +duniterpy.documents.constants module +------------------------------------ + +.. automodule:: duniterpy.documents.constants + :members: + :undoc-members: + :show-inheritance: + +duniterpy.documents.document module +----------------------------------- + +.. automodule:: duniterpy.documents.document + :members: + :undoc-members: + :show-inheritance: + +duniterpy.documents.membership module +------------------------------------- + +.. automodule:: duniterpy.documents.membership + :members: + :undoc-members: + :show-inheritance: + +duniterpy.documents.peer module +------------------------------- + +.. automodule:: duniterpy.documents.peer + :members: + :undoc-members: + :show-inheritance: + +duniterpy.documents.transaction module +-------------------------------------- + +.. automodule:: duniterpy.documents.transaction + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: duniterpy.documents + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/duniterpy.grammars.rst b/docs/duniterpy.grammars.rst new file mode 100644 index 0000000000000000000000000000000000000000..b99c644d91cc5c95434ccc71928da3c36c3223e9 --- /dev/null +++ b/docs/duniterpy.grammars.rst @@ -0,0 +1,22 @@ +duniterpy.grammars package +========================== + +Submodules +---------- + +duniterpy.grammars.output module +-------------------------------- + +.. automodule:: duniterpy.grammars.output + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: duniterpy.grammars + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/duniterpy.key.rst b/docs/duniterpy.key.rst new file mode 100644 index 0000000000000000000000000000000000000000..fb0d328568ccaf6c091d8b6fb3ff1067dcd2a7f8 --- /dev/null +++ b/docs/duniterpy.key.rst @@ -0,0 +1,46 @@ +duniterpy.key package +===================== + +Submodules +---------- + +duniterpy.key.base58 module +--------------------------- + +.. automodule:: duniterpy.key.base58 + :members: + :undoc-members: + :show-inheritance: + +duniterpy.key.encryption_key module +----------------------------------- + +.. automodule:: duniterpy.key.encryption_key + :members: + :undoc-members: + :show-inheritance: + +duniterpy.key.signing_key module +-------------------------------- + +.. automodule:: duniterpy.key.signing_key + :members: + :undoc-members: + :show-inheritance: + +duniterpy.key.verifying_key module +---------------------------------- + +.. automodule:: duniterpy.key.verifying_key + :members: + :undoc-members: + :show-inheritance: + + +Module contents +--------------- + +.. automodule:: duniterpy.key + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/duniterpy.rst b/docs/duniterpy.rst new file mode 100644 index 0000000000000000000000000000000000000000..943b8d110ad03c26d80f25257a45e478d5d4ffb1 --- /dev/null +++ b/docs/duniterpy.rst @@ -0,0 +1,20 @@ +duniterpy package +================= + +Subpackages +----------- + +.. toctree:: + + duniterpy.api + duniterpy.documents + duniterpy.grammars + duniterpy.key + +Module contents +--------------- + +.. automodule:: duniterpy + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/generate.sh b/docs/generate.sh new file mode 100755 index 0000000000000000000000000000000000000000..c67c6991f3aa991b2a21ba15f8f682e710da81c1 --- /dev/null +++ b/docs/generate.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +# Infos how-to generate automatically all rst documents for a python package : +# http://stackoverflow.com/questions/4616693/automatically-generating-documentation-for-all-python-package-contents + +# generate rst files of all the package +sphinx-apidoc -o . ../duniterpy + +# generate HTML +make html diff --git a/docs/index.rst b/docs/index.rst index 524cf6e4481fbc15a90a77ca6c9090944e058276..4531a0ce2290ab60fe96734a2ce833d9c5c7f4c7 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -4,7 +4,7 @@ contain the root `toctree` directive. duniterpy : A python implementation of duniter API -============================================== +================================================== duniterpy is a library to develop an application for duniter. duniterpy helps to handle the following problem : @@ -28,13 +28,12 @@ Contributions are welcome. Contents: -======== +========= .. toctree:: - :maxdepth: 2 - - documents + :glob: + modules Indices and tables ================== diff --git a/docs/modules.rst b/docs/modules.rst new file mode 100644 index 0000000000000000000000000000000000000000..be11485ab93f360e4581fe0594f3d6de4db598f5 --- /dev/null +++ b/docs/modules.rst @@ -0,0 +1,7 @@ +duniterpy +========= + +.. toctree:: + :maxdepth: 4 + + duniterpy diff --git a/duniterpy/documents/certification.py b/duniterpy/documents/certification.py index 8407e8e72eac81a1264bb16794fd99bc7881842d..d3b9ebf5e1212c0e6b4e7359e33a596c2ad4f304 100644 --- a/duniterpy/documents/certification.py +++ b/duniterpy/documents/certification.py @@ -8,7 +8,7 @@ from .constants import pubkey_regex, signature_regex, block_id_regex, block_uid_ class SelfCertification(Document): """ - A document discribing a self certification. + A document describing a self certification. """ re_inline = re.compile("({pubkey_regex}):({signature_regex}):({block_uid_regex}):([^\n]+)\n" @@ -19,6 +19,16 @@ class SelfCertification(Document): re_timestamp = re.compile("META:TS:({block_uid_regex})\n".format(block_uid_regex=block_uid_regex)) def __init__(self, version, currency, pubkey, uid, ts, signature): + """ + Create an identity document + + :param int version: Version of the document + :param str currency: Name of the currency + :param str pubkey: Public key of the account linked to the identity + :param str uid: Unique identifier + :param BlockUID ts: Block timestamp + :param str|None signature: Signature of the document + """ if signature: super().__init__(version, currency, [signature]) else: @@ -80,7 +90,7 @@ class Certification(Document): """ Constructor - :param str version: the UCP version + :param int version: the UCP version :param str currency: the currency of the blockchain :param str pubkey_from: :param str pubkey_to: diff --git a/duniterpy/documents/membership.py b/duniterpy/documents/membership.py index 24abc12b667b72a23f19ae2f3577ce039aa7b128..26e09f47627a06f29def3df3a0b02fe4c4ac3523 100644 --- a/duniterpy/documents/membership.py +++ b/duniterpy/documents/membership.py @@ -48,7 +48,16 @@ class Membership(Document): def __init__(self, version, currency, issuer, membership_ts, membership_type, uid, identity_ts, signature): """ - Constructor + Create a membership document + + :param int version: Version of the document + :param currency: Name of the currency + :param issuer: Public key of the issuer + :param BlockUID membership_ts: BlockUID of this membership + :param membership_type: "IN" or "OUT" to enter or quit the community + :param str uid: Unique identifier of the identity + :param BlockUID identity_ts: BlockUID of the identity + :param str|None signature: Signature of the document """ super().__init__(version, currency, [signature]) self.issuer = issuer diff --git a/duniterpy/documents/transaction.py b/duniterpy/documents/transaction.py index a39582227dea467cef65638150a555479e2cc54b..79b892e5017f451cb85cbc9454ddac1128cadaa0 100644 --- a/duniterpy/documents/transaction.py +++ b/duniterpy/documents/transaction.py @@ -99,7 +99,7 @@ class Transaction(Document): :param BlockUID blockstamp: :param int locktime: :param list[str] issuers: - :param inputs: + :param list[InputSource] inputs: :param unlocks: :param outputs: :param comment: diff --git a/examples/create_and_publish_identity.py b/examples/create_and_publish_identity.py new file mode 100644 index 0000000000000000000000000000000000000000..2bd1fae18aead9d9033a36cf695a7f54589272aa --- /dev/null +++ b/examples/create_and_publish_identity.py @@ -0,0 +1,106 @@ +import asyncio +import aiohttp + +import duniterpy.api.bma as bma +from duniterpy.documents import BMAEndpoint, BlockUID, SelfCertification +from duniterpy.key import SigningKey + + +# CONFIG ####################################### + +# You can either use a complete defined endpoint : [NAME_OF_THE_API] [DOMAIN] [IPv4] [IPv6] [PORT] +# or the simple definition : [NAME_OF_THE_API] [DOMAIN] [PORT] +# Here we use the BASIC_MERKLED_API +BMA_ENDPOINT = "BASIC_MERKLED_API cgeek.fr 9330" + +# Credentials should be prompted or kept in a separate secure file +# create a file with the salt on the first line and the password on the second line +# the script will load them from the file +FROM_CREDENTIALS_FILE = "/home/username/.credentials.txt" + +# Your unique identifier in the Web of Trust +UID = "MyIdentity" + +################################################ + +# Latest duniter-python-api is asynchronous and you have to create an aiohttp session to send request +# ( http://pythonhosted.org/aiohttp ) +AIOHTTP_SESSION = aiohttp.ClientSession() + +async def get_current_block(connection): + """ + Get the current block data + + :param bma.api.ConnectionHandler connection: Connection handler + + :rtype: dict + """ + # Here we request for the path blockchain/block/N + return await bma.blockchain.Current(connection).get(AIOHTTP_SESSION) + + +def get_identity_document(current_block, uid, salt, password): + """ + Get an Identity document + + :param dict current_block: Current block data + :param str uid: Unique Identifier + :param str salt: Passphrase of the account + :param str password: Password of the account + + :rtype: SelfCertification + """ + + # get current block BlockStamp + timestamp = BlockUID(current_block['number'], current_block['hash']) + + # create keys from credentials + key = SigningKey(salt, password) + + # create identity document + identity = SelfCertification( + version=2, + currency=current_block['currency'], + pubkey=key.pubkey, + uid=uid, + ts=timestamp, + signature=None + ) + + # sign document + identity.sign([key]) + + return identity + +async def main(): + """ + Main code + """ + # connection handler from BMA endpoint + connection = BMAEndpoint.from_inline(BMA_ENDPOINT).conn_handler() + + # capture current block to get version and currency and blockstamp + current_block = await get_current_block(connection) + + # load credentials from a text file + salt, password = open(FROM_CREDENTIALS_FILE).readlines() + + # cleanup newlines + salt, password = salt.strip(), password.strip() + + # create our signed identity document + identity = get_identity_document(current_block, UID, salt, password) + + # send the identity document to the node + data = {identity: identity.signed_raw()} + response = await bma.wot.Add(connection).post(AIOHTTP_SESSION, **data) + + print(response) + + response.close() + +with AIOHTTP_SESSION: + + # Latest duniter-python-api is asynchronous and you have to use asyncio, an asyncio loop and a "as" on the data. + # ( https://docs.python.org/3/library/asyncio.html ) + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/create_public_key.py b/examples/create_public_key.py new file mode 100644 index 0000000000000000000000000000000000000000..ea7ab135da0af81d2d6e7bde5a010075f897f844 --- /dev/null +++ b/examples/create_public_key.py @@ -0,0 +1,30 @@ +from duniterpy.key import SigningKey + +# CONFIG ####################################### + +# CREDENTIALS in Duniter are a couple of strings: +# - A secret pass-phrase +# - A password +# They create a seed which create keys (some are private and one is public) + +# Credentials should be prompted or kept in a separate secure file +# create a file with the salt on the first line and the password on the second line +# the script will load them from the file +CREDENTIALS_FILE_PATH = "/home/username/.credentials.txt" + +################################################ + +if __name__ == '__main__': + + # Load your credentials from a text file + salt, password = open(CREDENTIALS_FILE_PATH).readlines() + # Cleanup newlines + salt, password = salt.strip(), password.strip() + + # Create key object + key = SigningKey(salt, password) + + # Display your public key + print("Public key for your credentials: %s" % key.pubkey) + + diff --git a/examples/request_data.py b/examples/request_data.py new file mode 100644 index 0000000000000000000000000000000000000000..b658eafc6d8de7bac30c316aefe9e15b0dcc3b7b --- /dev/null +++ b/examples/request_data.py @@ -0,0 +1,42 @@ +import asyncio +import aiohttp +import duniterpy.api.bma as bma +from duniterpy.documents import BMAEndpoint + +# CONFIG ####################################### + +# You can either use a complete defined endpoint : [NAME_OF_THE_API] [DOMAIN] [IPv4] [IPv6] [PORT] +# or the simple definition : [NAME_OF_THE_API] [DOMAIN] [PORT] +# Here we use the BASIC_MERKLED_API +BMA_ENDPOINT = "BASIC_MERKLED_API cgeek.fr 9330" + +################################################ + +# Latest duniter-python-api is asynchronous and you have to create an aiohttp session to send request +# ( http://pythonhosted.org/aiohttp ) +AIOHTTP_SESSION = aiohttp.ClientSession() + +async def main(): + """ + Main code + """ + # connection handler from BMA endpoint + connection = BMAEndpoint.from_inline(BMA_ENDPOINT).conn_handler() + + # Get the node summary infos + response = await bma.node.Summary(connection).get(AIOHTTP_SESSION) + print(response) + + # Get the current block data + response = await bma.blockchain.Current(connection).get(AIOHTTP_SESSION) + print(response) + + # Get the block number 0 + response = await bma.blockchain.Block(connection, 0).get(AIOHTTP_SESSION) + print(response) + +with AIOHTTP_SESSION: + + # Latest duniter-python-api is asynchronous and you have to use asyncio, an asyncio loop and a "as" on the data. + # ( https://docs.python.org/3/library/asyncio.html ) + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/save_revoke_document.py b/examples/save_revoke_document.py new file mode 100644 index 0000000000000000000000000000000000000000..e47d94c90fb00b88f0bc1886c7d9b240a381b641 --- /dev/null +++ b/examples/save_revoke_document.py @@ -0,0 +1,138 @@ +import asyncio +import aiohttp +import duniterpy.api.bma as bma +from duniterpy.documents import BMAEndpoint, BlockUID, SelfCertification +from duniterpy.documents import Revocation +from duniterpy.key import SigningKey + +# CONFIG ####################################### + +# You can either use a complete defined endpoint : [NAME_OF_THE_API] [DOMAIN] [IPv4] [IPv6] [PORT] +# or the simple definition : [NAME_OF_THE_API] [DOMAIN] [PORT] +# Here we use the BASIC_MERKLED_API +BMA_ENDPOINT = "BASIC_MERKLED_API cgeek.fr 9330" + +# Credentials should be prompted or kept in a separate secure file +# create a file with the salt on the first line and the password on the second line +# the script will load them from the file +CREDENTIALS_FILE_PATH = "/home/username/.credentials.txt" + +# Public key of the revoked identity account +PUBKEY = "XXXXXXXX" + +# 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 +REVOKE_DOCUMENT_FILE_PATH = "/home/username/duniter_account_revoke_document.txt" + +################################################ + +# Latest duniter-python-api is asynchronous and you have to create an aiohttp session to send request +# ( http://pythonhosted.org/aiohttp ) +AIOHTTP_SESSION = aiohttp.ClientSession() + +# Current protocole version +PROTOCOL_VERSION = 2 + +async def get_current_block(connection): + """ + Get the current block data + + :param bma.api.ConnectionHandler connection: Connection handler + + :rtype: dict + """ + # Here we request for the path blockchain/current + return await bma.blockchain.Current(connection).get(AIOHTTP_SESSION) + +async def get_identity_document(connection, currency, pubkey): + """ + Get the SelfCertification document of the pubkey + + :param bma.api.ConnectionHandler connection: Connection handler + :param str currency: Currency name + :param str pubkey: Public key + + :rtype: SelfCertification + """ + # Here we request for the path wot/lookup/pubkey + lookup_data = await bma.wot.Lookup(connection, pubkey).get(AIOHTTP_SESSION) + + # init vars + uid = None + timestamp = BlockUID.empty() + signature = None + + # parse results + for result in lookup_data['results']: + if result["pubkey"] == pubkey: + uids = result['uids'] + for uid_data in uids: + # capture data + timestamp = BlockUID.from_str(uid_data["meta"]["timestamp"]) + uid = uid_data["uid"] + signature = uid_data["self"] + + # return self-certification document + return SelfCertification( + version=PROTOCOL_VERSION, + currency=currency, + pubkey=pubkey, + uid=uid, + ts=timestamp, + signature=signature + ) + + +async def get_revoke_document(identity, salt, password): + """ + Generate account revocation document for given identity + + :param SelfCertification identity: Self Certification of the identity + :param str salt: Salt + :param str password: Password + + :return: the revokation document + :rtype: duniterpy.documents.certification.Revocation + """ + document = Revocation(PROTOCOL_VERSION, identity.currency, identity.pubkey, "") + + key = SigningKey(salt, password) + document.sign(identity, [key]) + return document.signed_raw(identity) + +async def main(): + """ + Main code + """ + # connection handler from BMA endpoint + connection = BMAEndpoint.from_inline(BMA_ENDPOINT).conn_handler() + + # capture current block to get currency name + current_block = await get_current_block(connection) + + # create our SelfCertification document to sign the revoke document + identity_document = await get_identity_document(connection, current_block['currency'], PUBKEY) + + # load credentials from a text file + salt, password = open(CREDENTIALS_FILE_PATH).readlines() + + # cleanup newlines + salt, password = salt.strip(), password.strip() + + # get the revoke document + revoke_document = await get_revoke_document(identity_document, salt, password) + + # save revoke document in a file + fp = open(REVOKE_DOCUMENT_FILE_PATH, 'w') + fp.write(revoke_document) + fp.close() + + # document saved + print("Revoke document saved in %s" % REVOKE_DOCUMENT_FILE_PATH) + +with AIOHTTP_SESSION: + + # Latest duniter-python-api is asynchronous and you have to use asyncio, an asyncio loop and a "as" on the data. + # ( https://docs.python.org/3/library/asyncio.html ) + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/send_certification.py b/examples/send_certification.py new file mode 100644 index 0000000000000000000000000000000000000000..9897138cd5c3ab4315a62216b13bf8ef6b485f00 --- /dev/null +++ b/examples/send_certification.py @@ -0,0 +1,143 @@ +import asyncio +import aiohttp +import duniterpy.api.bma as bma +from duniterpy.documents import BMAEndpoint, BlockUID, SelfCertification, Certification +from duniterpy.key import SigningKey + + +# CONFIG ####################################### + +# You can either use a complete defined endpoint : [NAME_OF_THE_API] [DOMAIN] [IPv4] [IPv6] [PORT] +# or the simple definition : [NAME_OF_THE_API] [DOMAIN] [PORT] +# Here we use the BASIC_MERKLED_API +BMA_ENDPOINT = "BASIC_MERKLED_API cgeek.fr 9330" + +# Public key of the certifier +FROM_PUBKEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + +# Credentials should be prompted or kept in a separate secure file +# create a file with the salt on the first line and the password on the second line +# the script will load them from the file +FROM_CREDENTIALS_FILE = "/home/username/.credentials.txt" + +# Public key to certified +TO_PUBKEY = "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY" + +################################################ + + +# Latest duniter-python-api is asynchronous and you have to create an aiohttp session to send request +# ( http://pythonhosted.org/aiohttp ) +AIOHTTP_SESSION = aiohttp.ClientSession() + +async def get_current_block(connection): + """ + Get the current block data + + :param bma.api.ConnectionHandler connection: Connection handler + :rtype: dict + """ + # Here we request for the path blockchain/block/N + return await bma.blockchain.Current(connection).get(AIOHTTP_SESSION) + +async def get_identity_document(connection, current_block, pubkey): + """ + Get the identity document of the pubkey + + :param bma.api.ConnectionHandler connection: Connection handler + :param dict current_block: Current block data + :param str pubkey: Public key + + :rtype: SelfCertification + """ + # Here we request for the path wot/lookup/pubkey + lookup_data = await bma.wot.Lookup(connection, pubkey).get(AIOHTTP_SESSION) + + # init vars + uid = None + timestamp = BlockUID.empty() + signature = None + + # parse results + for result in lookup_data['results']: + if result["pubkey"] == pubkey: + uids = result['uids'] + for uid_data in uids: + # capture data + timestamp = BlockUID.from_str(uid_data["meta"]["timestamp"]) + uid = uid_data["uid"] + signature = uid_data["self"] + + # return self-certification document + return SelfCertification( + version=2, + currency=current_block['currency'], + pubkey=pubkey, + uid=uid, + ts=timestamp, + signature=signature + ) + + +def get_certification_document(current_block, self_cert_document, from_pubkey, salt, password): + """ + Create and return a Certification document + + :param dict current_block: Current block data + :param SelfCertification self_cert_document: SelfCertification document + :param str from_pubkey: Pubkey of the certifier + :param str salt: Secret salt (DO NOT SHOW IT ANYWHERE, IT IS SECRET !!!) + :param str password: Secret password (DO NOT SHOW IT ANYWHERE, IT IS SECRET !!!) + + :rtype: Certification + """ + # construct Certification Document + certification = Certification( + version=2, + currency=current_block['currency'], + pubkey_from=from_pubkey, + pubkey_to=self_cert_document.pubkey, + timestamp=BlockUID(current_block['number'], current_block['hash']), + signature="" + ) + # sign document + key = SigningKey(salt, password) + certification.sign(self_cert_document, [key]) + + return certification + +async def main(): + """ + Main code + """ + # connection handler from BMA endpoint + connection = BMAEndpoint.from_inline(BMA_ENDPOINT).conn_handler() + + # capture current block to get version and currency and blockstamp + current_block = await get_current_block(connection) + + # create our SelfCertification document to sign the Certification document + identity = await get_identity_document(connection, current_block, TO_PUBKEY) + + # load credentials from a text file + salt, password = open(FROM_CREDENTIALS_FILE).readlines() + + # cleanup newlines + salt, password = salt.strip(), password.strip() + + # send the Certification document to the node + certification = get_certification_document(current_block, identity, FROM_PUBKEY, salt, password) + + # Here we request for the path wot/certify + data = {'cert': certification.signed_raw(identity)} + response = await bma.wot.Certify(connection).post(AIOHTTP_SESSION, **data) + + print(response) + + response.close() + +with AIOHTTP_SESSION: + + # Latest duniter-python-api is asynchronous and you have to use asyncio, an asyncio loop and a "as" on the data. + # ( https://docs.python.org/3/library/asyncio.html ) + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/send_membership.py b/examples/send_membership.py new file mode 100644 index 0000000000000000000000000000000000000000..cc31ec7c5337a2616a0287d1838a85c06df4e1c6 --- /dev/null +++ b/examples/send_membership.py @@ -0,0 +1,145 @@ +import asyncio +import aiohttp + +import duniterpy.api.bma as bma +from duniterpy.documents import BMAEndpoint, BlockUID, SelfCertification, Membership +from duniterpy.key import SigningKey + + +# CONFIG ####################################### + +# You can either use a complete defined endpoint : [NAME_OF_THE_API] [DOMAIN] [IPv4] [IPv6] [PORT] +# or the simple definition : [NAME_OF_THE_API] [DOMAIN] [PORT] +# Here we use the BASIC_MERKLED_API +BMA_ENDPOINT = "BASIC_MERKLED_API cgeek.fr 9330" + +# Credentials should be prompted or kept in a separate secure file +# create a file with the salt on the first line and the password on the second line +# the script will load them from the file +FROM_CREDENTIALS_FILE = "/home/username/.credentials.txt" + +# Your unique identifier in the Web of Trust +UID = "MyIdentity" + +################################################ + +# Latest duniter-python-api is asynchronous and you have to create an aiohttp session to send request +# ( http://pythonhosted.org/aiohttp ) +AIOHTTP_SESSION = aiohttp.ClientSession() + +async def get_current_block(connection): + """ + Get the current block data + + :param bma.api.ConnectionHandler connection: Connection handler + :rtype: dict + """ + # Here we request for the path blockchain/block/N + return await bma.blockchain.Current(connection).get(AIOHTTP_SESSION) + + +def get_identity_document(current_block, uid, salt, password): + """ + Get an Identity document + + :param dict current_block: Current block data + :param str uid: Unique Identifier + :param str salt: Passphrase of the account + :param str password: Password of the account + + :rtype: SelfCertification + """ + + # get current block BlockStamp + timestamp = BlockUID(current_block['number'], current_block['hash']) + + # create keys from credentials + key = SigningKey(salt, password) + + # create identity document + identity = SelfCertification( + version=2, + currency=current_block['currency'], + pubkey=key.pubkey, + uid=uid, + ts=timestamp, + signature=None + ) + + # sign document + identity.sign([key]) + + return identity + + +def get_membership_document(type, current_block, identity, salt, password): + """ + Get a Membership document + + :param str type: "IN" to ask for membership or "OUT" to cancel membership + :param dict current_block: Current block data + :param SelfCertification identity: Identity document + :param str salt: Passphrase of the account + :param str password: Password of the account + + :rtype: Membership + """ + + # get current block BlockStamp + timestamp = BlockUID(current_block['number'], current_block['hash']) + + # create keys from credentials + key = SigningKey(salt, password) + + # create identity document + membership = Membership( + version=2, + currency=current_block['currency'], + issuer=key.pubkey, + membership_ts=timestamp, + membership_type=type, + uid=identity.uid, + identity_ts=identity.timestamp, + signature=None + ) + + # sign document + membership.sign([key]) + + return membership + +async def main(): + """ + Main code + """ + # connection handler from BMA endpoint + connection = BMAEndpoint.from_inline(BMA_ENDPOINT).conn_handler() + + # capture current block to get version and currency and blockstamp + current_block = await get_current_block(connection) + + # load credentials from a text file + salt, password = open(FROM_CREDENTIALS_FILE).readlines() + + # cleanup newlines + salt, password = salt.strip(), password.strip() + + # create our signed identity document + identity = get_identity_document(current_block, UID, salt, password) + + # create a membership demand document + membership = get_membership_document("IN", current_block, identity, salt, password) + + # send the membership document to the node + data = {membership: membership.signed_raw()} + response = await bma.blockchain.Membership(connection).post(AIOHTTP_SESSION, **data) + + print(response) + response.close() + + +with AIOHTTP_SESSION: + + # Latest duniter-python-api is asynchronous and you have to use asyncio, an asyncio loop and a "as" on the data. + # ( https://docs.python.org/3/library/asyncio.html ) + asyncio.get_event_loop().run_until_complete(main()) diff --git a/examples/send_transaction.py b/examples/send_transaction.py new file mode 100644 index 0000000000000000000000000000000000000000..496516c5ba132d0311d8b9cfa99744c16839f2af --- /dev/null +++ b/examples/send_transaction.py @@ -0,0 +1,171 @@ +import asyncio + +import aiohttp + +import duniterpy.api.bma as bma +from duniterpy.documents import BMAEndpoint, BlockUID, Transaction +from duniterpy.documents.transaction import InputSource, OutputSource, Unlock, SIGParameter +from duniterpy.grammars.output import Condition, SIG +from duniterpy.key import SigningKey + +# CONFIG ####################################### + +# You can either use a complete defined endpoint : [NAME_OF_THE_API] [DOMAIN] [IPv4] [IPv6] [PORT] +# or the simple definition : [NAME_OF_THE_API] [DOMAIN] [PORT] +# Here we use the BASIC_MERKLED_API +BMA_ENDPOINT = "BASIC_MERKLED_API cgeek.fr 9330" + +# Credentials should be prompted or kept in a separate secure file +# create a file with the salt on the first line and the password on the second line +# the script will load them from the file +FROM_CREDENTIALS_FILE = "/home/username/.credentials.txt" + +# Public key of the source account +FROM_PUBKEY = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" + +# Public key of the target account +TO_PUBKEY = "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY" + + +################################################ + + +# Latest duniter-python-api is asynchronous and you have to create an aiohttp session to send request +# ( http://pythonhosted.org/aiohttp ) +AIOHTTP_SESSION = aiohttp.ClientSession() + +# Version of the transaction document +TRANSACTION_VERSION = 3 + +async def get_current_block(connection): + """ + Get the current block data + + :param bma.api.ConnectionHandler connection: Connection handler + :rtype: dict + """ + # Here we request for the path blockchain/block/N + return await bma.blockchain.Current(connection).get(AIOHTTP_SESSION) + +async def get_sources(connection, pubkey): + """ + Get the current block data + + :param bma.api.ConnectionHandler connection: Connection handler + :param str pubkey: Public key of the sources account + + :rtype: dict + """ + # Here we request for the path blockchain/block/N + return await bma.tx.Sources(connection, pubkey).get(AIOHTTP_SESSION) + + +def get_transaction_document(current_block, source, from_pubkey, to_pubkey): + """ + Return a Transaction document + + :param dict current_block: Current block infos + :param dict source: Source to send + :param str from_pubkey: Public key of the issuer + :param str to_pubkey: Public key of the receiver + + :return: Transaction + """ + # list of inputs (sources) + inputs = [ + InputSource( + amount=source['amount'], + base=source['base'], + source=source['type'], + origin_id=source['identifier'], + index=source['noffset'] + ) + ] + + # list of issuers of the inputs + issuers = [ + from_pubkey + ] + + # list of unlocks of the inputs + unlocks = [ + Unlock( + # inputs[index] + index=0, + # unlock inputs[index] if signatures[0] is from public key of issuers[0] + parameters=[SIGParameter(0)] + ) + ] + + # lists of outputs + outputs = [ + OutputSource( + amount=source['amount'], + base=source['base'], + # only the receiver of the output can use it as input in another transaction + conditions=Condition.token(SIG.token(to_pubkey)) + ) + ] + + transaction = Transaction( + version=TRANSACTION_VERSION, + currency=current_block['currency'], + blockstamp=BlockUID(current_block['number'], current_block['hash']), + locktime=0, + issuers=issuers, + inputs=inputs, + unlocks=unlocks, + outputs=outputs, + comment='', + signatures=None + ) + + return transaction + +async def main(): + """ + Main code + """ + # connection handler from BMA endpoint + connection = BMAEndpoint.from_inline(BMA_ENDPOINT).conn_handler() + + # capture current block to get version and currency and blockstamp + current_block = await get_current_block(connection) + + response = await get_sources(connection, FROM_PUBKEY) + + if len(response['sources']) == 0: + print("no sources found for account %s" % FROM_PUBKEY) + exit(1) + + # get the first source + source = response['sources'][0] + + # create the transaction document + transaction = get_transaction_document(current_block, source, FROM_PUBKEY, TO_PUBKEY) + + # load credentials from a text file + salt, password = open(FROM_CREDENTIALS_FILE).readlines() + + # cleanup newlines + salt, password = salt.strip(), password.strip() + + # create keys from credentials + key = SigningKey(salt, password) + + # sign document + transaction.sign([key]) + + # send the Transaction document to the node + data = {'transaction': transaction.signed_raw()} + response = await bma.tx.Process(connection).post(AIOHTTP_SESSION, **data) + + print(response) + + response.close() + +with AIOHTTP_SESSION: + + # Latest duniter-python-api is asynchronous and you have to use asyncio, an asyncio loop and a "as" on the data. + # ( https://docs.python.org/3/library/asyncio.html ) + asyncio.get_event_loop().run_until_complete(main())