diff --git a/duniterpy/__init__.py b/duniterpy/__init__.py index c4b422a824c33ef9d9e2298ace3b014cda202bcd..3fdc4c834e34695f1b9cbe81e3492e0b5e83846e 100644 --- a/duniterpy/__init__.py +++ b/duniterpy/__init__.py @@ -17,8 +17,8 @@ # -__author__ = 'Caner Candan & inso & vit' -__version__ = '0.54.3' -__nonsense__ = 'duniter' +__author__ = "Caner Candan & inso & vit" +__version__ = "0.54.3" +__nonsense__ = "duniter" from . import api, documents, key diff --git a/duniterpy/api/bma/__init__.py b/duniterpy/api/bma/__init__.py index 37ba38208fdcf9c775a434edbef223073a4c9679..9857ef0eaa3160974177683e79c241f3a11a82f9 100644 --- a/duniterpy/api/bma/__init__.py +++ b/duniterpy/api/bma/__init__.py @@ -18,9 +18,8 @@ import logging from . import network, blockchain, tx, wot, node, ud, ws -__all__ = ['network', 'blockchain', 'tx', 'wot', 'node', 'ud', 'ws'] +__all__ = ["network", "blockchain", "tx", "wot", "node", "ud", "ws"] PROTOCOL_VERSION = 2 logger = logging.getLogger("duniter") - diff --git a/duniterpy/api/bma/blockchain.py b/duniterpy/api/bma/blockchain.py index ab1fb9737f8346537f95e91965f8eb1ebbf27587..db62de6407d5f4a197e4d8fa213df533ab6df55d 100644 --- a/duniterpy/api/bma/blockchain.py +++ b/duniterpy/api/bma/blockchain.py @@ -24,138 +24,79 @@ from duniterpy.api.client import Client, RESPONSE_AIOHTTP logger = logging.getLogger("duniter/blockchain") -MODULE = 'blockchain' +MODULE = "blockchain" BLOCK_SCHEMA = { "type": "object", "properties": { - "version": { - "type": "number" - }, - "currency": { - "type": "string" - }, - "nonce": { - "type": "number" - }, - "number": { - "type": "number" - }, - "time": { - "type": "number" - }, - "medianTime": { - "type": "number" - }, - "dividend": { - "type": ["number", "null"] - }, - "monetaryMass": { - "type": ["number", "null"] - }, - "issuer": { - "type": "string" - }, - "previousHash": { - "type": ["string", "null"] - }, - "previousIssuer": { - "type": ["string", "null"] - }, - "membersCount": { - "type": "number" - }, - "hash": { - "type": "string" - }, - "inner_hash": { - "type": "string" - }, - "identities": { - "type": "array", - "items": { - "type": "string" - } - }, - "joiners": { - "type": "array", - "items": { - "type": "string" - } - }, - "leavers": { - "type": "array", - "items": { - "type": "string" - } - }, - "revoked": { - "type": "array", - "items": { - "type": "string" - } - }, - "excluded": { - "type": "array", - "items": { - "type": "string" - } - }, - "certifications": { - "type": "array", - "items": { - "type": "string" - } - }, + "version": {"type": "number"}, + "currency": {"type": "string"}, + "nonce": {"type": "number"}, + "number": {"type": "number"}, + "time": {"type": "number"}, + "medianTime": {"type": "number"}, + "dividend": {"type": ["number", "null"]}, + "monetaryMass": {"type": ["number", "null"]}, + "issuer": {"type": "string"}, + "previousHash": {"type": ["string", "null"]}, + "previousIssuer": {"type": ["string", "null"]}, + "membersCount": {"type": "number"}, + "hash": {"type": "string"}, + "inner_hash": {"type": "string"}, + "identities": {"type": "array", "items": {"type": "string"}}, + "joiners": {"type": "array", "items": {"type": "string"}}, + "leavers": {"type": "array", "items": {"type": "string"}}, + "revoked": {"type": "array", "items": {"type": "string"}}, + "excluded": {"type": "array", "items": {"type": "string"}}, + "certifications": {"type": "array", "items": {"type": "string"}}, "transactions": { "type": "array", "items": { "type": "object", "properties": { - "signatures": { - "type": "array" - }, - "version": { - "type": "number" - }, - "currency": { - "type": "string" - }, - "issuers": { - "type": "array", - "items": { - "type": "string" - } - }, - "inputs": { - "type": "array", - "items": { - "type": "string" - } - }, - "unlocks": { - "type": "array", - "items": { - "type": "string" - } - }, - "outputs": { - "type": "array", - "item": { - "type": "string" - } - } + "signatures": {"type": "array"}, + "version": {"type": "number"}, + "currency": {"type": "string"}, + "issuers": {"type": "array", "items": {"type": "string"}}, + "inputs": {"type": "array", "items": {"type": "string"}}, + "unlocks": {"type": "array", "items": {"type": "string"}}, + "outputs": {"type": "array", "item": {"type": "string"}}, }, - "required": ["signatures", "version", "currency", "issuers", "inputs", "outputs"] - } - }, - "signature": { - "type": "string" + "required": [ + "signatures", + "version", + "currency", + "issuers", + "inputs", + "outputs", + ], + }, }, + "signature": {"type": "string"}, }, - "required": ["version", "currency", "nonce", "number", "time", "medianTime", "dividend", "monetaryMass", - "issuer", "previousHash", "previousIssuer", "membersCount", "hash", "inner_hash", "identities", - "joiners", "leavers", "excluded", "certifications", "transactions", "signature"] + "required": [ + "version", + "currency", + "nonce", + "number", + "time", + "medianTime", + "dividend", + "monetaryMass", + "issuer", + "previousHash", + "previousIssuer", + "membersCount", + "hash", + "inner_hash", + "identities", + "joiners", + "leavers", + "excluded", + "certifications", + "transactions", + "signature", + ], } BLOCK_NUMBERS_SCHEMA = { @@ -163,192 +104,123 @@ BLOCK_NUMBERS_SCHEMA = { "properties": { "result": { "type": "object", - "properties": { - "blocks": { - "type": "array", - "items": { - "type": "number" - } - }, - }, - "required": ["blocks"] + "properties": {"blocks": {"type": "array", "items": {"type": "number"}}}, + "required": ["blocks"], } }, - "required": ["result"] + "required": ["result"], } PARAMETERS_SCHEMA = { "type": "object", - "properties": - { - "currency": { - "type": "string" - }, - "c": { - "type": "number" - }, - "dt": { - "type": "number" - }, - "ud0": { - "type": "number" - }, - "sigPeriod": { - "type": "number" - }, - "sigStock": { - "type": "number" - }, - "sigWindow": { - "type": "number" - }, - "sigValidity": { - "type": "number" - }, - "sigQty": { - "type": "number" - }, - "sigReplay": { - "type": "number" - }, - "xpercent": { - "type": "number" - }, - "msValidity": { - "type": "number" - }, - "msPeriod": { - "type": "number" - }, - "stepMax": { - "type": "number" - }, - "medianTimeBlocks": { - "type": "number" - }, - "avgGenTime": { - "type": "number" - }, - "dtDiffEval": { - "type": "number" - }, - "percentRot": { - "type": "number" - }, - "udTime0": { - "type": "number" - }, - "udReevalTime0": { - "type": "number" - }, - "dtReeval": { - "type": "number" - } - }, - "required": ["currency", "c", "dt", "ud0", "sigPeriod", "sigValidity", "sigQty", "xpercent", "sigStock", - "sigWindow", "msValidity", "stepMax", "medianTimeBlocks", - "avgGenTime", "dtDiffEval", "percentRot", "udTime0", "udReevalTime0", "dtReeval"] + "properties": { + "currency": {"type": "string"}, + "c": {"type": "number"}, + "dt": {"type": "number"}, + "ud0": {"type": "number"}, + "sigPeriod": {"type": "number"}, + "sigStock": {"type": "number"}, + "sigWindow": {"type": "number"}, + "sigValidity": {"type": "number"}, + "sigQty": {"type": "number"}, + "sigReplay": {"type": "number"}, + "xpercent": {"type": "number"}, + "msValidity": {"type": "number"}, + "msPeriod": {"type": "number"}, + "stepMax": {"type": "number"}, + "medianTimeBlocks": {"type": "number"}, + "avgGenTime": {"type": "number"}, + "dtDiffEval": {"type": "number"}, + "percentRot": {"type": "number"}, + "udTime0": {"type": "number"}, + "udReevalTime0": {"type": "number"}, + "dtReeval": {"type": "number"}, + }, + "required": [ + "currency", + "c", + "dt", + "ud0", + "sigPeriod", + "sigValidity", + "sigQty", + "xpercent", + "sigStock", + "sigWindow", + "msValidity", + "stepMax", + "medianTimeBlocks", + "avgGenTime", + "dtDiffEval", + "percentRot", + "udTime0", + "udReevalTime0", + "dtReeval", + ], } MEMBERSHIPS_SCHEMA = { "type": "object", - "properties": - { - "pubkey": { - "type": "string" - }, - "uid": { - "type": "string", - }, - "sigDate": { - "type": "string" + "properties": { + "pubkey": {"type": "string"}, + "uid": {"type": "string"}, + "sigDate": {"type": "string"}, + "memberships": { + "type": "array", + "items": { + "type": "object", + "properties": { + "version": {"type": "number"}, + "currency": {"type": "string"}, + "membership": {"type": "string"}, + "blockNumber": {"type": "number"}, + "written": {"type": ["number", "null"]}, + }, + "required": [ + "version", + "currency", + "membership", + "blockNumber", + "blockHash", + "written", + ], }, - "memberships": { - "type": "array", - "items": { - "type": "object", - "properties": { - "version": { - "type": "number" - }, - "currency": { - "type": "string" - }, - "membership": { - "type": "string" - }, - "blockNumber": { - "type": "number" - }, - "written": { - "type": ["number", "null"] - } - }, - "required": ["version", "currency", "membership", "blockNumber", "blockHash", "written"] - } - } }, - "required": ["pubkey", "uid", "sigDate", "memberships"] + }, + "required": ["pubkey", "uid", "sigDate", "memberships"], } -BLOCKS_SCHEMA = { - "type": "array", - "items": BLOCK_SCHEMA -} +BLOCKS_SCHEMA = {"type": "array", "items": BLOCK_SCHEMA} HARDSHIP_SCHEMA = { "type": "object", - "properties": { - "block": { - "type": "number" - }, - "level": { - "type": "number" - } - }, - "required": ["block", "level"] + "properties": {"block": {"type": "number"}, "level": {"type": "number"}}, + "required": ["block", "level"], } DIFFICULTIES_SCHEMA = { - "type": "object", - "properties": { - "block": { - "type": "number" - }, - "levels": { - "type": "array", - "items": [ - { - "type": "object", - "properties": { - "uid": { - "type": "string" - }, - "level": { - "type": "number" - } - }, - "required": [ - "uid", - "level" - ] - } - ] - } - }, - "required": [ - "block", - "levels" - ] -} - -BRANCHES_SCHEMA = { "type": "object", "properties": { - "blocks": BLOCKS_SCHEMA - } + "block": {"type": "number"}, + "levels": { + "type": "array", + "items": [ + { + "type": "object", + "properties": { + "uid": {"type": "string"}, + "level": {"type": "number"}, + }, + "required": ["uid", "level"], + } + ], + }, + }, + "required": ["block", "levels"], } +BRANCHES_SCHEMA = {"type": "object", "properties": {"blocks": BLOCKS_SCHEMA}} + async def parameters(client: Client) -> dict: """ @@ -357,7 +229,7 @@ async def parameters(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/parameters', schema=PARAMETERS_SCHEMA) + return await client.get(MODULE + "/parameters", schema=PARAMETERS_SCHEMA) async def memberships(client: Client, search: str) -> dict: @@ -368,7 +240,9 @@ async def memberships(client: Client, search: str) -> dict: :param search: UID/Public key :return: """ - return await client.get(MODULE + '/memberships/%s' % search, schema=MEMBERSHIPS_SCHEMA) + return await client.get( + MODULE + "/memberships/%s" % search, schema=MEMBERSHIPS_SCHEMA + ) async def membership(client: Client, membership_signed_raw: str) -> ClientResponse: @@ -379,7 +253,11 @@ async def membership(client: Client, membership_signed_raw: str) -> ClientRespon :param membership_signed_raw: Membership signed raw document :return: """ - return await client.post(MODULE + '/membership', {'membership': membership_signed_raw}, rtype=RESPONSE_AIOHTTP) + return await client.post( + MODULE + "/membership", + {"membership": membership_signed_raw}, + rtype=RESPONSE_AIOHTTP, + ) async def current(client: Client) -> dict: @@ -389,11 +267,12 @@ async def current(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/current', schema=BLOCK_SCHEMA) + return await client.get(MODULE + "/current", schema=BLOCK_SCHEMA) -async def block(client: Client, number: int = 0, block_raw: str = None, signature: str = None) -> Union[dict, - ClientResponse]: +async def block( + client: Client, number: int = 0, block_raw: str = None, signature: str = None +) -> Union[dict, ClientResponse]: """ GET/POST a block from/to the blockchain @@ -405,10 +284,13 @@ async def block(client: Client, number: int = 0, block_raw: str = None, signatur """ # POST block if block_raw is not None and signature is not None: - return await client.post(MODULE + '/block', {'block': block_raw, 'signature': signature}, - rtype=RESPONSE_AIOHTTP) + return await client.post( + MODULE + "/block", + {"block": block_raw, "signature": signature}, + rtype=RESPONSE_AIOHTTP, + ) # GET block - return await client.get(MODULE + '/block/%d' % number, schema=BLOCK_SCHEMA) + return await client.get(MODULE + "/block/%d" % number, schema=BLOCK_SCHEMA) async def blocks(client: Client, count: int, start: int) -> list: @@ -423,7 +305,9 @@ async def blocks(client: Client, count: int, start: int) -> list: assert type(count) is int assert type(start) is int - return await client.get(MODULE + '/blocks/%d/%d' % (count, start), schema=BLOCKS_SCHEMA) + return await client.get( + MODULE + "/blocks/%d/%d" % (count, start), schema=BLOCKS_SCHEMA + ) async def hardship(client: Client, pubkey: str) -> dict: @@ -434,7 +318,7 @@ async def hardship(client: Client, pubkey: str) -> dict: :param pubkey: Public key of the member :return: """ - return await client.get(MODULE + '/hardship/%s' % pubkey, schema=HARDSHIP_SCHEMA) + return await client.get(MODULE + "/hardship/%s" % pubkey, schema=HARDSHIP_SCHEMA) async def difficulties(client: Client) -> dict: @@ -444,7 +328,7 @@ async def difficulties(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/difficulties', schema=DIFFICULTIES_SCHEMA) + return await client.get(MODULE + "/difficulties", schema=DIFFICULTIES_SCHEMA) async def branches(client: Client) -> list: @@ -454,7 +338,7 @@ async def branches(client: Client) -> list: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/branches', schema=BRANCHES_SCHEMA) + return await client.get(MODULE + "/branches", schema=BRANCHES_SCHEMA) async def newcomers(client: Client) -> dict: @@ -464,7 +348,7 @@ async def newcomers(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/with/newcomers', schema=BLOCK_NUMBERS_SCHEMA) + return await client.get(MODULE + "/with/newcomers", schema=BLOCK_NUMBERS_SCHEMA) async def certifications(client: Client) -> dict: @@ -474,7 +358,7 @@ async def certifications(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/with/certs', schema=BLOCK_NUMBERS_SCHEMA) + return await client.get(MODULE + "/with/certs", schema=BLOCK_NUMBERS_SCHEMA) async def joiners(client: Client) -> dict: @@ -484,7 +368,7 @@ async def joiners(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/with/joiners', schema=BLOCK_NUMBERS_SCHEMA) + return await client.get(MODULE + "/with/joiners", schema=BLOCK_NUMBERS_SCHEMA) async def actives(client: Client) -> dict: @@ -494,7 +378,7 @@ async def actives(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/with/actives', schema=BLOCK_NUMBERS_SCHEMA) + return await client.get(MODULE + "/with/actives", schema=BLOCK_NUMBERS_SCHEMA) async def leavers(client: Client) -> dict: @@ -504,7 +388,7 @@ async def leavers(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/with/leavers', schema=BLOCK_NUMBERS_SCHEMA) + return await client.get(MODULE + "/with/leavers", schema=BLOCK_NUMBERS_SCHEMA) async def revoked(client: Client) -> dict: @@ -514,7 +398,7 @@ async def revoked(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/with/excluded', schema=BLOCK_NUMBERS_SCHEMA) + return await client.get(MODULE + "/with/excluded", schema=BLOCK_NUMBERS_SCHEMA) async def excluded(client: Client) -> dict: @@ -524,7 +408,7 @@ async def excluded(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/with/excluded', schema=BLOCK_NUMBERS_SCHEMA) + return await client.get(MODULE + "/with/excluded", schema=BLOCK_NUMBERS_SCHEMA) async def ud(client: Client) -> dict: @@ -534,7 +418,7 @@ async def ud(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/with/ud', schema=BLOCK_NUMBERS_SCHEMA) + return await client.get(MODULE + "/with/ud", schema=BLOCK_NUMBERS_SCHEMA) async def tx(client: Client) -> dict: @@ -544,4 +428,4 @@ async def tx(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/with/tx', schema=BLOCK_NUMBERS_SCHEMA) + return await client.get(MODULE + "/with/tx", schema=BLOCK_NUMBERS_SCHEMA) diff --git a/duniterpy/api/bma/network.py b/duniterpy/api/bma/network.py index e8b9818d686d7b2299089a023e94b4dc68f15cba..bdf54d84b717e2fbdcd2756490040acbc275912a 100644 --- a/duniterpy/api/bma/network.py +++ b/duniterpy/api/bma/network.py @@ -23,84 +23,44 @@ from duniterpy.api.client import Client, RESPONSE_AIOHTTP logger = logging.getLogger("duniter/network") -MODULE = 'network' +MODULE = "network" PEERING_SCHEMA = { "type": "object", "properties": { - "version": { - "type": ["number", "string"] - }, - "currency": { - "type": "string" - }, - "pubkey": { - "type": "string" - }, - "endpoints": { - "type": "array", - "items": { - "type": "string" - } - }, - "signature": { - "type": "string" - } + "version": {"type": ["number", "string"]}, + "currency": {"type": "string"}, + "pubkey": {"type": "string"}, + "endpoints": {"type": "array", "items": {"type": "string"}}, + "signature": {"type": "string"}, }, - "required": ["version", "currency", "pubkey", "endpoints", "signature"] + "required": ["version", "currency", "pubkey", "endpoints", "signature"], } PEERS_SCHEMA = schema = { "type": ["object"], "properties": { - "depth": { - "type": "number" - }, - "nodesCount": { - "type": "number" - }, - "leavesCount": { - "type": "number" - }, - "root": { - "type": "string" - }, - "hash": { - "type": "string" - }, + "depth": {"type": "number"}, + "nodesCount": {"type": "number"}, + "leavesCount": {"type": "number"}, + "root": {"type": "string"}, + "hash": {"type": "string"}, "value": { "type": "object", "properties": { - "version": { - "type": "string" - }, - "currency": { - "type": "string" - }, - "pubkey": { - "type": "string" - }, - "endpoints": { - "type": "array", - "items": { - "type": "string" - } - }, - "signature": { - "type": "string" - } + "version": {"type": "string"}, + "currency": {"type": "string"}, + "pubkey": {"type": "string"}, + "endpoints": {"type": "array", "items": {"type": "string"}}, + "signature": {"type": "string"}, }, - "required": ["version", "currency", "pubkey", "endpoints", "signature"] - } + "required": ["version", "currency", "pubkey", "endpoints", "signature"], + }, }, "oneOf": [ - { - "required": ["depth", "nodesCount", "leavesCount", "root"] - }, - { - "required": ["hash", "value"] - } - ] + {"required": ["depth", "nodesCount", "leavesCount", "root"]}, + {"required": ["hash", "value"]}, + ], } WS2P_HEADS_SCHEMA = { @@ -111,27 +71,17 @@ WS2P_HEADS_SCHEMA = { "items": { "type": "object", "properties": { - "message": { - "type": "string" - }, - "sig": { - "type": "string", - }, - "messageV2": { - "type": "string" - }, - "sigV2": { - "type": "string", - }, - "step": { - "type": "number", - }, + "message": {"type": "string"}, + "sig": {"type": "string"}, + "messageV2": {"type": "string"}, + "sigV2": {"type": "string"}, + "step": {"type": "number"}, }, - "required": ["messageV2", "sigV2", "step"] - } + "required": ["messageV2", "sigV2", "step"], + }, } }, - "required": ["heads"] + "required": ["heads"], } @@ -142,7 +92,7 @@ async def peering(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/peering', schema=PEERING_SCHEMA) + return await client.get(MODULE + "/peering", schema=PEERING_SCHEMA) async def peers(client: Client, leaves: bool = False, leaf: str = "") -> dict: @@ -155,9 +105,13 @@ async def peers(client: Client, leaves: bool = False, leaf: str = "") -> dict: :return: """ if leaves is True: - response = await client.get(MODULE + '/peering/peers', {"leaves": "true"}, schema=PEERS_SCHEMA) + response = await client.get( + MODULE + "/peering/peers", {"leaves": "true"}, schema=PEERS_SCHEMA + ) else: - response = await client.get(MODULE + '/peering/peers', {"leaf": leaf}, schema=PEERS_SCHEMA) + response = await client.get( + MODULE + "/peering/peers", {"leaf": leaf}, schema=PEERS_SCHEMA + ) return response @@ -169,7 +123,9 @@ async def peer(client: Client, peer_signed_raw: str) -> ClientResponse: :param peer_signed_raw: Peer signed raw document :return: """ - return await client.post(MODULE + '/peering/peers', {'peer': peer_signed_raw}, rtype=RESPONSE_AIOHTTP) + return await client.post( + MODULE + "/peering/peers", {"peer": peer_signed_raw}, rtype=RESPONSE_AIOHTTP + ) async def ws2p_heads(client: Client) -> dict: @@ -179,4 +135,4 @@ async def ws2p_heads(client: Client) -> dict: :param client: Client to connect to the api :rtype: dict """ - return await client.get(MODULE + '/ws2p/heads', schema=WS2P_HEADS_SCHEMA) + return await client.get(MODULE + "/ws2p/heads", schema=WS2P_HEADS_SCHEMA) diff --git a/duniterpy/api/bma/node.py b/duniterpy/api/bma/node.py index db9f3730bb4d3c23ef3f2aba53b548654ac29cba..95d45dd2f36e52aef9cc7d47b64900b1eda82520 100644 --- a/duniterpy/api/bma/node.py +++ b/duniterpy/api/bma/node.py @@ -21,7 +21,7 @@ from duniterpy.api.client import Client logger = logging.getLogger("duniter/node") -MODULE = 'node' +MODULE = "node" SUMMARY_SCHEMA = { "type": "object", @@ -29,33 +29,20 @@ SUMMARY_SCHEMA = { "duniter": { "type": "object", "properties": { - "software": { - "type": "string" - }, - "version": { - "type": "string", - }, - "forkWindowSize": { - "type": "number" - } + "software": {"type": "string"}, + "version": {"type": "string"}, + "forkWindowSize": {"type": "number"}, }, - "required": ["software", "version"] - }, + "required": ["software", "version"], + } }, - "required": ["duniter"] + "required": ["duniter"], } SANDBOX_SCHEMA = { "type": "object", - "properties": { - "size": { - "type": "number" - }, - "free": { - "type": "number" - } - }, - "required": ["size", "free"] + "properties": {"size": {"type": "number"}, "free": {"type": "number"}}, + "required": ["size", "free"], } SANDBOXES_SCHEMA = { @@ -63,9 +50,9 @@ SANDBOXES_SCHEMA = { "properties": { "identities": SANDBOX_SCHEMA, "memberships": SANDBOX_SCHEMA, - "transactions": SANDBOX_SCHEMA + "transactions": SANDBOX_SCHEMA, }, - "required": ["identities", "memberships", "transactions"] + "required": ["identities", "memberships", "transactions"], } @@ -76,7 +63,7 @@ async def summary(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/summary', schema=SUMMARY_SCHEMA) + return await client.get(MODULE + "/summary", schema=SUMMARY_SCHEMA) async def sandboxes(client: Client) -> dict: @@ -86,4 +73,4 @@ async def sandboxes(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/sandboxes', schema=SANDBOXES_SCHEMA) + return await client.get(MODULE + "/sandboxes", schema=SANDBOXES_SCHEMA) diff --git a/duniterpy/api/bma/tx.py b/duniterpy/api/bma/tx.py index 7045e7a95bc51f9537b680d99121ffadc931761e..9bb4db60e43c32a36e51eeef122aee476dad8ffc 100644 --- a/duniterpy/api/bma/tx.py +++ b/duniterpy/api/bma/tx.py @@ -23,38 +23,24 @@ from duniterpy.api.client import Client, RESPONSE_AIOHTTP logger = logging.getLogger("duniter/tx") -MODULE = 'tx' +MODULE = "tx" HISTORY_SCHEMA = { "type": "object", "properties": { - "currency": { - "type": "string" - }, - "pubkey": { - "type": "string" - }, + "currency": {"type": "string"}, + "pubkey": {"type": "string"}, "history": { "type": "object", "properties": { - "sent": { - "$ref": "#/definitions/transaction_data" - }, - "received": { - "$ref": "#/definitions/transaction_data" - }, - "sending": { - "$ref": "#/definitions/transactioning_data" - }, - "receiving": { - "$ref": "#/definitions/transactioning_data" - }, - "pending": { - "$ref": "#/definitions/transactioning_data" - } + "sent": {"$ref": "#/definitions/transaction_data"}, + "received": {"$ref": "#/definitions/transaction_data"}, + "sending": {"$ref": "#/definitions/transactioning_data"}, + "receiving": {"$ref": "#/definitions/transactioning_data"}, + "pending": {"$ref": "#/definitions/transactioning_data"}, }, - "required": ["sent", "received", "sending", "receiving", "pending"] - } + "required": ["sent", "received", "sending", "receiving", "pending"], + }, }, "definitions": { "transaction_data": { @@ -62,144 +48,80 @@ HISTORY_SCHEMA = { "items": { "type": "object", "properties": { - "version": { - "type": "number" - }, - "issuers": { - "type": "array", - "items": { - "type": "string" - } - }, - "inputs": { - "type": "array", - "items": { - "type": "string" - } - }, - "outputs": { - "type": "array", - "items": { - "type": "string" - } - }, - "unlocks": { - "type": "array", - "items": { - "type": "string" - } - }, - "comment": { - "type": "string" - }, - "signatures": { - "type": "array", - "items": { - "type": "string" - } - }, - "hash": { - "type": "string" - }, - "block_number": { - "type": "number" - }, - "time": { - "type": "number" - } + "version": {"type": "number"}, + "issuers": {"type": "array", "items": {"type": "string"}}, + "inputs": {"type": "array", "items": {"type": "string"}}, + "outputs": {"type": "array", "items": {"type": "string"}}, + "unlocks": {"type": "array", "items": {"type": "string"}}, + "comment": {"type": "string"}, + "signatures": {"type": "array", "items": {"type": "string"}}, + "hash": {"type": "string"}, + "block_number": {"type": "number"}, + "time": {"type": "number"}, }, - "required": ["version", "issuers", "inputs", "outputs", - "comment", "signatures", "hash", "block_number", "time"] - } + "required": [ + "version", + "issuers", + "inputs", + "outputs", + "comment", + "signatures", + "hash", + "block_number", + "time", + ], + }, }, "transactioning_data": { "type": "array", "items": { "type": "object", "properties": { - "version": { - "type": "number" - }, - "issuers": { - "type": "array", - "items": { - "type": "string" - } - }, - "inputs": { - "type": "array", - "items": { - "type": "string" - } - }, - "outputs": { - "type": "array", - "items": { - "type": "string" - } - }, - "unlocks": { - "type": "array", - "items": { - "type": "string" - } - }, - "comment": { - "type": "string" - }, - "signatures": { - "type": "array", - "items": { - "type": "string" - } - }, - "hash": { - "type": "string" - }, + "version": {"type": "number"}, + "issuers": {"type": "array", "items": {"type": "string"}}, + "inputs": {"type": "array", "items": {"type": "string"}}, + "outputs": {"type": "array", "items": {"type": "string"}}, + "unlocks": {"type": "array", "items": {"type": "string"}}, + "comment": {"type": "string"}, + "signatures": {"type": "array", "items": {"type": "string"}}, + "hash": {"type": "string"}, }, - "required": ["version", "issuers", "inputs", "outputs", - "comment", "signatures", "hash"] - } - } + "required": [ + "version", + "issuers", + "inputs", + "outputs", + "comment", + "signatures", + "hash", + ], + }, + }, }, - "required": ["currency", "pubkey", "history"] + "required": ["currency", "pubkey", "history"], } SOURCES_SCHEMA = { "type": "object", "properties": { - "currency": { - "type": "string" - }, - "pubkey": { - "type": "string" - }, + "currency": {"type": "string"}, + "pubkey": {"type": "string"}, "sources": { "type": "array", "items": { "type": "object", "properties": { - "type": { - "type": "string" - }, - "noffset": { - "type": "number" - }, - "identifier": { - "type": "string" - }, - "amount": { - "type": "number" - }, - "base": { - "type": "number" - } + "type": {"type": "string"}, + "noffset": {"type": "number"}, + "identifier": {"type": "string"}, + "amount": {"type": "number"}, + "base": {"type": "number"}, }, - "required": ["type", "noffset", "identifier", "amount", "base"] - } - } + "required": ["type", "noffset", "identifier", "amount", "base"], + }, + }, }, - "required": ["currency", "pubkey", "sources"] + "required": ["currency", "pubkey", "sources"], } @@ -211,7 +133,7 @@ async def history(client: Client, pubkey: str) -> dict: :param pubkey: Public key :return: """ - return await client.get(MODULE + '/history/%s' % pubkey, schema=HISTORY_SCHEMA) + return await client.get(MODULE + "/history/%s" % pubkey, schema=HISTORY_SCHEMA) async def process(client: Client, transaction_signed_raw: str) -> ClientResponse: @@ -222,7 +144,11 @@ async def process(client: Client, transaction_signed_raw: str) -> ClientResponse :param transaction_signed_raw: Transaction signed raw document :return: """ - return await client.post(MODULE + '/process', {'transaction': transaction_signed_raw}, rtype=RESPONSE_AIOHTTP) + return await client.post( + MODULE + "/process", + {"transaction": transaction_signed_raw}, + rtype=RESPONSE_AIOHTTP, + ) async def sources(client: Client, pubkey: str) -> dict: @@ -233,7 +159,7 @@ async def sources(client: Client, pubkey: str) -> dict: :param pubkey: Public key :return: """ - return await client.get(MODULE + '/sources/%s' % pubkey, schema=SOURCES_SCHEMA) + return await client.get(MODULE + "/sources/%s" % pubkey, schema=SOURCES_SCHEMA) async def pending(client: Client, pubkey: str) -> dict: @@ -244,7 +170,9 @@ async def pending(client: Client, pubkey: str) -> dict: :param pubkey: Public key :return: """ - return await client.get(MODULE + '/history/%s/pending' % pubkey, schema=HISTORY_SCHEMA) + return await client.get( + MODULE + "/history/%s/pending" % pubkey, schema=HISTORY_SCHEMA + ) async def blocks(client: Client, pubkey: str, start: int, end: int) -> dict: @@ -257,7 +185,10 @@ async def blocks(client: Client, pubkey: str, start: int, end: int) -> dict: :param end: End to block number :return: """ - return await client.get(MODULE + '/history/%s/blocks/%s/%s' % (pubkey, start, end), schema=HISTORY_SCHEMA) + return await client.get( + MODULE + "/history/%s/blocks/%s/%s" % (pubkey, start, end), + schema=HISTORY_SCHEMA, + ) async def times(client: Client, pubkey: str, start: int, end: int) -> dict: @@ -270,4 +201,6 @@ async def times(client: Client, pubkey: str, start: int, end: int) -> dict: :param end: End to timestamp :return: """ - return await client.get(MODULE + '/history/%s/times/%s/%s' % (pubkey, start, end), schema=HISTORY_SCHEMA) + return await client.get( + MODULE + "/history/%s/times/%s/%s" % (pubkey, start, end), schema=HISTORY_SCHEMA + ) diff --git a/duniterpy/api/bma/ud.py b/duniterpy/api/bma/ud.py index 9fed33be5f79ae3e31117abc8c100ff7d6bde443..4b2653cb25b2534b78ab38fa80e11dab6f16fccf 100644 --- a/duniterpy/api/bma/ud.py +++ b/duniterpy/api/bma/ud.py @@ -21,17 +21,13 @@ from duniterpy.api.client import Client logger = logging.getLogger("duniter/ud") -MODULE = 'ud' +MODULE = "ud" UD_SCHEMA = { "type": "object", "properties": { - "currency": { - "type": "string" - }, - "pubkey": { - "type": "string" - }, + "currency": {"type": "string"}, + "pubkey": {"type": "string"}, "history": { "type": "object", "properties": { @@ -40,29 +36,18 @@ UD_SCHEMA = { "items": { "type": "object", "properties": { - "block_number": { - "type": "number" - }, - - "consumed": { - "type": "boolean" - }, - "time": { - "type": "number" - }, - "amount": { - "type": "number" - }, - "base": { - "type": "number" - }, - } - } + "block_number": {"type": "number"}, + "consumed": {"type": "boolean"}, + "time": {"type": "number"}, + "amount": {"type": "number"}, + "base": {"type": "number"}, + }, + }, } - } - } + }, + }, }, - "required": ["currency", "pubkey", "history"] + "required": ["currency", "pubkey", "history"], } @@ -74,4 +59,4 @@ async def history(client: Client, pubkey: str) -> dict: :param pubkey: Public key of the member :return: """ - return await client.get(MODULE + '/history/%s' % pubkey, schema=UD_SCHEMA) + return await client.get(MODULE + "/history/%s" % pubkey, schema=UD_SCHEMA) diff --git a/duniterpy/api/bma/wot.py b/duniterpy/api/bma/wot.py index 94422413a450cf3c9ae3493d684b5824cbdaaae9..4befb4b01d5bee8b7ea10230ceef151e5d155920 100644 --- a/duniterpy/api/bma/wot.py +++ b/duniterpy/api/bma/wot.py @@ -23,81 +23,61 @@ from duniterpy.api.client import Client, RESPONSE_AIOHTTP logger = logging.getLogger("duniter/wot") -MODULE = 'wot' +MODULE = "wot" CERTIFICATIONS_SCHEMA = { "type": "object", "properties": { - "pubkey": { - "type": "string" - }, - "uid": { - "type": "string" - }, - "isMember": { - "type": "boolean" - }, + "pubkey": {"type": "string"}, + "uid": {"type": "string"}, + "isMember": {"type": "boolean"}, "certifications": { "type": "array", "items": { "type": "object", "properties": { - "pubkey": { - "type": "string" - }, - "uid": { - "type": "string" - }, + "pubkey": {"type": "string"}, + "uid": {"type": "string"}, "cert_time": { "type": "object", "properties": { - "block": { - "type": "number" - }, - "medianTime": { - "type": "number" - } + "block": {"type": "number"}, + "medianTime": {"type": "number"}, }, - "required": ["block", "medianTime"] - }, - "sigDate": { - "type": "string" + "required": ["block", "medianTime"], }, + "sigDate": {"type": "string"}, "written": { "oneOf": [ { "type": "object", "properties": { - "number": { - "type": "number", - }, - "hash": { - "type": "string" - } + "number": {"type": "number"}, + "hash": {"type": "string"}, }, - "required": ["number", "hash"] + "required": ["number", "hash"], }, - { - "type": "null" - } + {"type": "null"}, ] }, - "isMember": { - "type": "boolean" - }, - "wasMember": { - "type": "boolean" - }, - "signature": { - "type": "string" - } + "isMember": {"type": "boolean"}, + "wasMember": {"type": "boolean"}, + "signature": {"type": "string"}, }, - "required": ["pubkey", "uid", "cert_time", "sigDate", - "written", "wasMember", "isMember", "signature"] - } - } + "required": [ + "pubkey", + "uid", + "cert_time", + "sigDate", + "written", + "wasMember", + "isMember", + "signature", + ], + }, + }, }, - "required": ["pubkey", "uid", "isMember", "certifications"] + "required": ["pubkey", "uid", "isMember", "certifications"], } MEMBERS_SCHEMA = { @@ -107,16 +87,12 @@ MEMBERS_SCHEMA = { "type": "array", "items": { "type": "object", - "properties": { - "pubkey": { - "type": "string" - } - }, - "required": ["pubkey"] - } + "properties": {"pubkey": {"type": "string"}}, + "required": ["pubkey"], + }, } }, - "required": ["results"] + "required": ["results"], } REQUIREMENTS_SCHEMA = { @@ -127,190 +103,130 @@ REQUIREMENTS_SCHEMA = { "items": { "type": "object", "properties": { - "pubkey": { - "type": "string" - }, - "uid": { - "type": "string" - }, + "pubkey": {"type": "string"}, + "uid": {"type": "string"}, "meta": { "type": "object", - "properties": { - "timestamp": { - "type": "string" - } - }, - "required": ["timestamp"] - }, - "outdistanced": { - "type": "boolean" + "properties": {"timestamp": {"type": "string"}}, + "required": ["timestamp"], }, + "outdistanced": {"type": "boolean"}, "certifications": { "type": "array", "items": { "type": "object", "properties": { - "from": { - "type": "string" - }, - "to": { - "type": "string" - }, - "expiresIn": { - "type": "number" - } + "from": {"type": "string"}, + "to": {"type": "string"}, + "expiresIn": {"type": "number"}, }, - "required": ["from", "to", "expiresIn"] - } - }, - "membershipPendingExpiresIn": { - "type": "number" - }, - "membershipExpiresIn": { - "type": "number" - }, - "wasMember": { - "type": "boolean" - }, - "isSentry": { - "type": "boolean" - }, - "revoked": { - "type": "boolean" - }, - "revokation_sig": { - "type": ["string", "null"] - }, - "revoked_on": { - "type": ["number", "null"] + "required": ["from", "to", "expiresIn"], + }, }, + "membershipPendingExpiresIn": {"type": "number"}, + "membershipExpiresIn": {"type": "number"}, + "wasMember": {"type": "boolean"}, + "isSentry": {"type": "boolean"}, + "revoked": {"type": "boolean"}, + "revokation_sig": {"type": ["string", "null"]}, + "revoked_on": {"type": ["number", "null"]}, }, - "required": ["pubkey", "uid", "meta", "outdistanced", "certifications", "membershipPendingExpiresIn", - "membershipExpiresIn", "wasMember", "isSentry", "revoked", "revoked_on", "revocation_sig"] - } + "required": [ + "pubkey", + "uid", + "meta", + "outdistanced", + "certifications", + "membershipPendingExpiresIn", + "membershipExpiresIn", + "wasMember", + "isSentry", + "revoked", + "revoked_on", + "revocation_sig", + ], + }, } }, - "required": ["identities"] + "required": ["identities"], } LOOKUP_SCHEMA = { "type": "object", "definitions": { - "meta_data": { - "type": "object", - "properties": { - "timestamp": { - "type": "string" - } - } - }, + "meta_data": {"type": "object", "properties": {"timestamp": {"type": "string"}}} }, "properties": { - "partial": { - "type": "boolean" - }, + "partial": {"type": "boolean"}, "results": { "type": "array", "items": { "type": "object", "properties": { - "pubkey": { - "type": "string" - }, + "pubkey": {"type": "string"}, "uids": { "type": "array", "items": { "type": "object", "properties": { - "uid": { - "type": "string" - }, - "meta": { - "$ref": "#/definitions/meta_data" - }, - "self": { - "type": "string", - }, - "revocation_sig": { - "type": ["string", "null"] - }, - "revoked_on": { - "type": ["number", "null"] - }, - "revoked": { - "type": "boolean" - }, + "uid": {"type": "string"}, + "meta": {"$ref": "#/definitions/meta_data"}, + "self": {"type": "string"}, + "revocation_sig": {"type": ["string", "null"]}, + "revoked_on": {"type": ["number", "null"]}, + "revoked": {"type": "boolean"}, "others": { "type": "array", "items": { "type": "object", "properties": { - "pubkey": { - "type": "string", - }, - "meta": { - "$ref": "#/definitions/meta_data" - }, - "signature": { - "type": "string" - } - } - } - } + "pubkey": {"type": "string"}, + "meta": {"$ref": "#/definitions/meta_data"}, + "signature": {"type": "string"}, + }, + }, + }, }, - "required": ["uid", "meta", "self", "revocation_sig", "revoked", "others"] - } + "required": [ + "uid", + "meta", + "self", + "revocation_sig", + "revoked", + "others", + ], + }, }, "signed": { "type": "array", "items": { "type": "object", "properties": { - "uid": { - "type": "string" - }, - "pubkey": { - "type": "string" - }, - "meta": { - "$ref": "#/definitions/meta_data" - }, - "signature": { - "type": "string" - }, - "revocation_sig": { - "type": ["string", "null"] - }, - "revoked_on": { - "type": ["number", "null"] - }, - "revoked": { - "type": "boolean" - } + "uid": {"type": "string"}, + "pubkey": {"type": "string"}, + "meta": {"$ref": "#/definitions/meta_data"}, + "signature": {"type": "string"}, + "revocation_sig": {"type": ["string", "null"]}, + "revoked_on": {"type": ["number", "null"]}, + "revoked": {"type": "boolean"}, }, - "required": ["uid", "pubkey", "meta", "signature"] - } + "required": ["uid", "pubkey", "meta", "signature"], + }, }, }, - } - } + }, + }, }, - "required": ["partial", "results"] + "required": ["partial", "results"], } IDENTITY_OF_SCHEMA = { "type": "object", "properties": { - "pubkey": { - "type": "string" - }, - "uid": { - "type": "string" - }, - "sigDate": { - "type": "string" - } - } + "pubkey": {"type": "string"}, + "uid": {"type": "string"}, + "sigDate": {"type": "string"}, + }, } @@ -322,7 +238,9 @@ async def add(client: Client, identity_signed_raw: str) -> ClientResponse: :param identity_signed_raw: Identity raw document :return: """ - return await client.post(MODULE + '/add', {'identity': identity_signed_raw}, rtype=RESPONSE_AIOHTTP) + return await client.post( + MODULE + "/add", {"identity": identity_signed_raw}, rtype=RESPONSE_AIOHTTP + ) async def certify(client: Client, certification_signed_raw: str) -> ClientResponse: @@ -333,7 +251,9 @@ async def certify(client: Client, certification_signed_raw: str) -> ClientRespon :param certification_signed_raw: Certification raw document :return: """ - return await client.post(MODULE + '/certify', {'cert': certification_signed_raw}, rtype=RESPONSE_AIOHTTP) + return await client.post( + MODULE + "/certify", {"cert": certification_signed_raw}, rtype=RESPONSE_AIOHTTP + ) async def revoke(client: Client, revocation_signed_raw: str) -> ClientResponse: @@ -344,7 +264,11 @@ async def revoke(client: Client, revocation_signed_raw: str) -> ClientResponse: :param revocation_signed_raw: Certification raw document :return: """ - return await client.post(MODULE + '/revoke', {'revocation': revocation_signed_raw}, rtype=RESPONSE_AIOHTTP) + return await client.post( + MODULE + "/revoke", + {"revocation": revocation_signed_raw}, + rtype=RESPONSE_AIOHTTP, + ) async def lookup(client: Client, search: str) -> dict: @@ -355,7 +279,7 @@ async def lookup(client: Client, search: str) -> dict: :param search: UID or public key :return: """ - return await client.get(MODULE + '/lookup/%s' % search, schema=LOOKUP_SCHEMA) + return await client.get(MODULE + "/lookup/%s" % search, schema=LOOKUP_SCHEMA) async def certifiers_of(client: Client, search: str) -> dict: @@ -366,7 +290,9 @@ async def certifiers_of(client: Client, search: str) -> dict: :param search: UID or public key :return: """ - return await client.get(MODULE + '/certifiers-of/%s' % search, schema=CERTIFICATIONS_SCHEMA) + return await client.get( + MODULE + "/certifiers-of/%s" % search, schema=CERTIFICATIONS_SCHEMA + ) async def certified_by(client: Client, search: str) -> dict: @@ -377,7 +303,9 @@ async def certified_by(client: Client, search: str) -> dict: :param search: UID or public key :return: """ - return await client.get(MODULE + '/certified-by/%s' % search, schema=CERTIFICATIONS_SCHEMA) + return await client.get( + MODULE + "/certified-by/%s" % search, schema=CERTIFICATIONS_SCHEMA + ) async def members(client: Client) -> dict: @@ -387,7 +315,7 @@ async def members(client: Client) -> dict: :param client: Client to connect to the api :return: """ - return await client.get(MODULE + '/members', schema=MEMBERS_SCHEMA) + return await client.get(MODULE + "/members", schema=MEMBERS_SCHEMA) async def requirements(client: Client, search: str) -> dict: @@ -398,7 +326,9 @@ async def requirements(client: Client, search: str) -> dict: :param search: UID or public key :return: """ - return await client.get(MODULE + '/requirements/%s' % search, schema=REQUIREMENTS_SCHEMA) + return await client.get( + MODULE + "/requirements/%s" % search, schema=REQUIREMENTS_SCHEMA + ) async def identity_of(client: Client, search: str) -> dict: @@ -409,4 +339,6 @@ async def identity_of(client: Client, search: str) -> dict: :param search: UID or public key :return: """ - return await client.get(MODULE + '/identity-of/%s' % search, schema=IDENTITY_OF_SCHEMA) + return await client.get( + MODULE + "/identity-of/%s" % search, schema=IDENTITY_OF_SCHEMA + ) diff --git a/duniterpy/api/bma/ws.py b/duniterpy/api/bma/ws.py index be6c49849e3a068b5245d56f87c3e894c0ff311d..5f2e40b9edddcee772131e228777813acaf65eb3 100644 --- a/duniterpy/api/bma/ws.py +++ b/duniterpy/api/bma/ws.py @@ -25,33 +25,20 @@ from duniterpy.api.client import Client logger = logging.getLogger("duniter/ws") -MODULE = 'ws' +MODULE = "ws" WS_BLOCK_SCHEMA = BLOCK_SCHEMA WS_PEER_SCHEMA = { "type": "object", "properties": { - "version": { - "type": "number" - }, - "currency": { - "type": "string" - }, - "pubkey": { - "type": "string" - }, - "endpoints": { - "type": "array", - "items": { - "type": "string" - } - }, - "signature": { - "type": "string" - } + "version": {"type": "number"}, + "currency": {"type": "string"}, + "pubkey": {"type": "string"}, + "endpoints": {"type": "array", "items": {"type": "string"}}, + "signature": {"type": "string"}, }, - "required": ["version", "currency", "pubkey", "endpoints", "signature"] + "required": ["version", "currency", "pubkey", "endpoints", "signature"], } @@ -62,7 +49,7 @@ def block(client: Client) -> _WSRequestContextManager: :param client: Client to connect to the api :return: """ - return client.connect_ws(MODULE + '/block') + return client.connect_ws(MODULE + "/block") def peer(client: Client) -> _WSRequestContextManager: @@ -72,4 +59,4 @@ def peer(client: Client) -> _WSRequestContextManager: :param client: Client to connect to the api :return: """ - return client.connect_ws(MODULE + '/peer') + return client.connect_ws(MODULE + "/peer") diff --git a/duniterpy/api/client.py b/duniterpy/api/client.py index a1f102a09387e5f384122140dfc8b63f287dcb4a..4960d961fcb218c26c49708898873088ce6a68d9 100644 --- a/duniterpy/api/client.py +++ b/duniterpy/api/client.py @@ -15,22 +15,15 @@ from .errors import DuniterError logger = logging.getLogger("duniter") # Response type constants -RESPONSE_JSON = 'json' -RESPONSE_TEXT = 'text' -RESPONSE_AIOHTTP = 'aiohttp' +RESPONSE_JSON = "json" +RESPONSE_TEXT = "text" +RESPONSE_AIOHTTP = "aiohttp" # jsonschema validator ERROR_SCHEMA = { "type": "object", - "properties": { - "ucode": { - "type": "number" - }, - "message": { - "type": "string" - } - }, - "required": ["ucode", "message"] + "properties": {"ucode": {"type": "number"}, "message": {"type": "string"}}, + "required": ["ucode", "message"], } @@ -90,7 +83,11 @@ class API: API is a class used as an abstraction layer over the request library (AIOHTTP). """ - def __init__(self, connection_handler: endpoint.ConnectionHandler, headers: Optional[dict] = None) -> None: + def __init__( + self, + connection_handler: endpoint.ConnectionHandler, + headers: Optional[dict] = None, + ) -> None: """ Asks a module in order to create the url used then by derivated classes. @@ -109,18 +106,17 @@ class API: :return: """ # remove starting slash in path if present - path = path.lstrip('/') + path = path.lstrip("/") server, port = self.connection_handler.server, self.connection_handler.port if self.connection_handler.path: - url = '{scheme}://{server}:{port}/{path}'.format(scheme=scheme, - server=server, - port=port, - path=path) + url = "{scheme}://{server}:{port}/{path}".format( + scheme=scheme, server=server, port=port, path=path + ) else: - url = '{scheme}://{server}:{port}/'.format(scheme=scheme, - server=server, - port=port) + url = "{scheme}://{server}:{port}/".format( + scheme=scheme, server=server, port=port + ) return url + path @@ -131,17 +127,26 @@ class API: :param path: the request path :return: """ - logging.debug("Request : %s", self.reverse_url(self.connection_handler.http_scheme, path)) + logging.debug( + "Request : %s", self.reverse_url(self.connection_handler.http_scheme, path) + ) url = self.reverse_url(self.connection_handler.http_scheme, path) - response = await self.connection_handler.session.get(url, params=kwargs, headers=self.headers, - proxy=self.connection_handler.proxy, - timeout=15) + response = await self.connection_handler.session.get( + url, + params=kwargs, + headers=self.headers, + proxy=self.connection_handler.proxy, + timeout=15, + ) if response.status != 200: try: error_data = parse_error(await response.text()) raise DuniterError(error_data) except (TypeError, jsonschema.ValidationError): - raise ValueError('status code != 200 => %d (%s)' % (response.status, (await response.text()))) + raise ValueError( + "status code != 200 => %d (%s)" + % (response.status, (await response.text())) + ) return response @@ -152,8 +157,8 @@ class API: :param path: the request path :return: """ - if 'self_' in kwargs: - kwargs['self'] = kwargs.pop('self_') + if "self_" in kwargs: + kwargs["self"] = kwargs.pop("self_") logging.debug("POST : %s", kwargs) response = await self.connection_handler.session.post( @@ -161,7 +166,7 @@ class API: data=kwargs, headers=self.headers, proxy=self.connection_handler.proxy, - timeout=15 + timeout=15, ) return response @@ -178,7 +183,9 @@ class API: :return: """ url = self.reverse_url(self.connection_handler.ws_scheme, path) - return self.connection_handler.session.ws_connect(url, proxy=self.connection_handler.proxy) + return self.connection_handler.session.ws_connect( + url, proxy=self.connection_handler.proxy + ) class Client: @@ -186,8 +193,12 @@ class Client: Main class to create an API client """ - def __init__(self, _endpoint: Union[str, endpoint.Endpoint], session: ClientSession = None, - proxy: str = None) -> None: + def __init__( + self, + _endpoint: Union[str, endpoint.Endpoint], + session: ClientSession = None, + proxy: str = None, + ) -> None: """ Init Client instance @@ -202,7 +213,9 @@ class Client: self.endpoint = _endpoint if isinstance(self.endpoint, endpoint.UnknownEndpoint): - raise NotImplementedError("{0} endpoint in not supported".format(self.endpoint.api)) + raise NotImplementedError( + "{0} endpoint in not supported".format(self.endpoint.api) + ) # if no user session... if session is None: @@ -212,7 +225,13 @@ class Client: self.session = session self.proxy = proxy - async def get(self, url_path: str, params: dict = None, rtype: str = RESPONSE_JSON, schema: dict = None) -> Any: + async def get( + self, + url_path: str, + params: dict = None, + rtype: str = RESPONSE_JSON, + schema: dict = None, + ) -> Any: """ GET request on self.endpoint + url_path @@ -244,7 +263,13 @@ class Client: return result - async def post(self, url_path: str, params: dict = None, rtype: str = RESPONSE_JSON, schema: dict = None) -> Any: + async def post( + self, + url_path: str, + params: dict = None, + rtype: str = RESPONSE_JSON, + schema: dict = None, + ) -> Any: """ POST request on self.endpoint + url_path diff --git a/duniterpy/api/endpoint.py b/duniterpy/api/endpoint.py index 0175abbe802cf8e74de983a33f53662f118651ed..4b5f86c25835d6237bf0823f1a59bac3d92038f6 100644 --- a/duniterpy/api/endpoint.py +++ b/duniterpy/api/endpoint.py @@ -10,8 +10,16 @@ from ..documents import MalformedDocumentError class ConnectionHandler: """Helper class used by other API classes to ease passing server connection information.""" - def __init__(self, http_scheme: str, ws_scheme: str, server: str, port: int, path: str, - session: ClientSession, proxy: Optional[str] = None) -> None: + def __init__( + self, + http_scheme: str, + ws_scheme: str, + server: str, + port: int, + path: str, + session: ClientSession, + proxy: Optional[str] = None, + ) -> None: """ Init instance of connection handler @@ -32,11 +40,11 @@ class ConnectionHandler: self.session = session def __str__(self) -> str: - return 'connection info: %s:%d' % (self.server, self.port) + return "connection info: %s:%d" % (self.server, self.port) # required to type hint cls in classmethod -EndpointType = TypeVar('EndpointType', bound='Endpoint') +EndpointType = TypeVar("EndpointType", bound="Endpoint") class Endpoint: @@ -47,7 +55,9 @@ class Endpoint: def inline(self) -> str: raise NotImplementedError("inline() is not implemented") - def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler: + def conn_handler( + self, session: ClientSession, proxy: str = None + ) -> ConnectionHandler: raise NotImplementedError("conn_handler is not implemented") def __str__(self) -> str: @@ -58,7 +68,7 @@ class Endpoint: # required to type hint cls in classmethod -UnknownEndpointType = TypeVar('UnknownEndpointType', bound='UnknownEndpoint') +UnknownEndpointType = TypeVar("UnknownEndpointType", bound="UnknownEndpoint") class UnknownEndpoint(Endpoint): @@ -94,7 +104,9 @@ class UnknownEndpoint(Endpoint): doc += " {0}".format(p) return doc - def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler: + def conn_handler( + self, session: ClientSession, proxy: str = None + ) -> ConnectionHandler: """ Return connection handler from session @@ -105,7 +117,9 @@ class UnknownEndpoint(Endpoint): return ConnectionHandler("", "", "", 0, "", ClientSession()) def __str__(self) -> str: - return "{0} {1}".format(self.api, ' '.join(["{0}".format(p) for p in self.properties])) + return "{0} {1}".format( + self.api, " ".join(["{0}".format(p) for p in self.properties]) + ) def __eq__(self, other: Any) -> bool: if not isinstance(other, UnknownEndpoint): @@ -118,28 +132,23 @@ class UnknownEndpoint(Endpoint): ERROR_SCHEMA = { "type": "object", - "properties": { - "ucode": { - "type": "number" - }, - "message": { - "type": "string" - } - }, - "required": ["ucode", "message"] + "properties": {"ucode": {"type": "number"}, "message": {"type": "string"}}, + "required": ["ucode", "message"], } # required to type hint cls in classmethod -BMAEndpointType = TypeVar('BMAEndpointType', bound='BMAEndpoint') +BMAEndpointType = TypeVar("BMAEndpointType", bound="BMAEndpoint") class BMAEndpoint(Endpoint): API = "BASIC_MERKLED_API" re_inline = re.compile( - '^BASIC_MERKLED_API(?: ({host_regex}))?(?: ({ipv4_regex}))?(?: ({ipv6_regex}))?(?: ([0-9]+))$'.format( + "^BASIC_MERKLED_API(?: ({host_regex}))?(?: ({ipv4_regex}))?(?: ({ipv6_regex}))?(?: ([0-9]+))$".format( host_regex=constants.HOST_REGEX, ipv4_regex=constants.IPV4_REGEX, - ipv6_regex=constants.IPV6_REGEX)) + ipv6_regex=constants.IPV6_REGEX, + ) + ) def __init__(self, server: str, ipv4: str, ipv6: str, port: int) -> None: """ @@ -178,13 +187,16 @@ class BMAEndpoint(Endpoint): :return: """ - return BMAEndpoint.API + "{DNS}{IPv4}{IPv6}{PORT}" \ - .format(DNS=(" {0}".format(self.server) if self.server else ""), - IPv4=(" {0}".format(self.ipv4) if self.ipv4 else ""), - IPv6=(" {0}".format(self.ipv6) if self.ipv6 else ""), - PORT=(" {0}".format(self.port) if self.port else "")) + return BMAEndpoint.API + "{DNS}{IPv4}{IPv6}{PORT}".format( + DNS=(" {0}".format(self.server) if self.server else ""), + IPv4=(" {0}".format(self.ipv4) if self.ipv4 else ""), + IPv6=(" {0}".format(self.ipv6) if self.ipv6 else ""), + PORT=(" {0}".format(self.port) if self.port else ""), + ) - def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler: + def conn_handler( + self, session: ClientSession, proxy: str = None + ) -> ConnectionHandler: """ Return connection handler instance for the endpoint @@ -193,11 +205,17 @@ class BMAEndpoint(Endpoint): :return: """ if self.server: - conn_handler = ConnectionHandler("http", "ws", self.server, self.port, "", session, proxy) + conn_handler = ConnectionHandler( + "http", "ws", self.server, self.port, "", session, proxy + ) elif self.ipv6: - conn_handler = ConnectionHandler("http", "ws", "[{0}]".format(self.ipv6), self.port, "", session, proxy) + conn_handler = ConnectionHandler( + "http", "ws", "[{0}]".format(self.ipv6), self.port, "", session, proxy + ) else: - conn_handler = ConnectionHandler("http", "ws", self.ipv4, self.port, "", session, proxy) + conn_handler = ConnectionHandler( + "http", "ws", self.ipv4, self.port, "", session, proxy + ) return conn_handler @@ -207,25 +225,31 @@ class BMAEndpoint(Endpoint): def __eq__(self, other: Any) -> bool: if not isinstance(other, BMAEndpoint): return NotImplemented - return self.server == other.server and self.ipv4 == other.ipv4 \ - and self.ipv6 == other.ipv6 and self.port == other.port + return ( + self.server == other.server + and self.ipv4 == other.ipv4 + and self.ipv6 == other.ipv6 + and self.port == other.port + ) def __hash__(self) -> int: return hash((self.server, self.ipv4, self.ipv6, self.port)) # required to type hint cls in classmethod -SecuredBMAEndpointType = TypeVar('SecuredBMAEndpointType', bound='SecuredBMAEndpoint') +SecuredBMAEndpointType = TypeVar("SecuredBMAEndpointType", bound="SecuredBMAEndpoint") class SecuredBMAEndpoint(BMAEndpoint): API = "BMAS" re_inline = re.compile( - '^BMAS(?: ({host_regex}))?(?: ({ipv4_regex}))?(?: ({ipv6_regex}))? ([0-9]+)(?: ({path_regex}))?$'.format( + "^BMAS(?: ({host_regex}))?(?: ({ipv4_regex}))?(?: ({ipv6_regex}))? ([0-9]+)(?: ({path_regex}))?$".format( host_regex=constants.HOST_REGEX, ipv4_regex=constants.IPV4_REGEX, ipv6_regex=constants.IPV6_REGEX, - path_regex=constants.PATH_REGEX)) + path_regex=constants.PATH_REGEX, + ) + ) def __init__(self, server: str, ipv4: str, ipv6: str, port: int, path: str) -> None: """ @@ -241,7 +265,9 @@ class SecuredBMAEndpoint(BMAEndpoint): self.path = path @classmethod - def from_inline(cls: Type[SecuredBMAEndpointType], inline: str) -> SecuredBMAEndpointType: + def from_inline( + cls: Type[SecuredBMAEndpointType], inline: str + ) -> SecuredBMAEndpointType: """ Return SecuredBMAEndpoint instance from endpoint string @@ -266,10 +292,16 @@ class SecuredBMAEndpoint(BMAEndpoint): :return: """ - inlined = [str(info) for info in (self.server, self.ipv4, self.ipv6, self.port, self.path) if info] + inlined = [ + str(info) + for info in (self.server, self.ipv4, self.ipv6, self.port, self.path) + if info + ] return SecuredBMAEndpoint.API + " " + " ".join(inlined) - def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler: + def conn_handler( + self, session: ClientSession, proxy: str = None + ) -> ConnectionHandler: """ Return connection handler instance for the endpoint @@ -278,30 +310,40 @@ class SecuredBMAEndpoint(BMAEndpoint): :return: """ if self.server: - conn_handler = ConnectionHandler("https", "wss", self.server, self.port, self.path, session, proxy) + conn_handler = ConnectionHandler( + "https", "wss", self.server, self.port, self.path, session, proxy + ) elif self.ipv6: - conn_handler = ConnectionHandler("https", "wss", "[{0}]".format(self.ipv6), self.port, self.path, session, - proxy) + conn_handler = ConnectionHandler( + "https", + "wss", + "[{0}]".format(self.ipv6), + self.port, + self.path, + session, + proxy, + ) else: - conn_handler = ConnectionHandler("https", "wss", self.ipv4, self.port, self.path, session, proxy) + conn_handler = ConnectionHandler( + "https", "wss", self.ipv4, self.port, self.path, session, proxy + ) return conn_handler # required to type hint cls in classmethod -WS2PEndpointType = TypeVar('WS2PEndpointType', bound='WS2PEndpoint') +WS2PEndpointType = TypeVar("WS2PEndpointType", bound="WS2PEndpoint") class WS2PEndpoint(Endpoint): API = "WS2P" re_inline = re.compile( - '^WS2P ({ws2pid_regex}) ((?:{host_regex})|(?:{ipv4_regex})|(?:{ipv6_regex})) ([0-9]+)?(?: ({path_regex}))?$' - .format( + "^WS2P ({ws2pid_regex}) ((?:{host_regex})|(?:{ipv4_regex})|(?:{ipv6_regex})) ([0-9]+)?(?: ({path_regex}))?$".format( ws2pid_regex=constants.WS2PID_REGEX, host_regex=constants.HOST_REGEX, ipv4_regex=constants.IPV4_REGEX, ipv6_regex=constants.IPV6_REGEX, - path_regex=constants.PATH_REGEX + path_regex=constants.PATH_REGEX, ) ) @@ -336,10 +378,16 @@ class WS2PEndpoint(Endpoint): :return: """ - inlined = [str(info) for info in (self.ws2pid, self.server, self.port, self.path) if info] + inlined = [ + str(info) + for info in (self.ws2pid, self.server, self.port, self.path) + if info + ] return WS2PEndpoint.API + " " + " ".join(inlined) - def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler: + def conn_handler( + self, session: ClientSession, proxy: str = None + ) -> ConnectionHandler: """ Return connection handler instance for the endpoint @@ -347,7 +395,9 @@ class WS2PEndpoint(Endpoint): :param proxy: Proxy url :return: """ - return ConnectionHandler("https", "wss", self.server, self.port, self.path, session, proxy) + return ConnectionHandler( + "https", "wss", self.server, self.port, self.path, session, proxy + ) def __str__(self) -> str: return self.inline() @@ -355,22 +405,28 @@ class WS2PEndpoint(Endpoint): def __eq__(self, other: Any) -> bool: if not isinstance(other, WS2PEndpoint): return NotImplemented - return self.server == other.server and self.ws2pid == other.ws2pid \ - and self.port == other.port and self.path == other.path + return ( + self.server == other.server + and self.ws2pid == other.ws2pid + and self.port == other.port + and self.path == other.path + ) def __hash__(self) -> int: return hash((self.ws2pid, self.server, self.port, self.path)) # required to type hint cls in classmethod -ESCoreEndpointType = TypeVar('ESCoreEndpointType', bound='ESCoreEndpoint') +ESCoreEndpointType = TypeVar("ESCoreEndpointType", bound="ESCoreEndpoint") class ESCoreEndpoint(Endpoint): API = "ES_CORE_API" re_inline = re.compile( - '^ES_CORE_API ((?:{host_regex})|(?:{ipv4_regex})) ([0-9]+)$'.format(host_regex=constants.HOST_REGEX, - ipv4_regex=constants.IPV4_REGEX)) + "^ES_CORE_API ((?:{host_regex})|(?:{ipv4_regex})) ([0-9]+)$".format( + host_regex=constants.HOST_REGEX, ipv4_regex=constants.IPV4_REGEX + ) + ) def __init__(self, server: str, port: int) -> None: self.server = server @@ -400,7 +456,9 @@ class ESCoreEndpoint(Endpoint): inlined = [str(info) for info in (self.server, self.port) if info] return ESCoreEndpoint.API + " " + " ".join(inlined) - def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler: + def conn_handler( + self, session: ClientSession, proxy: str = None + ) -> ConnectionHandler: """ Return connection handler instance for the endpoint @@ -408,7 +466,9 @@ class ESCoreEndpoint(Endpoint): :param proxy: Proxy url :return: """ - return ConnectionHandler("https", "wss", self.server, self.port, "", session, proxy) + return ConnectionHandler( + "https", "wss", self.server, self.port, "", session, proxy + ) def __str__(self) -> str: return self.inline() @@ -423,14 +483,16 @@ class ESCoreEndpoint(Endpoint): # required to type hint cls in classmethod -ESUserEndpointType = TypeVar('ESUserEndpointType', bound='ESUserEndpoint') +ESUserEndpointType = TypeVar("ESUserEndpointType", bound="ESUserEndpoint") class ESUserEndpoint(Endpoint): API = "ES_USER_API" re_inline = re.compile( - '^ES_USER_API ((?:{host_regex})|(?:{ipv4_regex})) ([0-9]+)$'.format(host_regex=constants.HOST_REGEX, - ipv4_regex=constants.IPV4_REGEX)) + "^ES_USER_API ((?:{host_regex})|(?:{ipv4_regex})) ([0-9]+)$".format( + host_regex=constants.HOST_REGEX, ipv4_regex=constants.IPV4_REGEX + ) + ) def __init__(self, server: str, port: int) -> None: self.server = server @@ -460,7 +522,9 @@ class ESUserEndpoint(Endpoint): inlined = [str(info) for info in (self.server, self.port) if info] return ESUserEndpoint.API + " " + " ".join(inlined) - def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler: + def conn_handler( + self, session: ClientSession, proxy: str = None + ) -> ConnectionHandler: """ Return connection handler instance for the endpoint @@ -468,7 +532,9 @@ class ESUserEndpoint(Endpoint): :param proxy: Proxy url :return: """ - return ConnectionHandler("https", "wss", self.server, self.port, "", session, proxy) + return ConnectionHandler( + "https", "wss", self.server, self.port, "", session, proxy + ) def __str__(self) -> str: return self.inline() @@ -483,21 +549,27 @@ class ESUserEndpoint(Endpoint): # required to type hint cls in classmethod -ESSubscribtionEndpointType = TypeVar('ESSubscribtionEndpointType', bound='ESSubscribtionEndpoint') +ESSubscribtionEndpointType = TypeVar( + "ESSubscribtionEndpointType", bound="ESSubscribtionEndpoint" +) class ESSubscribtionEndpoint(Endpoint): API = "ES_SUBSCRIPTION_API" re_inline = re.compile( - '^ES_SUBSCRIPTION_API ((?:{host_regex})|(?:{ipv4_regex})) ([0-9]+)$'.format(host_regex=constants.HOST_REGEX, - ipv4_regex=constants.IPV4_REGEX)) + "^ES_SUBSCRIPTION_API ((?:{host_regex})|(?:{ipv4_regex})) ([0-9]+)$".format( + host_regex=constants.HOST_REGEX, ipv4_regex=constants.IPV4_REGEX + ) + ) def __init__(self, server: str, port: int) -> None: self.server = server self.port = port @classmethod - def from_inline(cls: Type[ESSubscribtionEndpointType], inline: str) -> ESSubscribtionEndpointType: + def from_inline( + cls: Type[ESSubscribtionEndpointType], inline: str + ) -> ESSubscribtionEndpointType: """ Return ESSubscribtionEndpoint instance from endpoint string @@ -520,7 +592,9 @@ class ESSubscribtionEndpoint(Endpoint): inlined = [str(info) for info in (self.server, self.port) if info] return ESSubscribtionEndpoint.API + " " + " ".join(inlined) - def conn_handler(self, session: ClientSession, proxy: str = None) -> ConnectionHandler: + def conn_handler( + self, session: ClientSession, proxy: str = None + ) -> ConnectionHandler: """ Return connection handler instance for the endpoint @@ -528,7 +602,9 @@ class ESSubscribtionEndpoint(Endpoint): :param proxy: Proxy url :return: """ - return ConnectionHandler("https", "wss", self.server, self.port, "", session, proxy) + return ConnectionHandler( + "https", "wss", self.server, self.port, "", session, proxy + ) def __str__(self) -> str: return self.inline() @@ -548,7 +624,7 @@ MANAGED_API = { WS2PEndpoint.API: WS2PEndpoint, ESCoreEndpoint.API: ESCoreEndpoint, ESUserEndpoint.API: ESUserEndpoint, - ESSubscribtionEndpoint.API: ESSubscribtionEndpoint + ESSubscribtionEndpoint.API: ESSubscribtionEndpoint, } # type: Dict[str, Any] diff --git a/duniterpy/api/errors.py b/duniterpy/api/errors.py index 9738dd415637a0254fedd737d296f8bb4a6a79fd..5875cfbb32a1b8a83b02e29a57b0bb27504ed818 100644 --- a/duniterpy/api/errors.py +++ b/duniterpy/api/errors.py @@ -2,6 +2,7 @@ class DuniterError(Exception): """ Handle duniter error """ + def __init__(self, data: dict) -> None: """ Init instance from Duniter data diff --git a/duniterpy/constants.py b/duniterpy/constants.py index c619d0f2009fd883a0246f678f07e4a27a968b92..c76c99621b0c8b9c98ac4d4d5ab329b617dc4bf5 100644 --- a/duniterpy/constants.py +++ b/duniterpy/constants.py @@ -5,28 +5,34 @@ BLOCK_HASH_REGEX = "[0-9a-fA-F]{5,64}" TRANSACTION_HASH_REGEX = "[0-9a-fA-F]{5,64}" HASH_REGEX = "[A-F0-9]{64}" BLOCK_ID_REGEX = "[0-9]+" -BLOCK_UID_REGEX = "{block_id_regex}-{block_hash_regex}".format(block_id_regex=BLOCK_ID_REGEX, - block_hash_regex=BLOCK_HASH_REGEX) -CONDITIONS_REGEX = "(&&|\\|\\|| |[()]|(SIG\\({pubkey_regex}\\)|(XHX\\({hash_regex}\\))))*" \ - .format(pubkey_regex=PUBKEY_REGEX, hash_regex=HASH_REGEX) -IPV4_REGEX = '(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][' \ - '0-9]|25[0-5])' -IPV6_REGEX = '(?:(?:[0-9A-Fa-f]{1,4}:){6}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[' \ - '0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|::(?:[0-9A-Fa-f]{1,' \ - '4}:){5}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){' \ - '3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(?:[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,' \ - '4}:){4}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){' \ - '3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,' \ - '4})?::(?:[0-9A-Fa-f]{1,4}:){3}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[' \ - '0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(?:(?:[0-9A-Fa-f]{1,4}:){,' \ - '2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][' \ - '0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(?:(?:[' \ - '0-9A-Fa-f]{1,4}:){,3}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}:(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,' \ - '4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][' \ - '0-9]|25[0-5]))|(?:(?:[0-9A-Fa-f]{1,4}:){,4}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,' \ - '4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][' \ - '0-9]|25[0-5]))|(?:(?:[0-9A-Fa-f]{1,4}:){,5}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}|(?:(?:[0-9A-Fa-f]{1,' \ - '4}:){,6}[0-9A-Fa-f]{1,4})?::)(?:%.+)?' +BLOCK_UID_REGEX = "{block_id_regex}-{block_hash_regex}".format( + block_id_regex=BLOCK_ID_REGEX, block_hash_regex=BLOCK_HASH_REGEX +) +CONDITIONS_REGEX = "(&&|\\|\\|| |[()]|(SIG\\({pubkey_regex}\\)|(XHX\\({hash_regex}\\))))*".format( + pubkey_regex=PUBKEY_REGEX, hash_regex=HASH_REGEX +) +IPV4_REGEX = ( + "(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][" + "0-9]|25[0-5])" +) +IPV6_REGEX = ( + "(?:(?:[0-9A-Fa-f]{1,4}:){6}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[" + "0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|::(?:[0-9A-Fa-f]{1," + "4}:){5}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){" + "3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(?:[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1," + "4}:){4}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){" + "3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1," + "4})?::(?:[0-9A-Fa-f]{1,4}:){3}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[" + "0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(?:(?:[0-9A-Fa-f]{1,4}:){," + "2}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:){2}(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|(?:(?:[0-9]|[1-9][" + "0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(?:(?:[" + "0-9A-Fa-f]{1,4}:){,3}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}:(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1," + "4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][" + "0-9]|25[0-5]))|(?:(?:[0-9A-Fa-f]{1,4}:){,4}[0-9A-Fa-f]{1,4})?::(?:[0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1," + "4}|(?:(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}(?:[0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][" + "0-9]|25[0-5]))|(?:(?:[0-9A-Fa-f]{1,4}:){,5}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}|(?:(?:[0-9A-Fa-f]{1," + "4}:){,6}[0-9A-Fa-f]{1,4})?::)(?:%.+)?" +) HOST_REGEX = "[a-z0-9-_.]*(?:.[a-zA-Z])?" PATH_REGEX = "[/\\w \\.-]*/?" WS2PID_REGEX = "[0-9a-f]{8}" diff --git a/duniterpy/documents/__init__.py b/duniterpy/documents/__init__.py index a5b557fac6784a46566ffecb8ecf2b7c93f04851..25d684d739309089cc6571b8fb26d34cfaa7debe 100644 --- a/duniterpy/documents/__init__.py +++ b/duniterpy/documents/__init__.py @@ -5,6 +5,13 @@ from .certification import Certification from .revocation import Revocation from .identity import Identity from .membership import Membership -from .transaction import SimpleTransaction, Transaction, InputSource, OutputSource, \ - SIGParameter, Unlock, UnlockParameter +from .transaction import ( + SimpleTransaction, + Transaction, + InputSource, + OutputSource, + SIGParameter, + Unlock, + UnlockParameter, +) from .crc_pubkey import CRCPubkey diff --git a/duniterpy/documents/block.py b/duniterpy/documents/block.py index e020abf495e033205d7b9ef1226ce6013dbcd7c6..4cd298b8761bfdec38d61a52a8ab820b69a79742 100644 --- a/duniterpy/documents/block.py +++ b/duniterpy/documents/block.py @@ -13,7 +13,7 @@ from ..constants import PUBKEY_REGEX, BLOCK_HASH_REGEX # required to type hint cls in classmethod -BlockType = TypeVar('BlockType', bound='Block') +BlockType = TypeVar("BlockType", bound="Block") class Block(Document): @@ -68,15 +68,23 @@ The class Block handles Block documents. re_mediantime = re.compile("MedianTime: ([0-9]+)\n") re_universaldividend = re.compile("UniversalDividend: ([0-9]+)\n") re_unitbase = re.compile("UnitBase: ([0-9]+)\n") - re_issuer = re.compile("Issuer: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX)) + re_issuer = re.compile( + "Issuer: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX) + ) re_issuers_frame = re.compile("IssuersFrame: ([0-9]+)\n") re_issuers_frame_var = re.compile("IssuersFrameVar: (0|-?[1-9]\\d{0,18})\n") re_different_issuers_count = re.compile("DifferentIssuersCount: ([0-9]+)\n") - re_previoushash = re.compile("PreviousHash: ({block_hash_regex})\n".format(block_hash_regex=BLOCK_HASH_REGEX)) - re_previousissuer = re.compile("PreviousIssuer: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX)) - re_parameters = re.compile("Parameters: ([0-9]+\\.[0-9]+):([0-9]+):([0-9]+):([0-9]+):([0-9]+):([0-9]+):\ + re_previoushash = re.compile( + "PreviousHash: ({block_hash_regex})\n".format(block_hash_regex=BLOCK_HASH_REGEX) + ) + re_previousissuer = re.compile( + "PreviousIssuer: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX) + ) + re_parameters = re.compile( + "Parameters: ([0-9]+\\.[0-9]+):([0-9]+):([0-9]+):([0-9]+):([0-9]+):([0-9]+):\ ([0-9]+):([0-9]+):([0-9]+):([0-9]+):([0-9]+\\.[0-9]+):([0-9]+):([0-9]+):([0-9]+):([0-9]+):([0-9]+):([0-9]+\\.[0-9]+):\ -([0-9]+):([0-9]+):([0-9]+)\n") +([0-9]+):([0-9]+):([0-9]+)\n" + ) re_memberscount = re.compile("MembersCount: ([0-9]+)\n") re_identities = re.compile("Identities:\n") re_joiners = re.compile("Joiners:\n") @@ -87,67 +95,72 @@ The class Block handles Block documents. re_exclusion = re.compile("({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX)) re_certifications = re.compile("Certifications:\n") re_transactions = re.compile("Transactions:\n") - re_hash = re.compile("InnerHash: ({block_hash_regex})\n".format(block_hash_regex=BLOCK_HASH_REGEX)) + re_hash = re.compile( + "InnerHash: ({block_hash_regex})\n".format(block_hash_regex=BLOCK_HASH_REGEX) + ) re_noonce = re.compile("Nonce: ([0-9]+)\n") - fields_parsers = {**Document.fields_parsers, **{ - 'Type': re_type, - 'Number': re_number, - 'PoWMin': re_powmin, - 'Time': re_time, - 'MedianTime': re_mediantime, - 'UD': re_universaldividend, - 'UnitBase': re_unitbase, - 'Issuer': re_issuer, - 'IssuersFrame': re_issuers_frame, - 'IssuersFrameVar': re_issuers_frame_var, - 'DifferentIssuersCount': re_different_issuers_count, - 'PreviousIssuer': re_previousissuer, - 'PreviousHash': re_previoushash, - 'Parameters': re_parameters, - 'MembersCount': re_memberscount, - 'Identities': re_identities, - 'Joiners': re_joiners, - 'Actives': re_actives, - 'Leavers': re_leavers, - 'Revoked': re_revoked, - 'Excluded': re_excluded, - 'Certifications': re_certifications, - 'Transactions': re_transactions, - 'InnerHash': re_hash, - 'Noonce': re_noonce, + fields_parsers = { + **Document.fields_parsers, + **{ + "Type": re_type, + "Number": re_number, + "PoWMin": re_powmin, + "Time": re_time, + "MedianTime": re_mediantime, + "UD": re_universaldividend, + "UnitBase": re_unitbase, + "Issuer": re_issuer, + "IssuersFrame": re_issuers_frame, + "IssuersFrameVar": re_issuers_frame_var, + "DifferentIssuersCount": re_different_issuers_count, + "PreviousIssuer": re_previousissuer, + "PreviousHash": re_previoushash, + "Parameters": re_parameters, + "MembersCount": re_memberscount, + "Identities": re_identities, + "Joiners": re_joiners, + "Actives": re_actives, + "Leavers": re_leavers, + "Revoked": re_revoked, + "Excluded": re_excluded, + "Certifications": re_certifications, + "Transactions": re_transactions, + "InnerHash": re_hash, + "Noonce": re_noonce, + }, } - } - - def __init__(self, - version: int, - currency: str, - number: int, - powmin: int, - time: int, - mediantime: int, - ud: Optional[int], - unit_base: int, - issuer: str, - issuers_frame: int, - issuers_frame_var: int, - different_issuers_count: int, - prev_hash: Optional[str], - prev_issuer: Optional[str], - parameters: Optional[Sequence[str]], - members_count: int, - identities: List[Identity], - joiners: List[Membership], - actives: List[Membership], - leavers: List[Membership], - revokations: List[Revocation], - excluded: List[str], - certifications: List[Certification], - transactions: List[Transaction], - inner_hash: str, - noonce: int, - signature: str - ) -> None: + + def __init__( + self, + version: int, + currency: str, + number: int, + powmin: int, + time: int, + mediantime: int, + ud: Optional[int], + unit_base: int, + issuer: str, + issuers_frame: int, + issuers_frame_var: int, + different_issuers_count: int, + prev_hash: Optional[str], + prev_issuer: Optional[str], + parameters: Optional[Sequence[str]], + members_count: int, + identities: List[Identity], + joiners: List[Membership], + actives: List[Membership], + leavers: List[Membership], + revokations: List[Revocation], + excluded: List[str], + certifications: List[Certification], + transactions: List[Transaction], + inner_hash: str, + noonce: int, + signature: str, + ) -> None: """ Constructor @@ -180,14 +193,19 @@ The class Block handles Block documents. :param signature: the block signature """ super().__init__(version, currency, [signature]) - documents_versions = max(max([1] + [i.version for i in identities]), - max([1] + [m.version for m in actives + leavers + joiners]), - max([1] + [r.version for r in revokations]), - max([1] + [c.version for c in certifications]), - max([1] + [t.version for t in transactions])) + documents_versions = max( + max([1] + [i.version for i in identities]), + max([1] + [m.version for m in actives + leavers + joiners]), + max([1] + [r.version for r in revokations]), + max([1] + [c.version for c in certifications]), + max([1] + [t.version for t in transactions]), + ) if self.version < documents_versions: raise MalformedDocumentError( - "Block version is too low : {0} < {1}".format(self.version, documents_versions)) + "Block version is too low : {0} < {1}".format( + self.version, documents_versions + ) + ) self.number = number self.powmin = powmin self.time = time @@ -342,8 +360,9 @@ The class Block handles Block documents. if Block.re_certifications.match(lines[n]): n += 1 while Block.re_transactions.match(lines[n]) is None: - certification = Certification.from_inline(version, currency, - prev_hash, lines[n]) + certification = Certification.from_inline( + version, currency, prev_hash, lines[n] + ) certifications.append(certification) n += 1 @@ -353,17 +372,27 @@ The class Block handles Block documents. tx_lines = "" header_data = Transaction.re_header.match(lines[n]) if header_data is None: - raise MalformedDocumentError("Compact transaction ({0})".format(lines[n])) + raise MalformedDocumentError( + "Compact transaction ({0})".format(lines[n]) + ) issuers_num = int(header_data.group(2)) inputs_num = int(header_data.group(3)) unlocks_num = int(header_data.group(4)) outputs_num = int(header_data.group(5)) has_comment = int(header_data.group(6)) sup_lines = 2 - tx_max = n + sup_lines + issuers_num * 2 + inputs_num + unlocks_num + outputs_num + has_comment + tx_max = ( + n + + sup_lines + + issuers_num * 2 + + inputs_num + + unlocks_num + + outputs_num + + has_comment + ) for index in range(n, tx_max): tx_lines += lines[index] - n += (tx_max - n) + n += tx_max - n transaction = Transaction.from_compact(currency, tx_lines) transactions.append(transaction) @@ -375,12 +404,35 @@ The class Block handles Block documents. signature = Block.parse_field("Signature", lines[n]) - return cls(version, currency, number, powmin, time, - mediantime, ud, unit_base, issuer, issuers_frame, issuers_frame_var, - different_issuers_count, prev_hash, prev_issuer, - parameters, members_count, identities, joiners, - actives, leavers, revoked, excluded, certifications, - transactions, inner_hash, noonce, signature) + return cls( + version, + currency, + number, + powmin, + time, + mediantime, + ud, + unit_base, + issuer, + issuers_frame, + issuers_frame_var, + different_issuers_count, + prev_hash, + prev_issuer, + parameters, + members_count, + identities, + joiners, + actives, + leavers, + revoked, + excluded, + certifications, + transactions, + inner_hash, + noonce, + signature, + ) def raw(self) -> str: doc = """Version: {version} @@ -390,12 +442,14 @@ Number: {number} PoWMin: {powmin} Time: {time} MedianTime: {mediantime} -""".format(version=self.version, - currency=self.currency, - number=self.number, - powmin=self.powmin, - time=self.time, - mediantime=self.mediantime) +""".format( + version=self.version, + currency=self.currency, + number=self.number, + powmin=self.powmin, + time=self.time, + mediantime=self.mediantime, + ) if self.ud: doc += "UniversalDividend: {0}\n".format(self.ud) @@ -406,14 +460,18 @@ MedianTime: {mediantime} doc += """IssuersFrame: {0} IssuersFrameVar: {1} DifferentIssuersCount: {2} -""".format(self.issuers_frame, self.issuers_frame_var, self.different_issuers_count) +""".format( + self.issuers_frame, self.issuers_frame_var, self.different_issuers_count + ) if self.number == 0 and self.parameters is not None: str_params = ":".join([str(p) for p in self.parameters]) doc += "Parameters: {0}\n".format(str_params) else: doc += "PreviousHash: {0}\n\ -PreviousIssuer: {1}\n".format(self.prev_hash, self.prev_issuer) +PreviousIssuer: {1}\n".format( + self.prev_hash, self.prev_issuer + ) doc += "MembersCount: {0}\n".format(self.members_count) @@ -459,12 +517,14 @@ PreviousIssuer: {1}\n".format(self.prev_hash, self.prev_issuer) doc_str = """InnerHash: {inner_hash} Nonce: {nonce} {signature} -""".format(inner_hash=self.inner_hash, nonce=self.noonce, signature=self.signatures[0]) - return hashlib.sha256(doc_str.encode('ascii')).hexdigest().upper() +""".format( + inner_hash=self.inner_hash, nonce=self.noonce, signature=self.signatures[0] + ) + return hashlib.sha256(doc_str.encode("ascii")).hexdigest().upper() def computed_inner_hash(self) -> str: doc = self.signed_raw() - inner_doc = '\n'.join(doc.split('\n')[:-2]) + '\n' + inner_doc = "\n".join(doc.split("\n")[:-2]) + "\n" return hashlib.sha256(inner_doc.encode("ascii")).hexdigest().upper() def sign(self, keys): @@ -474,7 +534,7 @@ Nonce: {nonce} """ key = keys[0] signed = self.raw()[-2:] - signing = base64.b64encode(key.signature(bytes(signed, 'ascii'))) + signing = base64.b64encode(key.signature(bytes(signed, "ascii"))) self.signatures = [signing.decode("ascii")] def __eq__(self, other: object) -> bool: diff --git a/duniterpy/documents/block_uid.py b/duniterpy/documents/block_uid.py index 7afd21a539f4dafb3c34135e48a48ff6721ae992..1811d59f54529575cb676b894180aa89d9f89300 100644 --- a/duniterpy/documents/block_uid.py +++ b/duniterpy/documents/block_uid.py @@ -5,20 +5,26 @@ from .document import MalformedDocumentError from ..constants import EMPTY_HASH, BLOCK_ID_REGEX, BLOCK_HASH_REGEX # required to type hint cls in classmethod -BlockUIDType = TypeVar('BlockUIDType', bound='BlockUID') +BlockUIDType = TypeVar("BlockUIDType", bound="BlockUID") class BlockUID: """ A simple block id """ - re_block_uid = re.compile("({block_id_regex})-({block_hash_regex})".format(block_id_regex=BLOCK_ID_REGEX, - block_hash_regex=BLOCK_HASH_REGEX)) - re_hash = re.compile("({block_hash_regex})".format(block_hash_regex=BLOCK_HASH_REGEX)) + + re_block_uid = re.compile( + "({block_id_regex})-({block_hash_regex})".format( + block_id_regex=BLOCK_ID_REGEX, block_hash_regex=BLOCK_HASH_REGEX + ) + ) + re_hash = re.compile( + "({block_hash_regex})".format(block_hash_regex=BLOCK_HASH_REGEX) + ) def __init__(self, number: int, sha_hash: str) -> None: - assert (type(number) is int) - assert (BlockUID.re_hash.match(sha_hash) is not None) + assert type(number) is int + assert BlockUID.re_hash.match(sha_hash) is not None self.number = number self.sha_hash = sha_hash diff --git a/duniterpy/documents/certification.py b/duniterpy/documents/certification.py index 1f570239e2cfd39b823a50f8d0fe8c1ee1c20562..b5cdfd36a33691cfd089cf29d945badc993e5ae2 100644 --- a/duniterpy/documents/certification.py +++ b/duniterpy/documents/certification.py @@ -5,12 +5,18 @@ from typing import Optional, TypeVar, Type, Union from .identity import Identity from .block_uid import BlockUID -from ..constants import PUBKEY_REGEX, SIGNATURE_REGEX, BLOCK_ID_REGEX, BLOCK_UID_REGEX, UID_REGEX +from ..constants import ( + PUBKEY_REGEX, + SIGNATURE_REGEX, + BLOCK_ID_REGEX, + BLOCK_UID_REGEX, + UID_REGEX, +) from .document import Document, MalformedDocumentError # required to type hint cls in classmethod -CertificationType = TypeVar('CertificationType', bound='Certification') +CertificationType = TypeVar("CertificationType", bound="Certification") class Certification(Document): @@ -18,33 +24,59 @@ class Certification(Document): A document describing a certification. """ - re_inline = re.compile("({certifier_regex}):({certified_regex}):({block_id_regex}):({signature_regex})\n".format( - certifier_regex=PUBKEY_REGEX, - certified_regex=PUBKEY_REGEX, - block_id_regex=BLOCK_ID_REGEX, - signature_regex=SIGNATURE_REGEX - )) - re_timestamp = re.compile("META:TS:({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX)) + re_inline = re.compile( + "({certifier_regex}):({certified_regex}):({block_id_regex}):({signature_regex})\n".format( + certifier_regex=PUBKEY_REGEX, + certified_regex=PUBKEY_REGEX, + block_id_regex=BLOCK_ID_REGEX, + signature_regex=SIGNATURE_REGEX, + ) + ) + re_timestamp = re.compile( + "META:TS:({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX) + ) re_type = re.compile("Type: (Certification)") - re_issuer = re.compile("Issuer: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX)) - re_idty_issuer = re.compile("IdtyIssuer: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX)) - re_idty_unique_id = re.compile("IdtyUniqueID: ({uid_regex})\n".format(uid_regex=UID_REGEX)) - re_idty_timestamp = re.compile("IdtyTimestamp: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX)) - re_idty_signature = re.compile("IdtySignature: ({signature_regex})\n".format(signature_regex=SIGNATURE_REGEX)) - re_cert_timestamp = re.compile("CertTimestamp: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX)) - - fields_parsers = {**Document.fields_parsers, **{ - "Type": re_type, - "Issuer": re_issuer, - "CertTimestamp": re_cert_timestamp, - "IdtyIssuer": re_idty_issuer, - "IdtyUniqueID": re_idty_unique_id, - "IdtySignature": re_idty_signature, - "IdtyTimestamp": re_idty_timestamp - }} - - def __init__(self, version: int, currency: str, pubkey_from: str, identity: Union[Identity, str], - timestamp: BlockUID, signature: str) -> None: + re_issuer = re.compile( + "Issuer: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX) + ) + re_idty_issuer = re.compile( + "IdtyIssuer: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX) + ) + re_idty_unique_id = re.compile( + "IdtyUniqueID: ({uid_regex})\n".format(uid_regex=UID_REGEX) + ) + re_idty_timestamp = re.compile( + "IdtyTimestamp: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX) + ) + re_idty_signature = re.compile( + "IdtySignature: ({signature_regex})\n".format(signature_regex=SIGNATURE_REGEX) + ) + re_cert_timestamp = re.compile( + "CertTimestamp: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX) + ) + + fields_parsers = { + **Document.fields_parsers, + **{ + "Type": re_type, + "Issuer": re_issuer, + "CertTimestamp": re_cert_timestamp, + "IdtyIssuer": re_idty_issuer, + "IdtyUniqueID": re_idty_unique_id, + "IdtySignature": re_idty_signature, + "IdtyTimestamp": re_idty_timestamp, + }, + } + + def __init__( + self, + version: int, + currency: str, + pubkey_from: str, + identity: Union[Identity, str], + timestamp: BlockUID, + signature: str, + ) -> None: """ Constructor @@ -62,7 +94,9 @@ class Certification(Document): self.timestamp = timestamp @classmethod - def from_signed_raw(cls: Type[CertificationType], signed_raw: str) -> CertificationType: + def from_signed_raw( + cls: Type[CertificationType], signed_raw: str + ) -> CertificationType: """ Return Certification instance from signed raw document @@ -90,24 +124,40 @@ class Certification(Document): identity_uid = Certification.parse_field("IdtyUniqueID", lines[n]) n += 1 - identity_timestamp = BlockUID.from_str(Certification.parse_field("IdtyTimestamp", lines[n])) + identity_timestamp = BlockUID.from_str( + Certification.parse_field("IdtyTimestamp", lines[n]) + ) n += 1 identity_signature = Certification.parse_field("IdtySignature", lines[n]) n += 1 - timestamp = BlockUID.from_str(Certification.parse_field("CertTimestamp", lines[n])) + timestamp = BlockUID.from_str( + Certification.parse_field("CertTimestamp", lines[n]) + ) n += 1 signature = Certification.parse_field("Signature", lines[n]) - identity = Identity(version, currency, identity_pubkey, identity_uid, identity_timestamp, identity_signature) + identity = Identity( + version, + currency, + identity_pubkey, + identity_uid, + identity_timestamp, + identity_signature, + ) return cls(version, currency, pubkey_from, identity, timestamp, signature) @classmethod - def from_inline(cls: Type[CertificationType], version: int, currency: str, blockhash: Optional[str], - inline: str) -> CertificationType: + def from_inline( + cls: Type[CertificationType], + version: int, + currency: str, + blockhash: Optional[str], + inline: str, + ) -> CertificationType: """ Return Certification instance from inline document @@ -139,7 +189,9 @@ class Certification(Document): Return a raw document of the certification """ if not isinstance(self.identity, Identity): - raise MalformedDocumentError("Can not return full certification document created from inline") + raise MalformedDocumentError( + "Can not return full certification document created from inline" + ) return """Version: {version} Type: Certification @@ -150,14 +202,16 @@ IdtyUniqueID: {certified_uid} IdtyTimestamp: {certified_ts} IdtySignature: {certified_signature} CertTimestamp: {timestamp} -""".format(version=self.version, - currency=self.currency, - issuer=self.pubkey_from, - certified_pubkey=self.identity.pubkey, - certified_uid=self.identity.uid, - certified_ts=self.identity.timestamp, - certified_signature=self.identity.signatures[0], - timestamp=self.timestamp) +""".format( + version=self.version, + currency=self.currency, + issuer=self.pubkey_from, + certified_pubkey=self.identity.pubkey, + certified_uid=self.identity.uid, + certified_ts=self.identity.timestamp, + certified_signature=self.identity.signatures[0], + timestamp=self.timestamp, + ) def sign(self, keys: list) -> None: """ @@ -168,11 +222,13 @@ CertTimestamp: {timestamp} :param keys: List of libnacl key instances """ if not isinstance(self.identity, Identity): - raise MalformedDocumentError("Can not return full certification document created from inline") + raise MalformedDocumentError( + "Can not return full certification document created from inline" + ) self.signatures = [] for key in keys: - signing = base64.b64encode(key.signature(bytes(self.raw(), 'ascii'))) + signing = base64.b64encode(key.signature(bytes(self.raw(), "ascii"))) logging.debug("Signature : \n%s", signing.decode("ascii")) self.signatures.append(signing.decode("ascii")) @@ -183,7 +239,9 @@ CertTimestamp: {timestamp} :return: """ if not isinstance(self.identity, Identity): - raise MalformedDocumentError("Can not return full certification document created from inline") + raise MalformedDocumentError( + "Can not return full certification document created from inline" + ) raw = self.raw() signed = "\n".join(self.signatures) @@ -196,5 +254,6 @@ CertTimestamp: {timestamp} :return: """ - return "{0}:{1}:{2}:{3}".format(self.pubkey_from, self.pubkey_to, - self.timestamp.number, self.signatures[0]) + return "{0}:{1}:{2}:{3}".format( + self.pubkey_from, self.pubkey_to, self.timestamp.number, self.signatures[0] + ) diff --git a/duniterpy/documents/crc_pubkey.py b/duniterpy/documents/crc_pubkey.py index 9d387708fa290e089be87733bc6de260f893e394..75167cb3b0146f7d987cd96b877fa39d6f935daf 100644 --- a/duniterpy/documents/crc_pubkey.py +++ b/duniterpy/documents/crc_pubkey.py @@ -7,14 +7,17 @@ from ..constants import PUBKEY_REGEX from ..helpers import ensure_str # required to type hint cls in classmethod -CRCPubkeyType = TypeVar('CRCPubkeyType', bound='CRCPubkey') +CRCPubkeyType = TypeVar("CRCPubkeyType", bound="CRCPubkey") class CRCPubkey: """ Class to implement a crc on a pubkey """ - re_crc_pubkey = re.compile("({pubkey_regex}):([A-Za-z0-9]{{3}})".format(pubkey_regex=PUBKEY_REGEX)) + + re_crc_pubkey = re.compile( + "({pubkey_regex}):([A-Za-z0-9]{{3}})".format(pubkey_regex=PUBKEY_REGEX) + ) def __init__(self, pubkey: str, crc: str) -> None: """ diff --git a/duniterpy/documents/document.py b/duniterpy/documents/document.py index d197c4e951810edf63c5ae456f52e8fca9915858..107dbeb235d2160da247593c4422d543fc637458 100644 --- a/duniterpy/documents/document.py +++ b/duniterpy/documents/document.py @@ -22,18 +22,20 @@ class MalformedDocumentError(Exception): # required to type hint cls in classmethod -DocumentType = TypeVar('DocumentType', bound='Document') +DocumentType = TypeVar("DocumentType", bound="Document") class Document: re_version = re.compile("Version: ([0-9]+)\n") re_currency = re.compile("Currency: ([^\n]+)\n") - re_signature = re.compile("({signature_regex})\n".format(signature_regex=SIGNATURE_REGEX)) + re_signature = re.compile( + "({signature_regex})\n".format(signature_regex=SIGNATURE_REGEX) + ) fields_parsers = { "Version": re_version, "Currency": re_currency, - "Signature": re_signature + "Signature": re_signature, } def __init__(self, version: int, currency: str, signatures: List[str]) -> None: @@ -79,7 +81,7 @@ class Document: """ self.signatures = [] for key in keys: - signing = base64.b64encode(key.signature(bytes(self.raw(), 'ascii'))) + signing = base64.b64encode(key.signature(bytes(self.raw(), "ascii"))) logging.debug("Signature : \n%s", signing.decode("ascii")) self.signatures.append(signing.decode("ascii")) diff --git a/duniterpy/documents/identity.py b/duniterpy/documents/identity.py index d59096e274ba5ca92856e5c116514db13f924ba6..a3a95cd8ce84dc7f7853ec65000d3cf4d8229e11 100644 --- a/duniterpy/documents/identity.py +++ b/duniterpy/documents/identity.py @@ -6,7 +6,7 @@ from ..constants import PUBKEY_REGEX, SIGNATURE_REGEX, BLOCK_UID_REGEX, UID_REGE from .document import Document, MalformedDocumentError # required to type hint cls in classmethod -IdentityType = TypeVar('IdentityType', bound='Identity') +IdentityType = TypeVar("IdentityType", bound="Identity") class Identity(Document): @@ -14,26 +14,45 @@ class Identity(Document): A document describing a self certification. """ - re_inline = re.compile("({pubkey_regex}):({signature_regex}):({block_uid_regex}):([^\n]+)\n" - .format(pubkey_regex=PUBKEY_REGEX, - signature_regex=SIGNATURE_REGEX, - block_uid_regex=BLOCK_UID_REGEX)) + re_inline = re.compile( + "({pubkey_regex}):({signature_regex}):({block_uid_regex}):([^\n]+)\n".format( + pubkey_regex=PUBKEY_REGEX, + signature_regex=SIGNATURE_REGEX, + block_uid_regex=BLOCK_UID_REGEX, + ) + ) re_type = re.compile("Type: (Identity)") - re_issuer = re.compile("Issuer: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX)) + re_issuer = re.compile( + "Issuer: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX) + ) re_unique_id = re.compile("UniqueID: ({uid_regex})\n".format(uid_regex=UID_REGEX)) re_uid = re.compile("UID:([^\n]+)\n") - re_meta_ts = re.compile("META:TS:({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX)) - re_timestamp = re.compile("Timestamp: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX)) - - fields_parsers = {**Document.fields_parsers, **{ - "Type": re_type, - "UniqueID": re_unique_id, - "Issuer": re_issuer, - "Timestamp": re_timestamp - }} - - def __init__(self, version: int, currency: str, pubkey: str, uid: str, ts: BlockUID, - signature: Optional[str]) -> None: + re_meta_ts = re.compile( + "META:TS:({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX) + ) + re_timestamp = re.compile( + "Timestamp: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX) + ) + + fields_parsers = { + **Document.fields_parsers, + **{ + "Type": re_type, + "UniqueID": re_unique_id, + "Issuer": re_issuer, + "Timestamp": re_timestamp, + }, + } + + def __init__( + self, + version: int, + currency: str, + pubkey: str, + uid: str, + ts: BlockUID, + signature: Optional[str], + ) -> None: """ Create an identity document @@ -53,7 +72,9 @@ class Identity(Document): self.uid = uid @classmethod - def from_inline(cls: Type[IdentityType], version: int, currency: str, inline: str) -> IdentityType: + def from_inline( + cls: Type[IdentityType], version: int, currency: str, inline: str + ) -> IdentityType: """ Return Identity instance from inline Identity string :param version: Document version number @@ -114,11 +135,13 @@ Currency: {currency} Issuer: {pubkey} UniqueID: {uid} Timestamp: {timestamp} -""".format(version=self.version, - currency=self.currency, - pubkey=self.pubkey, - uid=self.uid, - timestamp=self.timestamp) +""".format( + version=self.version, + currency=self.currency, + pubkey=self.pubkey, + uid=self.uid, + timestamp=self.timestamp, + ) def inline(self) -> str: """ @@ -129,4 +152,5 @@ Timestamp: {timestamp} pubkey=self.pubkey, signature=self.signatures[0], timestamp=self.timestamp, - uid=self.uid) + uid=self.uid, + ) diff --git a/duniterpy/documents/membership.py b/duniterpy/documents/membership.py index f7726c6d9daf9fb1764d3c07dd86a9e5e3f364cd..d42ead3f0c484ceee081b6adbf96fb1d1d08ee7b 100644 --- a/duniterpy/documents/membership.py +++ b/duniterpy/documents/membership.py @@ -11,7 +11,7 @@ from .document import Document, MalformedDocumentError from ..constants import BLOCK_UID_REGEX, SIGNATURE_REGEX, PUBKEY_REGEX # required to type hint cls in classmethod -MembershipType = TypeVar('MembershipType', bound='Membership') +MembershipType = TypeVar("MembershipType", bound="Membership") class Membership(Document): @@ -31,28 +31,49 @@ class Membership(Document): # PUBLIC_KEY:SIGNATURE:NUMBER:HASH:TIMESTAMP:USER_ID re_inline = re.compile( - "({pubkey_regex}):({signature_regex}):({ms_block_uid_regex}):({identity_block_uid_regex}):([^\n]+)\n" - .format(pubkey_regex=PUBKEY_REGEX, signature_regex=SIGNATURE_REGEX, - ms_block_uid_regex=BLOCK_UID_REGEX, - identity_block_uid_regex=BLOCK_UID_REGEX)) + "({pubkey_regex}):({signature_regex}):({ms_block_uid_regex}):({identity_block_uid_regex}):([^\n]+)\n".format( + pubkey_regex=PUBKEY_REGEX, + signature_regex=SIGNATURE_REGEX, + ms_block_uid_regex=BLOCK_UID_REGEX, + identity_block_uid_regex=BLOCK_UID_REGEX, + ) + ) re_type = re.compile("Type: (Membership)") - re_issuer = re.compile("Issuer: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX)) - re_block = re.compile("Block: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX)) + re_issuer = re.compile( + "Issuer: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX) + ) + re_block = re.compile( + "Block: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX) + ) re_membership_type = re.compile("Membership: (IN|OUT)") re_userid = re.compile("UserID: ([^\n]+)\n") - re_certts = re.compile("CertTS: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX)) - - fields_parsers = {**Document.fields_parsers, **{ - "Type": re_type, - "Issuer": re_issuer, - "Block": re_block, - "Membership": re_membership_type, - "UserID": re_userid, - "CertTS": re_certts - }} - - def __init__(self, version: int, currency: str, issuer: str, membership_ts: BlockUID, - membership_type: str, uid: str, identity_ts: BlockUID, signature: str) -> None: + re_certts = re.compile( + "CertTS: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX) + ) + + fields_parsers = { + **Document.fields_parsers, + **{ + "Type": re_type, + "Issuer": re_issuer, + "Block": re_block, + "Membership": re_membership_type, + "UserID": re_userid, + "CertTS": re_certts, + }, + } + + def __init__( + self, + version: int, + currency: str, + issuer: str, + membership_ts: BlockUID, + membership_type: str, + uid: str, + identity_ts: BlockUID, + signature: str, + ) -> None: """ Create a membership document @@ -73,8 +94,13 @@ class Membership(Document): self.identity_ts = identity_ts @classmethod - def from_inline(cls: Type[MembershipType], version: int, currency: str, membership_type: str, - inline: str) -> MembershipType: + def from_inline( + cls: Type[MembershipType], + version: int, + currency: str, + membership_type: str, + inline: str, + ) -> MembershipType: """ Return Membership instance from inline format @@ -92,7 +118,16 @@ class Membership(Document): membership_ts = BlockUID.from_str(data.group(3)) identity_ts = BlockUID.from_str(data.group(4)) uid = data.group(5) - return cls(version, currency, issuer, membership_ts, membership_type, uid, identity_ts, signature) + return cls( + version, + currency, + issuer, + membership_ts, + membership_type, + uid, + identity_ts, + signature, + ) @classmethod def from_signed_raw(cls: Type[MembershipType], signed_raw: str) -> MembershipType: @@ -132,8 +167,16 @@ class Membership(Document): signature = Membership.parse_field("Signature", lines[n]) n += 1 - return cls(version, currency, issuer, membership_ts, - membership_type, uid, identity_ts, signature) + return cls( + version, + currency, + issuer, + membership_ts, + membership_type, + uid, + identity_ts, + signature, + ) def raw(self) -> str: """ @@ -149,21 +192,25 @@ Block: {3} Membership: {4} UserID: {5} CertTS: {6} -""".format(self.version, - self.currency, - self.issuer, - self.membership_ts, - self.membership_type, - self.uid, - self.identity_ts) +""".format( + self.version, + self.currency, + self.issuer, + self.membership_ts, + self.membership_type, + self.uid, + self.identity_ts, + ) def inline(self) -> str: """ Return inline string format of the Membership instance :return: """ - return "{0}:{1}:{2}:{3}:{4}".format(self.issuer, - self.signatures[0], - self.membership_ts, - self.identity_ts, - self.uid) + return "{0}:{1}:{2}:{3}:{4}".format( + self.issuer, + self.signatures[0], + self.membership_ts, + self.identity_ts, + self.uid, + ) diff --git a/duniterpy/documents/peer.py b/duniterpy/documents/peer.py index 2542544f57fc741e60eb1d103f4971df33f2c183..bc26cdbc0ed5dcea2a1a65cca1b1c948bf502e38 100644 --- a/duniterpy/documents/peer.py +++ b/duniterpy/documents/peer.py @@ -7,7 +7,7 @@ from .block_uid import BlockUID from ..constants import BLOCK_HASH_REGEX, PUBKEY_REGEX # required to type hint cls in classmethod -PeerType = TypeVar('PeerType', bound='Peer') +PeerType = TypeVar("PeerType", bound="Peer") class Peer(Document): @@ -28,19 +28,33 @@ class Peer(Document): """ re_type = re.compile("Type: (Peer)") - re_pubkey = re.compile("PublicKey: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX)) - re_block = re.compile("Block: ([0-9]+-{block_hash_regex})\n".format(block_hash_regex=BLOCK_HASH_REGEX)) + re_pubkey = re.compile( + "PublicKey: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX) + ) + re_block = re.compile( + "Block: ([0-9]+-{block_hash_regex})\n".format(block_hash_regex=BLOCK_HASH_REGEX) + ) re_endpoints = re.compile("(Endpoints:)\n") - fields_parsers = {**Document.fields_parsers, **{ - "Type": re_type, - "Pubkey": re_pubkey, - "Block": re_block, - "Endpoints": re_endpoints - }} - - def __init__(self, version: int, currency: str, pubkey: str, block_uid: BlockUID, - endpoints: List[Endpoint], signature: str) -> None: + fields_parsers = { + **Document.fields_parsers, + **{ + "Type": re_type, + "Pubkey": re_pubkey, + "Block": re_block, + "Endpoints": re_endpoints, + }, + } + + def __init__( + self, + version: int, + currency: str, + pubkey: str, + block_uid: BlockUID, + endpoints: List[Endpoint], + signature: str, + ) -> None: """ Init Peer instance @@ -110,7 +124,9 @@ Currency: {1} PublicKey: {2} Block: {3} Endpoints: -""".format(self.version, self.currency, self.pubkey, self.blockUID) +""".format( + self.version, self.currency, self.pubkey, self.blockUID + ) for _endpoint in self.endpoints: doc += "{0}\n".format(_endpoint.inline()) diff --git a/duniterpy/documents/revocation.py b/duniterpy/documents/revocation.py index ceefdf38374df0e1ef891cdb7f6534c04d6ac795..8292e8d2f4256dcbce863145327b54f68f8318d3 100644 --- a/duniterpy/documents/revocation.py +++ b/duniterpy/documents/revocation.py @@ -7,33 +7,50 @@ from .document import Document, MalformedDocumentError from .identity import Identity # required to type hint cls in classmethod -RevocationType = TypeVar('RevocationType', bound='Revocation') +RevocationType = TypeVar("RevocationType", bound="Revocation") class Revocation(Document): """ A document describing a self-revocation. """ - re_inline = re.compile("({pubkey_regex}):({signature_regex})\n".format( - pubkey_regex=PUBKEY_REGEX, - signature_regex=SIGNATURE_REGEX - )) + + re_inline = re.compile( + "({pubkey_regex}):({signature_regex})\n".format( + pubkey_regex=PUBKEY_REGEX, signature_regex=SIGNATURE_REGEX + ) + ) re_type = re.compile("Type: (Revocation)") - re_issuer = re.compile("Issuer: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX)) + re_issuer = re.compile( + "Issuer: ({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX) + ) re_uniqueid = re.compile("IdtyUniqueID: ([^\n]+)\n") - re_timestamp = re.compile("IdtyTimestamp: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX)) - re_idtysignature = re.compile("IdtySignature: ({signature_regex})\n".format(signature_regex=SIGNATURE_REGEX)) - - fields_parsers = {**Document.fields_parsers, **{ - "Type": re_type, - "Issuer": re_issuer, - "IdtyUniqueID": re_uniqueid, - "IdtyTimestamp": re_timestamp, - "IdtySignature": re_idtysignature, - }} - - def __init__(self, version: int, currency: str, identity: Union[Identity, str], signature: str) -> None: + re_timestamp = re.compile( + "IdtyTimestamp: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX) + ) + re_idtysignature = re.compile( + "IdtySignature: ({signature_regex})\n".format(signature_regex=SIGNATURE_REGEX) + ) + + fields_parsers = { + **Document.fields_parsers, + **{ + "Type": re_type, + "Issuer": re_issuer, + "IdtyUniqueID": re_uniqueid, + "IdtyTimestamp": re_timestamp, + "IdtySignature": re_idtysignature, + }, + } + + def __init__( + self, + version: int, + currency: str, + identity: Union[Identity, str], + signature: str, + ) -> None: """ Init Revocation instance @@ -48,7 +65,9 @@ class Revocation(Document): self.pubkey = identity.pubkey if isinstance(identity, Identity) else identity @classmethod - def from_inline(cls: Type[RevocationType], version: int, currency: str, inline: str) -> RevocationType: + def from_inline( + cls: Type[RevocationType], version: int, currency: str, inline: str + ) -> RevocationType: """ Return Revocation document instance from inline string @@ -103,7 +122,14 @@ class Revocation(Document): signature = Revocation.parse_field("Signature", lines[n]) n += 1 - identity = Identity(version, currency, issuer, identity_uid, identity_timestamp, identity_signature) + identity = Identity( + version, + currency, + issuer, + identity_uid, + identity_timestamp, + identity_signature, + ) return cls(version, currency, identity, signature) @@ -156,7 +182,9 @@ class Revocation(Document): :return: """ if not isinstance(self.identity, Identity): - raise MalformedDocumentError("Can not return full revocation document created from inline") + raise MalformedDocumentError( + "Can not return full revocation document created from inline" + ) return """Version: {version} Type: Revocation @@ -165,12 +193,14 @@ Issuer: {pubkey} IdtyUniqueID: {uid} IdtyTimestamp: {timestamp} IdtySignature: {signature} -""".format(version=self.version, - currency=self.currency, - pubkey=self.identity.pubkey, - uid=self.identity.uid, - timestamp=self.identity.timestamp, - signature=self.identity.signatures[0]) +""".format( + version=self.version, + currency=self.currency, + pubkey=self.identity.pubkey, + uid=self.identity.uid, + timestamp=self.identity.timestamp, + signature=self.identity.signatures[0], + ) def sign(self, keys: list) -> None: """ @@ -181,11 +211,13 @@ IdtySignature: {signature} :return: """ if not isinstance(self.identity, Identity): - raise MalformedDocumentError("Can not return full revocation document created from inline") + raise MalformedDocumentError( + "Can not return full revocation document created from inline" + ) self.signatures = [] for key in keys: - signing = base64.b64encode(key.signature(bytes(self.raw(), 'ascii'))) + signing = base64.b64encode(key.signature(bytes(self.raw(), "ascii"))) self.signatures.append(signing.decode("ascii")) def signed_raw(self) -> str: @@ -195,7 +227,9 @@ IdtySignature: {signature} :return: """ if not isinstance(self.identity, Identity): - raise MalformedDocumentError("Can not return full revocation document created from inline") + raise MalformedDocumentError( + "Can not return full revocation document created from inline" + ) raw = self.raw() signed = "\n".join(self.signatures) diff --git a/duniterpy/documents/transaction.py b/duniterpy/documents/transaction.py index 6d5af61be3ad002666457e7c2fd831d8948c4751..d2632ecc95b93ec2a2f429fd4299ba3ae1bf2bcf 100644 --- a/duniterpy/documents/transaction.py +++ b/duniterpy/documents/transaction.py @@ -6,7 +6,12 @@ import pypeg2 from duniterpy.grammars.output import Condition from .block_uid import BlockUID from .document import Document, MalformedDocumentError -from ..constants import PUBKEY_REGEX, TRANSACTION_HASH_REGEX, BLOCK_ID_REGEX, BLOCK_UID_REGEX +from ..constants import ( + PUBKEY_REGEX, + TRANSACTION_HASH_REGEX, + BLOCK_ID_REGEX, + BLOCK_UID_REGEX, +) from ..grammars import output @@ -38,7 +43,7 @@ def reduce_base(amount: int, base: int) -> tuple: # required to type hint cls in classmethod -InputSourceType = TypeVar('InputSourceType', bound='InputSource') +InputSourceType = TypeVar("InputSourceType", bound="InputSource") class InputSource: @@ -49,14 +54,19 @@ class InputSource: INDEX:SOURCE:FINGERPRINT:AMOUNT """ + re_inline = re.compile( "([0-9]+):([0-9]):(?:(?:(D):({pubkey_regex}):({block_id_regex}))|(?:(T):({transaction_hash_regex}):\ -([0-9]+)))" - .format(pubkey_regex=PUBKEY_REGEX, - block_id_regex=BLOCK_ID_REGEX, - transaction_hash_regex=TRANSACTION_HASH_REGEX)) +([0-9]+)))".format( + pubkey_regex=PUBKEY_REGEX, + block_id_regex=BLOCK_ID_REGEX, + transaction_hash_regex=TRANSACTION_HASH_REGEX, + ) + ) - def __init__(self, amount: int, base: int, source: str, origin_id: str, index: int) -> None: + def __init__( + self, amount: int, base: int, source: str, origin_id: str, index: int + ) -> None: """ An input source can come from a dividend or a transaction. @@ -79,11 +89,13 @@ class InputSource: """ if not isinstance(other, InputSource): return NotImplemented - return self.amount == other.amount and \ - self.base == other.base and \ - self.source == other.source and \ - self.origin_id == other.origin_id and \ - self.index == other.index + return ( + self.amount == other.amount + and self.base == other.base + and self.source == other.source + and self.origin_id == other.origin_id + and self.index == other.index + ) def __hash__(self) -> int: return hash((self.amount, self.base, self.source, self.origin_id, self.index)) @@ -119,21 +131,20 @@ class InputSource: :return: """ - return "{0}:{1}:{2}:{3}:{4}".format(self.amount, - self.base, - self.source, - self.origin_id, - self.index) + return "{0}:{1}:{2}:{3}:{4}".format( + self.amount, self.base, self.source, self.origin_id, self.index + ) # required to type hint cls in classmethod -OutputSourceType = TypeVar('OutputSourceType', bound='OutputSource') +OutputSourceType = TypeVar("OutputSourceType", bound="OutputSource") class OutputSource: """ A Transaction OUTPUT """ + re_inline = re.compile("([0-9]+):([0-9]):(.*)") def __init__(self, amount: int, base: int, condition: str) -> None: @@ -154,9 +165,11 @@ class OutputSource: """ if not isinstance(other, OutputSource): return NotImplemented - return self.amount == other.amount and \ - self.base == other.base and \ - self.condition == other.condition + return ( + self.amount == other.amount + and self.base == other.base + and self.condition == other.condition + ) def __hash__(self) -> int: return hash((self.amount, self.base, self.condition)) @@ -184,8 +197,9 @@ class OutputSource: :return: """ - return "{0}:{1}:{2}".format(self.amount, self.base, - pypeg2.compose(self.condition, output.Condition)) + return "{0}:{1}:{2}".format( + self.amount, self.base, pypeg2.compose(self.condition, output.Condition) + ) def inline_condition(self) -> str: """ @@ -213,13 +227,14 @@ class OutputSource: # required to type hint cls in classmethod -SIGParameterType = TypeVar('SIGParameterType', bound='SIGParameter') +SIGParameterType = TypeVar("SIGParameterType", bound="SIGParameter") class SIGParameter: """ A Transaction UNLOCK SIG parameter """ + re_sig = re.compile("SIG\\(([0-9]+)\\)") def __init__(self, index: int) -> None: @@ -230,7 +245,6 @@ class SIGParameter: """ self.index = index - def __eq__(self, other: Any) -> bool: """ Check SIGParameter instances equality @@ -242,9 +256,10 @@ class SIGParameter: def __hash__(self) -> int: return hash((self.index)) - @classmethod - def from_parameter(cls: Type[SIGParameterType], parameter: str) -> Optional[SIGParameterType]: + def from_parameter( + cls: Type[SIGParameterType], parameter: str + ) -> Optional[SIGParameterType]: """ Return a SIGParameter instance from an index parameter @@ -268,13 +283,14 @@ class SIGParameter: # required to type hint cls in classmethod -XHXParameterType = TypeVar('XHXParameterType', bound='XHXParameter') +XHXParameterType = TypeVar("XHXParameterType", bound="XHXParameter") class XHXParameter: """ A Transaction UNLOCK XHX parameter """ + re_xhx = re.compile("XHX\\(([0-9]+)\\)") def __init__(self, integer: int) -> None: @@ -285,7 +301,6 @@ class XHXParameter: """ self.integer = integer - def __eq__(self, other: Any) -> bool: """ Check XHXParameter instances equality @@ -297,9 +312,10 @@ class XHXParameter: def __hash__(self) -> int: return hash((self.integer)) - @classmethod - def from_parameter(cls: Type[XHXParameterType], parameter: str) -> Optional[XHXParameterType]: + def from_parameter( + cls: Type[XHXParameterType], parameter: str + ) -> Optional[XHXParameterType]: """ Return a XHXParameter instance from an index parameter @@ -326,13 +342,14 @@ class XHXParameter: # required to type hint cls in classmethod -UnlockParameterType = TypeVar('UnlockParameterType', bound='UnlockParameter') +UnlockParameterType = TypeVar("UnlockParameterType", bound="UnlockParameter") class UnlockParameter: - @classmethod - def from_parameter(cls: Type[UnlockParameterType], parameter: str) -> Optional[Union[SIGParameter, XHXParameter]]: + def from_parameter( + cls: Type[UnlockParameterType], parameter: str + ) -> Optional[Union[SIGParameter, XHXParameter]]: """ Return UnlockParameter instance from parameter string @@ -355,16 +372,19 @@ class UnlockParameter: # required to type hint cls in classmethod -UnlockType = TypeVar('UnlockType', bound='Unlock') +UnlockType = TypeVar("UnlockType", bound="Unlock") class Unlock: """ A Transaction UNLOCK """ + re_inline = re.compile("([0-9]+):((?:SIG\\([0-9]+\\)|XHX\\([0-9]+\\)|\\s)+)\n") - def __init__(self, index: int, parameters: List[Union[SIGParameter, XHXParameter]]) -> None: + def __init__( + self, index: int, parameters: List[Union[SIGParameter, XHXParameter]] + ) -> None: """ Init Unlock instance @@ -374,7 +394,6 @@ class Unlock: self.index = index self.parameters = parameters - def __eq__(self, other: Any) -> bool: """ Check Unlock instances equality @@ -391,7 +410,6 @@ class Unlock: def __hash__(self) -> int: return hash((self.index, self.parameters)) - @classmethod def from_inline(cls: Type[UnlockType], inline: str) -> UnlockType: """ @@ -405,7 +423,7 @@ class Unlock: if data is None: raise MalformedDocumentError("Inline input") index = int(data.group(1)) - parameters_str = data.group(2).split(' ') + parameters_str = data.group(2).split(" ") parameters = [] for p in parameters_str: param = UnlockParameter.from_parameter(p) @@ -419,11 +437,11 @@ class Unlock: :return: """ - return "{0}:{1}".format(self.index, ' '.join([str(p) for p in self.parameters])) + return "{0}:{1}".format(self.index, " ".join([str(p) for p in self.parameters])) # required to type hint cls in classmethod -TransactionType = TypeVar('TransactionType', bound='Transaction') +TransactionType = TypeVar("TransactionType", bound="Transaction") class Transaction(Document): @@ -462,9 +480,15 @@ class Transaction(Document): """ re_type = re.compile("Type: (Transaction)\n") - re_header = re.compile("TX:([0-9]+):([0-9]+):([0-9]+):([0-9]+):([0-9]+):([01]):([0-9]+)\n") - re_compact_blockstamp = re.compile("({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX)) - re_blockstamp = re.compile("Blockstamp: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX)) + re_header = re.compile( + "TX:([0-9]+):([0-9]+):([0-9]+):([0-9]+):([0-9]+):([01]):([0-9]+)\n" + ) + re_compact_blockstamp = re.compile( + "({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX) + ) + re_blockstamp = re.compile( + "Blockstamp: ({block_uid_regex})\n".format(block_uid_regex=BLOCK_UID_REGEX) + ) re_locktime = re.compile("Locktime: ([0-9]+)\n") re_issuers = re.compile("Issuers:\n") re_inputs = re.compile("Inputs:\n") @@ -474,25 +498,38 @@ class Transaction(Document): re_comment = re.compile("Comment: ([^\n]*)\n") re_pubkey = re.compile("({pubkey_regex})\n".format(pubkey_regex=PUBKEY_REGEX)) - fields_parsers = {**Document.fields_parsers, **{ - "Type": re_type, - "Blockstamp": re_blockstamp, - "CompactBlockstamp": re_compact_blockstamp, - "Locktime": re_locktime, - "TX": re_header, - "Issuers": re_issuers, - "Inputs": re_inputs, - "Unlocks": re_unlocks, - "Outputs": re_outputs, - "Comment": re_comment, - "Compact comment": re_compact_comment, - "Pubkey": re_pubkey + fields_parsers = { + **Document.fields_parsers, + **{ + "Type": re_type, + "Blockstamp": re_blockstamp, + "CompactBlockstamp": re_compact_blockstamp, + "Locktime": re_locktime, + "TX": re_header, + "Issuers": re_issuers, + "Inputs": re_inputs, + "Unlocks": re_unlocks, + "Outputs": re_outputs, + "Comment": re_comment, + "Compact comment": re_compact_comment, + "Pubkey": re_pubkey, + }, } - } - def __init__(self, version: int, currency: str, blockstamp: Optional[BlockUID], locktime: int, issuers: List[str], - inputs: List[InputSource], unlocks: List[Unlock], outputs: List[OutputSource], - comment: str, signatures: List[str], time: Optional[int] = None) -> None: + def __init__( + self, + version: int, + currency: str, + blockstamp: Optional[BlockUID], + locktime: int, + issuers: List[str], + inputs: List[InputSource], + unlocks: List[Unlock], + outputs: List[OutputSource], + comment: str, + signatures: List[str], + time: Optional[int] = None, + ) -> None: """ Init Transaction instance @@ -518,32 +555,47 @@ class Transaction(Document): self.comment = comment self.time = time - def __eq__(self, other: Any) -> bool: """ Check Transaction instances equality """ if not isinstance(other, Transaction): return NotImplemented - return self.version == other.version and \ - self.currency == other.currency and \ - self.signatures == other.signatures and \ - self.blockstamp == other.blockstamp and \ - self.locktime == other.locktime and \ - self.issuers == other.issuers and \ - self.inputs == other.inputs and \ - self.unlocks == other.unlocks and \ - self.outputs == other.outputs and \ - self.comment == other.comment and \ - self.time == other.time - + return ( + self.version == other.version + and self.currency == other.currency + and self.signatures == other.signatures + and self.blockstamp == other.blockstamp + and self.locktime == other.locktime + and self.issuers == other.issuers + and self.inputs == other.inputs + and self.unlocks == other.unlocks + and self.outputs == other.outputs + and self.comment == other.comment + and self.time == other.time + ) def __hash__(self) -> int: - return hash((self.version, self.currency, self.signatures, self.blockstamp, self.locktime, self.issuers, self.inputs, self.unlocks, self.outputs, self.comment, self.time)) - + return hash( + ( + self.version, + self.currency, + self.signatures, + self.blockstamp, + self.locktime, + self.issuers, + self.inputs, + self.unlocks, + self.outputs, + self.comment, + self.time, + ) + ) @classmethod - def from_bma_history(cls: Type[TransactionType], currency: str, tx_data: Dict) -> TransactionType: + def from_bma_history( + cls: Type[TransactionType], currency: str, tx_data: Dict + ) -> TransactionType: """ Get the transaction instance from json @@ -554,9 +606,10 @@ class Transaction(Document): """ tx_data = tx_data.copy() tx_data["currency"] = currency - for data_list in ('issuers', 'outputs', 'inputs', 'unlocks', 'signatures'): - tx_data['multiline_{0}'.format(data_list)] = '\n'.join(tx_data[data_list]) - return cls.from_signed_raw("""Version: {version} + for data_list in ("issuers", "outputs", "inputs", "unlocks", "signatures"): + tx_data["multiline_{0}".format(data_list)] = "\n".join(tx_data[data_list]) + return cls.from_signed_raw( + """Version: {version} Type: Transaction Currency: {currency} Blockstamp: {blockstamp} @@ -571,10 +624,16 @@ Outputs: {multiline_outputs} Comment: {comment} {multiline_signatures} -""".format(**tx_data), tx_data["time"]) +""".format( + **tx_data + ), + tx_data["time"], + ) @classmethod - def from_compact(cls: Type[TransactionType], currency: str, compact: str) -> TransactionType: + def from_compact( + cls: Type[TransactionType], currency: str, compact: str + ) -> TransactionType: """ Return Transaction instance from compact string format @@ -597,7 +656,9 @@ Comment: {comment} locktime = int(header_data.group(7)) n += 1 - blockstamp = BlockUID.from_str(Transaction.parse_field("CompactBlockstamp", lines[n])) + blockstamp = BlockUID.from_str( + Transaction.parse_field("CompactBlockstamp", lines[n]) + ) n += 1 issuers = [] @@ -642,10 +703,23 @@ Comment: {comment} else: raise MalformedDocumentError("Compact TX Signatures") - return cls(version, currency, blockstamp, locktime, issuers, inputs, unlocks, outputs, comment, signatures) + return cls( + version, + currency, + blockstamp, + locktime, + issuers, + inputs, + unlocks, + outputs, + comment, + signatures, + ) @classmethod - def from_signed_raw(cls: Type[TransactionType], raw: str, time: int = 0) -> TransactionType: + def from_signed_raw( + cls: Type[TransactionType], raw: str, time: int = 0 + ) -> TransactionType: """ Return a Transaction instance from a raw string format @@ -715,8 +789,19 @@ Comment: {comment} signatures.append(sign) n += 1 - return cls(version, currency, blockstamp, locktime, issuers, inputs, unlocks, outputs, - comment, signatures, time) + return cls( + version, + currency, + blockstamp, + locktime, + issuers, + inputs, + unlocks, + outputs, + comment, + signatures, + time, + ) def raw(self) -> str: """ @@ -727,8 +812,9 @@ Comment: {comment} doc = """Version: {0} Type: Transaction Currency: {1} -""".format(self.version, - self.currency) +""".format( + self.version, self.currency + ) doc += "Blockstamp: {0}\n".format(self.blockstamp) @@ -770,13 +856,15 @@ PUBLIC_KEY:AMOUNT ... COMMENT" """ - doc = "TX:{0}:{1}:{2}:{3}:{4}:{5}:{6}\n".format(self.version, - len(self.issuers), - len(self.inputs), - len(self.unlocks), - len(self.outputs), - '1' if self.comment != "" else '0', - self.locktime) + doc = "TX:{0}:{1}:{2}:{3}:{4}:{5}:{6}\n".format( + self.version, + len(self.issuers), + len(self.inputs), + len(self.unlocks), + len(self.outputs), + "1" if self.comment != "" else "0", + self.locktime, + ) doc += "{0}\n".format(self.blockstamp) for pubkey in self.issuers: @@ -801,9 +889,20 @@ class SimpleTransaction(Transaction): ... """ - def __init__(self, version: int, currency: str, blockstamp: BlockUID, locktime: int, issuer: str, - single_input: InputSource, unlocks: List[Unlock], outputs: List[OutputSource], comment: str, - signature: str, time: int) -> None: + def __init__( + self, + version: int, + currency: str, + blockstamp: BlockUID, + locktime: int, + issuer: str, + single_input: InputSource, + unlocks: List[Unlock], + outputs: List[OutputSource], + comment: str, + signature: str, + time: int, + ) -> None: """ Init instance @@ -819,8 +918,19 @@ class SimpleTransaction(Transaction): :param time: time when the transaction enters the blockchain :param signature: Signature """ - super().__init__(version, currency, blockstamp, locktime, [issuer], [single_input], unlocks, - outputs, comment, [signature], time) + super().__init__( + version, + currency, + blockstamp, + locktime, + [issuer], + [single_input], + unlocks, + outputs, + comment, + [signature], + time, + ) @staticmethod def is_simple(tx: Transaction) -> bool: @@ -845,7 +955,7 @@ class SimpleTransaction(Transaction): simple = False for o in tx.outputs: # if right condition is not None... - if getattr(o.condition, 'right', None): + if getattr(o.condition, "right", None): simple = False # if left is not SIG... elif type(o.condition.left) is not output.SIG: diff --git a/duniterpy/documents/ws2p/heads.py b/duniterpy/documents/ws2p/heads.py index c9ba61f8fdfef67c1df4a2cec5c8fed10366e999..09004cf9bb1fa6c82e17644e2e3828bd20f5bd8c 100644 --- a/duniterpy/documents/ws2p/heads.py +++ b/duniterpy/documents/ws2p/heads.py @@ -4,8 +4,15 @@ import attr from ..block_uid import BlockUID from ..document import MalformedDocumentError -from ...constants import WS2P_PUBLIC_PREFIX_REGEX, WS2P_PRIVATE_PREFIX_REGEX, WS2P_HEAD_REGEX, \ - PUBKEY_REGEX, SIGNATURE_REGEX, WS2PID_REGEX, BLOCK_UID_REGEX +from ...constants import ( + WS2P_PUBLIC_PREFIX_REGEX, + WS2P_PRIVATE_PREFIX_REGEX, + WS2P_HEAD_REGEX, + PUBKEY_REGEX, + SIGNATURE_REGEX, + WS2PID_REGEX, + BLOCK_UID_REGEX, +) @attr.s() @@ -13,9 +20,11 @@ class API: private = attr.ib(type=str) public = attr.ib(type=str) - re_inline = re.compile("WS2P({ws2p_private})?({ws2p_public})?".format( - ws2p_private=WS2P_PRIVATE_PREFIX_REGEX, - ws2p_public=WS2P_PUBLIC_PREFIX_REGEX)) + re_inline = re.compile( + "WS2P({ws2p_private})?({ws2p_public})?".format( + ws2p_private=WS2P_PRIVATE_PREFIX_REGEX, ws2p_public=WS2P_PUBLIC_PREFIX_REGEX + ) + ) @classmethod def from_inline(cls, inline: str): @@ -42,7 +51,7 @@ class Head: data = Head.re_inline.match(inline) if data is None: raise MalformedDocumentError("Head") - head = data.group(0).split(':') + head = data.group(0).split(":") version = int(head[1]) if len(head) == 2 else 0 return cls(version) except AttributeError: @@ -57,18 +66,22 @@ class HeadV0: """ A document describing a self certification. """ + signature = attr.ib(type=str) api = attr.ib(type=API) head = attr.ib(type=Head) pubkey = attr.ib(type=str) blockstamp = attr.ib(type=BlockUID) - re_inline = re.compile("^(WS2P(?:{ws2p_private})?(?:{ws2p_public})?):({head}):({pubkey}):({blockstamp})(?::)?(.*)" - .format(ws2p_private=WS2P_PRIVATE_PREFIX_REGEX, - ws2p_public=WS2P_PUBLIC_PREFIX_REGEX, - head=WS2P_HEAD_REGEX, - pubkey=PUBKEY_REGEX, - blockstamp=BLOCK_UID_REGEX)) + re_inline = re.compile( + "^(WS2P(?:{ws2p_private})?(?:{ws2p_public})?):({head}):({pubkey}):({blockstamp})(?::)?(.*)".format( + ws2p_private=WS2P_PRIVATE_PREFIX_REGEX, + ws2p_public=WS2P_PUBLIC_PREFIX_REGEX, + head=WS2P_HEAD_REGEX, + pubkey=PUBKEY_REGEX, + blockstamp=BLOCK_UID_REGEX, + ) + ) re_signature = re.compile(SIGNATURE_REGEX) @@ -88,8 +101,14 @@ class HeadV0: raise MalformedDocumentError("HeadV0") def inline(self) -> str: - values = (str(v) for v in attr.astuple(self, recurse=False, - filter=attr.filters.exclude(attr.fields(HeadV0).signature))) + values = ( + str(v) + for v in attr.astuple( + self, + recurse=False, + filter=attr.filters.exclude(attr.fields(HeadV0).signature), + ) + ) return ":".join(values) @@ -101,11 +120,14 @@ class HeadV1: software_version = attr.ib(type=str) pow_prefix = attr.ib(type=int) - re_inline = re.compile("({ws2pid}):({software}):({software_version}):({pow_prefix})(?::)?(.*)".format( - ws2pid=WS2PID_REGEX, - software="[A-Za-z-_]+", - software_version="[0-9]+[.][0-9]+[.][0-9]+", - pow_prefix="[0-9]+")) + re_inline = re.compile( + "({ws2pid}):({software}):({software_version}):({pow_prefix})(?::)?(.*)".format( + ws2pid=WS2PID_REGEX, + software="[A-Za-z-_]+", + software_version="[0-9]+[.][0-9]+[.][0-9]+", + pow_prefix="[0-9]+", + ) + ) @classmethod def from_inline(cls, inline: str, signature: str): @@ -124,7 +146,12 @@ class HeadV1: raise MalformedDocumentError("HeadV1") def inline(self) -> str: - values = [str(v) for v in attr.astuple(self, True, filter=attr.filters.exclude(attr.fields(HeadV1).v0))] + values = [ + str(v) + for v in attr.astuple( + self, True, filter=attr.filters.exclude(attr.fields(HeadV1).v0) + ) + ] return self.v0.inline() + ":" + ":".join(values) @property @@ -146,9 +173,11 @@ class HeadV2: free_member_room = attr.ib(type=int) free_mirror_room = attr.ib(type=int) - re_inline = re.compile("({free_member_room}):({free_mirror_room})(?::)?(.*)".format( - free_member_room="[0-9]+", - free_mirror_room="[0-9]+")) + re_inline = re.compile( + "({free_member_room}):({free_mirror_room})(?::)?(.*)".format( + free_member_room="[0-9]+", free_mirror_room="[0-9]+" + ) + ) @classmethod def from_inline(cls, inline: str, signature: str): @@ -164,7 +193,12 @@ class HeadV2: raise MalformedDocumentError("HeadV2") def inline(self) -> str: - values = (str(v) for v in attr.astuple(self, True, filter=attr.filters.exclude(attr.fields(HeadV2).v1))) + values = ( + str(v) + for v in attr.astuple( + self, True, filter=attr.filters.exclude(attr.fields(HeadV2).v1) + ) + ) return self.v1.inline() + ":" + ":".join(values) @property diff --git a/duniterpy/grammars/output.py b/duniterpy/grammars/output.py index dea06e7980de2449630a691fad7a2013741a62e9..475036540c95742f404a0097c0b56a6807b99719 100644 --- a/duniterpy/grammars/output.py +++ b/duniterpy/grammars/output.py @@ -9,6 +9,7 @@ class Pubkey(str): """ Pubkey in transaction output condition """ + regex = re.compile(PUBKEY_REGEX) @@ -16,6 +17,7 @@ class Hash(str): """ Hash in transaction output condition """ + regex = re.compile(HASH_REGEX) @@ -23,27 +25,29 @@ class Int(str): """ Integer in transaction output condition """ + regex = re.compile(r"[0-9]+") # required to type hint cls in classmethod -SIGType = TypeVar('SIGType', bound='SIG') +SIGType = TypeVar("SIGType", bound="SIG") class SIG: """ SIGnature function in transaction output condition """ - grammar = "SIG(", attr('pubkey', Pubkey), ")" - def __init__(self, value: str = '') -> None: + grammar = "SIG(", attr("pubkey", Pubkey), ")" + + def __init__(self, value: str = "") -> None: """ Init SIG instance :param value: Content of the string """ self.value = value - self.pubkey = '' + self.pubkey = "" def __str__(self) -> str: return self.value @@ -54,13 +58,11 @@ class SIG: """ if not isinstance(other, SIG): return NotImplemented - return self.value == other.value and \ - self.pubkey == other.pubkey + return self.value == other.value and self.pubkey == other.pubkey def __hash__(self) -> int: return hash((self.value, self.pubkey)) - @classmethod def token(cls: Type[SIGType], pubkey: str) -> SIGType: """ @@ -73,7 +75,9 @@ class SIG: sig.pubkey = pubkey return sig - def compose(self, parser: Any = None, grammar: Any = None, attr_of: Any = None) -> str: + def compose( + self, parser: Any = None, grammar: Any = None, attr_of: Any = None + ) -> str: """ Return the SIG(pubkey) expression as string format @@ -86,23 +90,24 @@ class SIG: # required to type hint cls in classmethod -CSVType = TypeVar('CSVType', bound='CSV') +CSVType = TypeVar("CSVType", bound="CSV") class CSV: """ CSV function in transaction output condition """ - grammar = "CSV(", attr('time', Int), ")" - def __init__(self, value: str = '') -> None: + grammar = "CSV(", attr("time", Int), ")" + + def __init__(self, value: str = "") -> None: """ Init CSV instance :param value: Content of the string """ self.value = value - self.time = '' + self.time = "" def __str__(self) -> str: return self.value @@ -113,13 +118,11 @@ class CSV: """ if not isinstance(other, CSV): return NotImplemented - return self.value == other.value and \ - self.time == other.time + return self.value == other.value and self.time == other.time def __hash__(self) -> int: return hash((self.value, self.time)) - @classmethod def token(cls: Type[CSVType], time: int) -> CSVType: """ @@ -132,7 +135,9 @@ class CSV: csv.time = str(time) return csv - def compose(self, parser: Any = None, grammar: Any = None, attr_of: str = None) -> str: + def compose( + self, parser: Any = None, grammar: Any = None, attr_of: str = None + ) -> str: """ Return the CSV(time) expression as string format @@ -144,23 +149,24 @@ class CSV: # required to type hint cls in classmethod -CLTVType = TypeVar('CLTVType', bound='CLTV') +CLTVType = TypeVar("CLTVType", bound="CLTV") class CLTV: """ CLTV function in transaction output condition """ - grammar = "CLTV(", attr('timestamp', Int), ")" - def __init__(self, value: str = '') -> None: + grammar = "CLTV(", attr("timestamp", Int), ")" + + def __init__(self, value: str = "") -> None: """ Init CLTV instance :param value: Content of the string """ self.value = value - self.timestamp = '' + self.timestamp = "" def __str__(self) -> str: return self.value @@ -171,13 +177,11 @@ class CLTV: """ if not isinstance(other, CLTV): return NotImplemented - return self.value == other.value and \ - self.timestamp == other.timestamp + return self.value == other.value and self.timestamp == other.timestamp def __hash__(self) -> int: return hash((self.value, self.timestamp)) - @classmethod def token(cls: Type[CLTVType], timestamp: int) -> CLTVType: """ @@ -190,7 +194,9 @@ class CLTV: cltv.timestamp = str(timestamp) return cltv - def compose(self, parser: Any = None, grammar: Any = None, attr_of: str = None) -> str: + def compose( + self, parser: Any = None, grammar: Any = None, attr_of: str = None + ) -> str: """ Return the CLTV(timestamp) expression as string format @@ -202,23 +208,24 @@ class CLTV: # required to type hint cls in classmethod -XHXType = TypeVar('XHXType', bound='XHX') +XHXType = TypeVar("XHXType", bound="XHX") class XHX: """ XHX function in transaction output condition """ - grammar = "XHX(", attr('sha_hash', Hash), ")" - def __init__(self, value: str = '') -> None: + grammar = "XHX(", attr("sha_hash", Hash), ")" + + def __init__(self, value: str = "") -> None: """ Init XHX instance :param value: Content of the string """ self.value = value - self.sha_hash = '' + self.sha_hash = "" def __str__(self) -> str: return self.value @@ -229,13 +236,11 @@ class XHX: """ if not isinstance(other, XHX): return NotImplemented - return self.value == other.value and \ - self.sha_hash == other.sha_hash + return self.value == other.value and self.sha_hash == other.sha_hash def __hash__(self) -> int: return hash((self.value, self.sha_hash)) - @classmethod def token(cls: Type[XHXType], sha_hash: str) -> XHXType: """ @@ -248,7 +253,9 @@ class XHX: xhx.sha_hash = sha_hash return xhx - def compose(self, parser: Any = None, grammar: Any = None, attr_of: str = None) -> str: + def compose( + self, parser: Any = None, grammar: Any = None, attr_of: str = None + ) -> str: """ Return the XHX(sha_hash) expression as string format @@ -260,13 +267,14 @@ class XHX: # required to type hint cls in classmethod -OperatorType = TypeVar('OperatorType', bound='Operator') +OperatorType = TypeVar("OperatorType", bound="Operator") class Operator(Keyword): """ Operator in transaction output condition """ + grammar = Enum(K("&&"), K("||"), K("AND"), K("OR")) regex = re.compile(r"[&&|\|\||\w]+") @@ -281,7 +289,9 @@ class Operator(Keyword): op = cls(keyword) return op - def compose(self, parser: Any = None, grammar: Any = None, attr_of: str = None) -> str: + def compose( + self, parser: Any = None, grammar: Any = None, attr_of: str = None + ) -> str: """ Return the Operator keyword as string format @@ -293,7 +303,7 @@ class Operator(Keyword): # required to type hint cls in classmethod -ConditionType = TypeVar('ConditionType', bound='Condition') +ConditionType = TypeVar("ConditionType", bound="Condition") class Condition: @@ -301,19 +311,19 @@ class Condition: Condition expression in transaction output """ + grammar = None - def __init__(self, value: str = '') -> None: + def __init__(self, value: str = "") -> None: """ Init Condition instance :param value: Content of the condition as string """ self.value = value - self.left = '' - self.right = '' - self.op = '' - + self.left = "" + self.right = "" + self.op = "" def __eq__(self, other: Any) -> bool: """ @@ -321,10 +331,12 @@ class Condition: """ if not isinstance(other, Condition): return NotImplemented - return self.value == other.value and \ - self.left == other.left and \ - self.right == other.right and \ - self.op == other.op + return ( + self.value == other.value + and self.left == other.left + and self.right == other.right + and self.op == other.op + ) def __hash__(self) -> int: return hash((self.value, self.left, self.right, self.op)) @@ -333,8 +345,12 @@ class Condition: return self.value @classmethod - def token(cls: Type[ConditionType], left: Any, op: Optional[Any] = None, - right: Optional[Any] = None) -> ConditionType: + def token( + cls: Type[ConditionType], + left: Any, + op: Optional[Any] = None, + right: Optional[Any] = None, + ) -> ConditionType: """ Return Condition instance from arguments and Operator @@ -360,14 +376,18 @@ class Condition: :param attr_of: Attribute of... """ if type(self.left) is Condition: - left = "({0})".format(parser.compose(self.left, grammar=grammar, attr_of=attr_of)) + left = "({0})".format( + parser.compose(self.left, grammar=grammar, attr_of=attr_of) + ) else: left = parser.compose(self.left, grammar=grammar, attr_of=attr_of) - if getattr(self, 'op', None): + if getattr(self, "op", None): if type(self.right) is Condition: - right = "({0})".format(parser.compose(self.right, grammar=grammar, attr_of=attr_of)) + right = "({0})".format( + parser.compose(self.right, grammar=grammar, attr_of=attr_of) + ) else: right = parser.compose(self.right, grammar=grammar, attr_of=attr_of) op = parser.compose(self.op, grammar=grammar, attr_of=attr_of) @@ -377,6 +397,12 @@ class Condition: return result -Condition.grammar = contiguous(attr('left', [SIG, XHX, CSV, CLTV, ('(', Condition, ')')]), - maybe_some(whitespace, attr('op', Operator), whitespace, - attr('right', [SIG, XHX, CSV, CLTV, ('(', Condition, ')')]))) +Condition.grammar = contiguous( + attr("left", [SIG, XHX, CSV, CLTV, ("(", Condition, ")")]), + maybe_some( + whitespace, + attr("op", Operator), + whitespace, + attr("right", [SIG, XHX, CSV, CLTV, ("(", Condition, ")")]), + ), +) diff --git a/duniterpy/helpers.py b/duniterpy/helpers.py index 0baaa2bba895a905f743ea52cfa7356b7e2f822a..d24ce3952b0ebb9740b5889f5b3fb4da88a7a4c7 100644 --- a/duniterpy/helpers.py +++ b/duniterpy/helpers.py @@ -10,7 +10,7 @@ def ensure_bytes(data: Union[str, bytes]) -> bytes: :rtype bytes: """ if isinstance(data, str): - return bytes(data, 'utf-8') + return bytes(data, "utf-8") return data @@ -23,7 +23,7 @@ def ensure_str(data: Union[str, bytes]) -> str: :rtype str: """ if isinstance(data, bytes): - return str(data, 'utf-8') + return str(data, "utf-8") return data diff --git a/duniterpy/key/ascii_armor.py b/duniterpy/key/ascii_armor.py index 1fdd24e4ff7265343261e133f6c742959d10cdf0..77b87242252e9916806629eef5d90941ae1ff59d 100644 --- a/duniterpy/key/ascii_armor.py +++ b/duniterpy/key/ascii_armor.py @@ -45,14 +45,16 @@ class ParserMissingPublicKeysException(Exception): # Exception messages listed here -PARSER_MISSING_SIGNING_KEY_EXCEPTION = ParserMissingSigningKeyException('The message is encrypted but no SigningKey ' - 'instance is provided') -PARSER_MISSING_PUBLIC_KEYS_EXCEPTION = ParserMissingPublicKeysException('At least one signature but no public keys ' - 'are provided') +PARSER_MISSING_SIGNING_KEY_EXCEPTION = ParserMissingSigningKeyException( + "The message is encrypted but no SigningKey " "instance is provided" +) +PARSER_MISSING_PUBLIC_KEYS_EXCEPTION = ParserMissingPublicKeysException( + "At least one signature but no public keys " "are provided" +) -MISSING_PUBLIC_KEY_AND_SIGNING_KEY_EXCEPTION = MissingPublickeyAndSigningKeyException('Ascii Armor Message needs a ' - 'public key or a SigningKey but ' - 'none are provided') +MISSING_PUBLIC_KEY_AND_SIGNING_KEY_EXCEPTION = MissingPublickeyAndSigningKeyException( + "Ascii Armor Message needs a " "public key or a SigningKey but " "none are provided" +) class AsciiArmor: @@ -61,8 +63,13 @@ class AsciiArmor: """ @staticmethod - def create(message: str, pubkey: Optional[str] = None, signing_keys: Optional[List[SigningKey]] = None, - message_comment: Optional[str] = None, signatures_comment: Optional[str] = None) -> str: + def create( + message: str, + pubkey: Optional[str] = None, + signing_keys: Optional[List[SigningKey]] = None, + message_comment: Optional[str] = None, + signatures_comment: Optional[str] = None, + ) -> str: """ Encrypt a message in ascii armor format, optionally signing it @@ -83,28 +90,38 @@ class AsciiArmor: # create block with headers ascii_armor_block = """{begin_message_header} -""".format(begin_message_header=BEGIN_MESSAGE_HEADER) +""".format( + begin_message_header=BEGIN_MESSAGE_HEADER + ) # if encrypted message... if pubkey: # add encrypted message fields ascii_armor_block += """{version_field} -""".format(version_field=AsciiArmor._get_version_field()) +""".format( + version_field=AsciiArmor._get_version_field() + ) # add message comment if specified if message_comment: ascii_armor_block += """{comment_field} -""".format(comment_field=AsciiArmor._get_comment_field(message_comment)) +""".format( + comment_field=AsciiArmor._get_comment_field(message_comment) + ) # blank line separator - ascii_armor_block += '\n' + ascii_armor_block += "\n" if pubkey: # add encrypted message pubkey_instance = PublicKey(pubkey) - base64_encrypted_message = base64.b64encode(pubkey_instance.encrypt_seal(message)) # type: bytes + base64_encrypted_message = base64.b64encode( + pubkey_instance.encrypt_seal(message) + ) # type: bytes ascii_armor_block += """{base64_encrypted_message} -""".format(base64_encrypted_message=base64_encrypted_message.decode('utf-8')) +""".format( + base64_encrypted_message=base64_encrypted_message.decode("utf-8") + ) else: # remove trailing spaces message = AsciiArmor._remove_trailing_spaces(message) @@ -120,8 +137,9 @@ class AsciiArmor: # add signature blocks and close block on last signature count = 1 for signing_key in signing_keys: - ascii_armor_block += AsciiArmor._get_signature_block(message, signing_key, count == len(signing_keys), - signatures_comment) + ascii_armor_block += AsciiArmor._get_signature_block( + message, signing_key, count == len(signing_keys), signatures_comment + ) count += 1 return ascii_armor_block @@ -167,7 +185,7 @@ class AsciiArmor: :return: """ text = str() - regex_dash_escape_prefix = re.compile('^' + DASH_ESCAPE_PREFIX) + regex_dash_escape_prefix = re.compile("^" + DASH_ESCAPE_PREFIX) # if prefixed by a dash escape prefix... if regex_dash_escape_prefix.match(dash_escaped_line): # remove dash '-' (0x2D) and space ' ' (0x20) prefix @@ -195,8 +213,12 @@ class AsciiArmor: return "Comment: {comment}".format(comment=comment) @staticmethod - def _get_signature_block(message: str, signing_key: SigningKey, close_block: bool = True, - comment: Optional[str] = None) -> str: + def _get_signature_block( + message: str, + signing_key: SigningKey, + close_block: bool = True, + comment: Optional[str] = None, + ) -> str: """ Return a signature block @@ -210,18 +232,25 @@ class AsciiArmor: block = """{begin_signature_header} {version_field} -""".format(begin_signature_header=BEGIN_SIGNATURE_HEADER, version_field=AsciiArmor._get_version_field()) +""".format( + begin_signature_header=BEGIN_SIGNATURE_HEADER, + version_field=AsciiArmor._get_version_field(), + ) # add message comment if specified if comment: block += """{comment_field} -""".format(comment_field=AsciiArmor._get_comment_field(comment)) +""".format( + comment_field=AsciiArmor._get_comment_field(comment) + ) # blank line separator - block += '\n' + block += "\n" block += """{base64_signature} -""".format(base64_signature=base64_signature.decode('utf-8')) +""".format( + base64_signature=base64_signature.decode("utf-8") + ) if close_block: block += END_SIGNATURE_HEADER @@ -229,8 +258,11 @@ class AsciiArmor: return block @staticmethod - def parse(ascii_armor_message: str, signing_key: Optional[SigningKey] = None, - sender_pubkeys: Optional[List[str]] = None) -> dict: + def parse( + ascii_armor_message: str, + signing_key: Optional[SigningKey] = None, + sender_pubkeys: Optional[List[str]] = None, + ) -> dict: """ Return a dict with parsed content (decrypted message, signature validation) :: @@ -265,15 +297,11 @@ class AsciiArmor: # init vars parsed_result = { - 'message': - { - 'fields': {}, - 'content': '', - }, - 'signatures': [] + "message": {"fields": {}, "content": ""}, + "signatures": [], } # type: Dict[str, Any] cursor_status = 0 - message = '' + message = "" signatures_index = 0 # parse each line... @@ -292,11 +320,11 @@ class AsciiArmor: # capture field msg_field_name = m.groups()[0] msg_field_value = m.groups()[1] - parsed_result['message']['fields'][msg_field_name] = msg_field_value + parsed_result["message"]["fields"][msg_field_name] = msg_field_value continue # if blank line... - if line.strip("\n\t\r ") == '': + if line.strip("\n\t\r ") == "": cursor_status = ON_MESSAGE_CONTENT continue @@ -307,7 +335,7 @@ class AsciiArmor: if line.startswith(HEADER_PREFIX): # if field Version is present, the message is encrypted... - if 'Version' in parsed_result['message']['fields']: + if "Version" in parsed_result["message"]["fields"]: # If keypair instance to decrypt not given... if signing_key is None: @@ -318,7 +346,7 @@ class AsciiArmor: message = AsciiArmor._decrypt(message, signing_key) # save message content in result - parsed_result['message']['content'] = message + parsed_result["message"]["content"] = message # if message end header... if regex_end_message.match(line): @@ -328,14 +356,12 @@ class AsciiArmor: # if signature begin header... if regex_begin_signature.match(line): # add signature dict in list - parsed_result['signatures'].append({ - 'fields': {} - }) + parsed_result["signatures"].append({"fields": {}}) cursor_status = ON_SIGNATURE_FIELDS continue else: # if field Version is present, the message is encrypted... - if 'Version' in parsed_result['message']['fields']: + if "Version" in parsed_result["message"]["fields"]: # concatenate encrypted line to message content message += line else: @@ -351,11 +377,13 @@ class AsciiArmor: # capture field sig_field_name = m.groups()[0] sig_field_value = m.groups()[1] - parsed_result['signatures'][signatures_index]['fields'][sig_field_name] = sig_field_value + parsed_result["signatures"][signatures_index]["fields"][ + sig_field_name + ] = sig_field_value continue # if blank line... - if line.strip("\n\t\r ") == '': + if line.strip("\n\t\r ") == "": cursor_status = ON_SIGNATURE_CONTENT continue @@ -381,12 +409,14 @@ class AsciiArmor: for pubkey in sender_pubkeys: verifier = VerifyingKey(pubkey) signature = base64.b64decode(line) - parsed_result['signatures'][signatures_index]['pubkey'] = pubkey + parsed_result["signatures"][signatures_index]["pubkey"] = pubkey try: - libnacl.crypto_sign_verify_detached(signature, message, verifier.vk) - parsed_result['signatures'][signatures_index]['valid'] = True + libnacl.crypto_sign_verify_detached( + signature, message, verifier.vk + ) + parsed_result["signatures"][signatures_index]["valid"] = True except ValueError: - parsed_result['signatures'][signatures_index]['valid'] = False + parsed_result["signatures"][signatures_index]["valid"] = False return parsed_result @@ -401,4 +431,4 @@ class AsciiArmor: """ data = signing_key.decrypt_seal(base64.b64decode(ascii_armor_message)) - return data.decode('utf-8') + return data.decode("utf-8") diff --git a/duniterpy/key/constants.py b/duniterpy/key/constants.py index cc930c0d347b1749a5e97f4dcb9de8a2f0658d86..9c3e3d88dbe04543c799cda87b0ed50f9e58d235 100644 --- a/duniterpy/key/constants.py +++ b/duniterpy/key/constants.py @@ -2,8 +2,4 @@ crypto_sign_BYTES = 64 # Scrypt -SCRYPT_PARAMS = {'N': 4096, - 'r': 16, - 'p': 1, - 'seed_length': 32 - } +SCRYPT_PARAMS = {"N": 4096, "r": 16, "p": 1, "seed_length": 32} diff --git a/duniterpy/key/encryption_key.py b/duniterpy/key/encryption_key.py index 4c534a4e4a12f1d64b4e00ecc374d64abbc25538..376c16ea9b9d3f744538e7b888ea80905b3d42b1 100644 --- a/duniterpy/key/encryption_key.py +++ b/duniterpy/key/encryption_key.py @@ -18,8 +18,12 @@ class SecretKey(libnacl.public.SecretKey): Raw Public Key Encryption Class """ - def __init__(self, salt: Union[str, bytes], password: Union[str, bytes], - scrypt_params: Optional[ScryptParams] = None) -> None: + def __init__( + self, + salt: Union[str, bytes], + password: Union[str, bytes], + scrypt_params: Optional[ScryptParams] = None, + ) -> None: """ Create SecretKey key pair instance from salt and password credentials @@ -32,12 +36,21 @@ class SecretKey(libnacl.public.SecretKey): salt = ensure_bytes(salt) password = ensure_bytes(password) - seed = scrypt(password, salt, scrypt_params.N, scrypt_params.r, scrypt_params.p, scrypt_params.seed_length) + seed = scrypt( + password, + salt, + scrypt_params.N, + scrypt_params.r, + scrypt_params.p, + scrypt_params.seed_length, + ) super().__init__(seed) self.public_key = PublicKey(Base58Encoder.encode(self.pk)) - def encrypt(self, pubkey: str, nonce: Union[str, bytes], text: Union[str, bytes]) -> str: + def encrypt( + self, pubkey: str, nonce: Union[str, bytes], text: Union[str, bytes] + ) -> str: """ Encrypt message text with the public key of the recipient and a nonce @@ -54,7 +67,9 @@ class SecretKey(libnacl.public.SecretKey): text_bytes = ensure_bytes(text) nonce_bytes = ensure_bytes(nonce) recipient_pubkey = PublicKey(pubkey) - crypt_bytes = libnacl.public.Box(self, recipient_pubkey).encrypt(text_bytes, nonce_bytes) + crypt_bytes = libnacl.public.Box(self, recipient_pubkey).encrypt( + text_bytes, nonce_bytes + ) return Base58Encoder.encode(crypt_bytes[24:]) def decrypt(self, pubkey: str, nonce: Union[str, bytes], text: str) -> str: @@ -69,8 +84,10 @@ class SecretKey(libnacl.public.SecretKey): sender_pubkey = PublicKey(pubkey) nonce_bytes = ensure_bytes(nonce) encrypt_bytes = Base58Encoder.decode(text) - decrypt_bytes = libnacl.public.Box(self, sender_pubkey).decrypt(encrypt_bytes, nonce_bytes) - return decrypt_bytes.decode('utf-8') + decrypt_bytes = libnacl.public.Box(self, sender_pubkey).decrypt( + encrypt_bytes, nonce_bytes + ) + return decrypt_bytes.decode("utf-8") class PublicKey(libnacl.public.PublicKey): diff --git a/duniterpy/key/scrypt_params.py b/duniterpy/key/scrypt_params.py index cf3b25dcd0382aedd69ab691a1293235e25af116..e4fb0f96e9f2e31c70b427e1453cafac4760069c 100644 --- a/duniterpy/key/scrypt_params.py +++ b/duniterpy/key/scrypt_params.py @@ -7,9 +7,14 @@ class ScryptParams: """ Class to simplify handling of scrypt parameters """ - def __init__(self, n: Optional[int] = SCRYPT_PARAMS['N'], r: Optional[int] = SCRYPT_PARAMS['r'], - p: Optional[int] = SCRYPT_PARAMS['p'], - seed_length: Optional[int] = SCRYPT_PARAMS['seed_length']) -> None: + + def __init__( + self, + n: Optional[int] = SCRYPT_PARAMS["N"], + r: Optional[int] = SCRYPT_PARAMS["r"], + p: Optional[int] = SCRYPT_PARAMS["p"], + seed_length: Optional[int] = SCRYPT_PARAMS["seed_length"], + ) -> None: """ Init a ScryptParams instance with crypto parameters diff --git a/duniterpy/key/signing_key.py b/duniterpy/key/signing_key.py index 061c1c1ba53cd647433a92187e4940ce7c938add..b7b64149f7f19c389e521f6d0dccbcb00d73a460 100644 --- a/duniterpy/key/signing_key.py +++ b/duniterpy/key/signing_key.py @@ -13,14 +13,18 @@ from pylibscrypt import scrypt from .scrypt_params import ScryptParams from .base58 import Base58Encoder -from ..helpers import ensure_bytes, xor_bytes, convert_seedhex_to_seed, convert_seed_to_seedhex +from ..helpers import ( + ensure_bytes, + xor_bytes, + convert_seedhex_to_seed, + convert_seed_to_seedhex, +) # required to type hint cls in classmethod -SigningKeyType = TypeVar('SigningKeyType', bound='SigningKey') +SigningKeyType = TypeVar("SigningKeyType", bound="SigningKey") class SigningKey(libnacl.sign.Signer): - def __init__(self, seed: bytes) -> None: """ Init pubkey property @@ -31,8 +35,12 @@ class SigningKey(libnacl.sign.Signer): self.pubkey = Base58Encoder.encode(self.vk) @classmethod - def from_credentials(cls: Type[SigningKeyType], salt: Union[str, bytes], password: Union[str, bytes], - scrypt_params: Optional[ScryptParams] = None) -> SigningKeyType: + def from_credentials( + cls: Type[SigningKeyType], + salt: Union[str, bytes], + password: Union[str, bytes], + scrypt_params: Optional[ScryptParams] = None, + ) -> SigningKeyType: """ Create a SigningKey object from credentials @@ -45,7 +53,14 @@ class SigningKey(libnacl.sign.Signer): salt = ensure_bytes(salt) password = ensure_bytes(password) - seed = scrypt(password, salt, scrypt_params.N, scrypt_params.r, scrypt_params.p, scrypt_params.seed_length) + seed = scrypt( + password, + salt, + scrypt_params.N, + scrypt_params.r, + scrypt_params.p, + scrypt_params.seed_length, + ) return cls(seed) @@ -56,7 +71,7 @@ class SigningKey(libnacl.sign.Signer): :param path: Authentication file path """ seedhex = convert_seed_to_seedhex(self.seed) - with open(path, 'w') as fh: + with open(path, "w") as fh: fh.write(seedhex) @staticmethod @@ -66,7 +81,7 @@ class SigningKey(libnacl.sign.Signer): :param str path: Hexadecimal seed file path """ - with open(path, 'r') as fh: + with open(path, "r") as fh: seedhex = fh.read() return SigningKey.from_seedhex(seedhex) @@ -80,7 +95,7 @@ class SigningKey(libnacl.sign.Signer): regex_seedhex = re.compile("([0-9a-fA-F]{64})") match = re.search(regex_seedhex, seedhex) if not match: - raise Exception('Error: Bad seed hexadecimal format') + raise Exception("Error: Bad seed hexadecimal format") seedhex = match.groups()[0] seed = convert_seedhex_to_seed(seedhex) return cls(seed) @@ -115,7 +130,9 @@ class SigningKey(libnacl.sign.Signer): """ curve25519_public_key = libnacl.crypto_sign_ed25519_pk_to_curve25519(self.vk) curve25519_secret_key = libnacl.crypto_sign_ed25519_sk_to_curve25519(self.sk) - return libnacl.crypto_box_seal_open(data, curve25519_public_key, curve25519_secret_key) + return libnacl.crypto_box_seal_open( + data, curve25519_public_key, curve25519_secret_key + ) @classmethod def from_pubsec_file(cls: Type[SigningKeyType], path: str) -> SigningKeyType: @@ -124,7 +141,7 @@ class SigningKey(libnacl.sign.Signer): :param path: Path to WIF file """ - with open(path, 'r') as fh: + with open(path, "r") as fh: pubsec_content = fh.read() # line patterns @@ -134,12 +151,12 @@ class SigningKey(libnacl.sign.Signer): # check public key field match = re.search(regex_pubkey, pubsec_content) if not match: - raise Exception('Error: Bad format PubSec v1 file, missing public key') + raise Exception("Error: Bad format PubSec v1 file, missing public key") # check signkey field match = re.search(regex_signkey, pubsec_content) if not match: - raise Exception('Error: Bad format PubSec v1 file, missing sec key') + raise Exception("Error: Bad format PubSec v1 file, missing sec key") # capture signkey signkey_hex = match.groups()[0] @@ -163,37 +180,45 @@ class SigningKey(libnacl.sign.Signer): base58_public_key = self.pubkey # save file - with open(path, 'w') as fh: + with open(path, "w") as fh: fh.write( """Type: PubSec Version: {version} pub: {pubkey} -sec: {signkey}""".format(version=version, pubkey=base58_public_key, signkey=base58_signing_key) +sec: {signkey}""".format( + version=version, + pubkey=base58_public_key, + signkey=base58_signing_key, + ) ) @staticmethod - def from_wif_or_ewif_file(path: str, password: Optional[str] = None) -> SigningKeyType: + def from_wif_or_ewif_file( + path: str, password: Optional[str] = None + ) -> SigningKeyType: """ Return SigningKey instance from Duniter WIF or EWIF file :param path: Path to WIF of EWIF file :param password: Password needed for EWIF file """ - with open(path, 'r') as fh: + with open(path, "r") as fh: wif_content = fh.read() # check data field - regex = re.compile('Data: ([1-9A-HJ-NP-Za-km-z]+)', re.MULTILINE) + regex = re.compile("Data: ([1-9A-HJ-NP-Za-km-z]+)", re.MULTILINE) match = re.search(regex, wif_content) if not match: - raise Exception('Error: Bad format WIF or EWIF v1 file') + raise Exception("Error: Bad format WIF or EWIF v1 file") # capture hexa wif key wif_hex = match.groups()[0] return SigningKey.from_wif_or_ewif_hex(wif_hex, password) @staticmethod - def from_wif_or_ewif_hex(wif_hex: str, password: Optional[str] = None) -> SigningKeyType: + def from_wif_or_ewif_hex( + wif_hex: str, password: Optional[str] = None + ) -> SigningKeyType: """ Return SigningKey instance from Duniter WIF or EWIF in hexadecimal format @@ -220,14 +245,14 @@ sec: {signkey}""".format(version=version, pubkey=base58_public_key, signkey=base :param path: Path to WIF file """ - with open(path, 'r') as fh: + with open(path, "r") as fh: wif_content = fh.read() # check data field - regex = re.compile('Data: ([1-9A-HJ-NP-Za-km-z]+)', re.MULTILINE) + regex = re.compile("Data: ([1-9A-HJ-NP-Za-km-z]+)", re.MULTILINE) match = re.search(regex, wif_content) if not match: - raise Exception('Error: Bad format WIF v1 file') + raise Exception("Error: Bad format WIF v1 file") # capture hexa wif key wif_hex = match.groups()[0] @@ -281,11 +306,13 @@ sec: {signkey}""".format(version=version, pubkey=base58_public_key, signkey=base # base58 encode key and checksum wif_key = Base58Encoder.encode(seed_fi + checksum) - with open(path, 'w') as fh: + with open(path, "w") as fh: fh.write( """Type: WIF Version: {version} -Data: {data}""".format(version=version, data=wif_key) +Data: {data}""".format( + version=version, data=wif_key + ) ) @staticmethod @@ -296,21 +323,23 @@ Data: {data}""".format(version=version, data=wif_key) :param path: Path to EWIF file :param password: Password of the encrypted seed """ - with open(path, 'r') as fh: + with open(path, "r") as fh: wif_content = fh.read() # check data field - regex = re.compile('Data: ([1-9A-HJ-NP-Za-km-z]+)', re.MULTILINE) + regex = re.compile("Data: ([1-9A-HJ-NP-Za-km-z]+)", re.MULTILINE) match = re.search(regex, wif_content) if not match: - raise Exception('Error: Bad format EWIF v1 file') + raise Exception("Error: Bad format EWIF v1 file") # capture ewif key ewif_hex = match.groups()[0] return SigningKey.from_ewif_hex(ewif_hex, password) @classmethod - def from_ewif_hex(cls: Type[SigningKeyType], ewif_hex: str, password: str) -> SigningKeyType: + def from_ewif_hex( + cls: Type[SigningKeyType], ewif_hex: str, password: str + ) -> SigningKeyType: """ Return SigningKey instance from Duniter EWIF in hexadecimal format @@ -334,7 +363,9 @@ Data: {data}""".format(version=version, data=wif_key) raise Exception("Error: bad format version, not EWIF") # checksum control - checksum = libnacl.crypto_hash_sha256(libnacl.crypto_hash_sha256(ewif_no_checksum))[0:2] + checksum = libnacl.crypto_hash_sha256( + libnacl.crypto_hash_sha256(ewif_no_checksum) + )[0:2] if checksum_from_ewif != checksum: raise Exception("Error: bad checksum of the EWIF") @@ -357,8 +388,8 @@ Data: {data}""".format(version=version, data=wif_key) # Password Control signer = SigningKey(seed) salt_from_seed = libnacl.crypto_hash_sha256( - libnacl.crypto_hash_sha256( - Base58Encoder.decode(signer.pubkey)))[0:4] + libnacl.crypto_hash_sha256(Base58Encoder.decode(signer.pubkey)) + )[0:4] if salt_from_seed != salt: raise Exception("Error: bad Password of EWIF address") @@ -376,8 +407,8 @@ Data: {data}""".format(version=version, data=wif_key) # add version to seed salt = libnacl.crypto_hash_sha256( - libnacl.crypto_hash_sha256( - Base58Encoder.decode(self.pubkey)))[0:4] + libnacl.crypto_hash_sha256(Base58Encoder.decode(self.pubkey)) + )[0:4] # SCRYPT password_bytes = password.encode("utf-8") @@ -387,7 +418,9 @@ Data: {data}""".format(version=version, data=wif_key) # XOR seed1_xor_derivedhalf1_1 = bytes(xor_bytes(self.seed[0:16], derivedhalf1[0:16])) - seed2_xor_derivedhalf1_2 = bytes(xor_bytes(self.seed[16:32], derivedhalf1[16:32])) + seed2_xor_derivedhalf1_2 = bytes( + xor_bytes(self.seed[16:32], derivedhalf1[16:32]) + ) # AES aes = pyaes.AESModeOfOperationECB(derivedhalf2) @@ -395,7 +428,7 @@ Data: {data}""".format(version=version, data=wif_key) encryptedhalf2 = aes.encrypt(seed2_xor_derivedhalf1_2) # add format to final seed (1=WIF,2=EWIF) - seed_bytes = b'\x02' + salt + encryptedhalf1 + encryptedhalf2 + seed_bytes = b"\x02" + salt + encryptedhalf1 + encryptedhalf2 # calculate checksum sha256_v1 = libnacl.crypto_hash_sha256(seed_bytes) @@ -406,9 +439,11 @@ Data: {data}""".format(version=version, data=wif_key) ewif_key = Base58Encoder.encode(seed_bytes + checksum) # save file - with open(path, 'w') as fh: + with open(path, "w") as fh: fh.write( """Type: EWIF Version: {version} -Data: {data}""".format(version=version, data=ewif_key) +Data: {data}""".format( + version=version, data=ewif_key + ) ) diff --git a/duniterpy/key/verifying_key.py b/duniterpy/key/verifying_key.py index 60e8a43666eb1797184d20a9325c781c33a42cea..87a189c2b2acedb2b81fe463dbba66f99b94045f 100644 --- a/duniterpy/key/verifying_key.py +++ b/duniterpy/key/verifying_key.py @@ -34,7 +34,7 @@ class VerifyingKey(libnacl.sign.Verifier): :return: """ signature = base64.b64decode(document.signatures[0]) - prepended = signature + bytes(document.raw(), 'ascii') + prepended = signature + bytes(document.raw(), "ascii") try: self.verify(prepended) @@ -50,7 +50,7 @@ class VerifyingKey(libnacl.sign.Verifier): """ signature = base64.b64decode(head.signature) inline = head.inline() - prepended = signature + bytes(inline, 'ascii') + prepended = signature + bytes(inline, "ascii") try: self.verify(prepended)