From d678f777d1b51c255c4d61ba9e7cace282e153a5 Mon Sep 17 00:00:00 2001 From: Vincent Texier <vit@free.fr> Date: Thu, 17 Jun 2021 20:00:19 +0200 Subject: [PATCH] [enh] #95 add optional signing_key argument in Document sub-classes argument is Optional[SigningKey] with default=None --- duniterpy/documents/block.py | 5 +++++ duniterpy/documents/certification.py | 5 +++++ duniterpy/documents/identity.py | 16 ++++++++++++++-- duniterpy/documents/membership.py | 9 ++++++++- duniterpy/documents/peer.py | 9 ++++++++- duniterpy/documents/revocation.py | 10 +++++++++- duniterpy/documents/transaction.py | 5 +++++ examples/save_revoke_document.py | 7 +++---- examples/send_certification.py | 17 +++++++---------- examples/send_identity.py | 4 +--- examples/send_membership.py | 4 +--- examples/send_transaction.py | 13 ++++++++----- 12 files changed, 74 insertions(+), 30 deletions(-) diff --git a/duniterpy/documents/block.py b/duniterpy/documents/block.py index c6252fcb..2efc7cff 100644 --- a/duniterpy/documents/block.py +++ b/duniterpy/documents/block.py @@ -176,6 +176,7 @@ class Block(Document): transactions: List[Transaction], inner_hash: str, nonce: int, + signing_key: SigningKey = None, ) -> None: """ Constructor @@ -206,6 +207,7 @@ class Block(Document): :param transactions: transactions documents :param inner_hash: the block hash :param nonce: the nonce value of the block + :param signing_key: SigningKey instance to sign the document (default=None) """ super().__init__(version, currency) @@ -247,6 +249,9 @@ class Block(Document): self.inner_hash = inner_hash self.nonce = nonce + if signing_key is not None: + self.sign(signing_key) + @property def blockUID(self) -> BlockUID: """ diff --git a/duniterpy/documents/certification.py b/duniterpy/documents/certification.py index b878e2c5..3a027d02 100644 --- a/duniterpy/documents/certification.py +++ b/duniterpy/documents/certification.py @@ -63,6 +63,7 @@ class Certification(Document): pubkey_from: str, identity: Union[Identity, str], timestamp: BlockUID, + signing_key: SigningKey = None, ) -> None: """ Constructor @@ -72,6 +73,7 @@ class Certification(Document): :param pubkey_from: Pubkey of the certifier :param identity: Document instance of the certified identity or identity pubkey string :param timestamp: the blockuid + :param signing_key: SigningKey instance to sign the document (default=None) """ super().__init__(version, currency) self.pubkey_from = pubkey_from @@ -79,6 +81,9 @@ class Certification(Document): self.pubkey_to = identity.pubkey if isinstance(identity, Identity) else identity self.timestamp = timestamp + if signing_key is not None: + self.sign(signing_key) + @classmethod def from_signed_raw( cls: Type[CertificationType], signed_raw: str diff --git a/duniterpy/documents/identity.py b/duniterpy/documents/identity.py index 0e51d331..7d996f94 100644 --- a/duniterpy/documents/identity.py +++ b/duniterpy/documents/identity.py @@ -17,10 +17,12 @@ import re from typing import Type, TypeVar from ..constants import BLOCK_UID_REGEX, PUBKEY_REGEX, SIGNATURE_REGEX, UID_REGEX + +# required to type hint cls in classmethod +from ..key import SigningKey from .block_uid import BlockUID from .document import Document, MalformedDocumentError -# required to type hint cls in classmethod IdentityType = TypeVar("IdentityType", bound="Identity") @@ -77,7 +79,13 @@ class Identity(Document): } def __init__( - self, version: int, currency: str, pubkey: str, uid: str, timestamp: BlockUID + self, + version: int, + currency: str, + pubkey: str, + uid: str, + timestamp: BlockUID, + signing_key: SigningKey = None, ) -> None: """ Create an identity document @@ -87,6 +95,7 @@ class Identity(Document): :param pubkey: Public key of the account linked to the identity :param uid: Unique identifier :param timestamp: BlockUID instance + :param signing_key: SigningKey instance to sign the document (default=None) """ super().__init__(version, currency) @@ -94,6 +103,9 @@ class Identity(Document): self.timestamp = timestamp self.uid = uid + if signing_key is not None: + self.sign(signing_key) + @classmethod def from_inline( cls: Type[IdentityType], version: int, currency: str, inline: str diff --git a/duniterpy/documents/membership.py b/duniterpy/documents/membership.py index fe4ebe82..0bfb9071 100644 --- a/duniterpy/documents/membership.py +++ b/duniterpy/documents/membership.py @@ -17,10 +17,12 @@ import re from typing import Type, TypeVar from ..constants import BLOCK_UID_REGEX, PUBKEY_REGEX, SIGNATURE_REGEX + +# required to type hint cls in classmethod +from ..key import SigningKey from .block_uid import BlockUID from .document import Document, MalformedDocumentError -# required to type hint cls in classmethod MembershipType = TypeVar("MembershipType", bound="Membership") @@ -82,6 +84,7 @@ class Membership(Document): membership_type: str, uid: str, identity_ts: BlockUID, + signing_key: SigningKey = None, ) -> None: """ Create a membership document @@ -93,6 +96,7 @@ class Membership(Document): :param membership_type: "IN" or "OUT" to enter or quit the community :param uid: Unique identifier of the identity :param identity_ts: BlockUID of the identity + :param signing_key: SigningKey instance to sign the document (default=None) """ super().__init__(version, currency) @@ -102,6 +106,9 @@ class Membership(Document): self.uid = uid self.identity_ts = identity_ts + if signing_key is not None: + self.sign(signing_key) + @classmethod def from_inline( cls: Type[MembershipType], diff --git a/duniterpy/documents/peer.py b/duniterpy/documents/peer.py index 3189b154..a7c0ce62 100644 --- a/duniterpy/documents/peer.py +++ b/duniterpy/documents/peer.py @@ -19,10 +19,12 @@ from typing import List, Type, TypeVar from duniterpy.api.endpoint import Endpoint, endpoint from ..constants import BLOCK_HASH_REGEX, PUBKEY_REGEX + +# required to type hint cls in classmethod +from ..key import SigningKey from .block_uid import BlockUID from .document import Document, MalformedDocumentError -# required to type hint cls in classmethod PeerType = TypeVar("PeerType", bound="Peer") @@ -69,6 +71,7 @@ class Peer(Document): pubkey: str, block_uid: BlockUID, endpoints: List[Endpoint], + signing_key: SigningKey = None, ) -> None: """ Init Peer instance @@ -78,6 +81,7 @@ class Peer(Document): :param pubkey: Public key of the issuer :param block_uid: BlockUID instance timestamp :param endpoints: List of endpoints string + :param signing_key: SigningKey instance to sign the document (default=None) """ super().__init__(version, currency) @@ -85,6 +89,9 @@ class Peer(Document): self.blockUID = block_uid self.endpoints: List[Endpoint] = endpoints + if signing_key is not None: + self.sign(signing_key) + @classmethod def from_signed_raw(cls: Type[PeerType], raw: str) -> PeerType: """ diff --git a/duniterpy/documents/revocation.py b/duniterpy/documents/revocation.py index 789a147e..1e030941 100644 --- a/duniterpy/documents/revocation.py +++ b/duniterpy/documents/revocation.py @@ -61,7 +61,11 @@ class Revocation(Document): } def __init__( - self, version: int, currency: str, identity: Union[Identity, str] + self, + version: int, + currency: str, + identity: Union[Identity, str], + signing_key: SigningKey = None, ) -> None: """ Init Revocation instance @@ -69,12 +73,16 @@ class Revocation(Document): :param version: Version number :param currency: Name of the currency :param identity: Identity instance or identity pubkey + :param signing_key: SigningKey instance to sign the document (default=None) """ super().__init__(version, currency) self.identity = identity if isinstance(identity, Identity) else None self.pubkey = identity.pubkey if isinstance(identity, Identity) else identity + if signing_key is not None: + self.sign(signing_key) + @classmethod def from_inline( cls: Type[RevocationType], version: int, currency: str, inline: str diff --git a/duniterpy/documents/transaction.py b/duniterpy/documents/transaction.py index f837e4b9..6be78625 100644 --- a/duniterpy/documents/transaction.py +++ b/duniterpy/documents/transaction.py @@ -548,6 +548,7 @@ class Transaction(Document): outputs: List[OutputSource], comment: str, time: Optional[int] = None, + signing_key: SigningKey = None, ) -> None: """ Init Transaction instance @@ -562,6 +563,7 @@ class Transaction(Document): :param outputs: List of OutputSource instances :param comment: Comment field :param time: time when the transaction enters the blockchain + :param signing_key: SigningKey instance to sign the document (default=None) """ super().__init__(version, currency) self.blockstamp = blockstamp @@ -574,6 +576,9 @@ class Transaction(Document): self.time = time self.signatures: List[str] = list() + if signing_key is not None: + self.sign(signing_key) + def __eq__(self, other: Any) -> bool: """ Check Transaction instances equality diff --git a/examples/save_revoke_document.py b/examples/save_revoke_document.py index c67a478b..33b27450 100644 --- a/examples/save_revoke_document.py +++ b/examples/save_revoke_document.py @@ -84,10 +84,9 @@ def get_signed_raw_revocation_document( :rtype: str """ - revocation = Revocation(PROTOCOL_VERSION, identity.currency, identity) - key = SigningKey.from_credentials(salt, password) - revocation.sign(key) + revocation = Revocation(PROTOCOL_VERSION, identity.currency, identity, key) + return revocation.signed_raw() @@ -122,7 +121,7 @@ def save_revoke_document(): # capture current block to get currency name current_block = client(bma.blockchain.current) - # create our Identity document to sign the Certification document + # create our Identity document to sign the revocation document identity = get_identity_document(client, current_block, pubkey) if identity is None: print("Identity not found for pubkey {0}".format(pubkey)) diff --git a/examples/send_certification.py b/examples/send_certification.py index 81d19ec1..2d20578e 100644 --- a/examples/send_certification.py +++ b/examples/send_certification.py @@ -57,14 +57,14 @@ def get_identity_document( def get_certification_document( - current_block: dict, self_cert_document: Identity, from_pubkey: str + current_block: dict, identity: Identity, signing_key: SigningKey ) -> Certification: """ Create and return a Certification document :param current_block: Current block data - :param self_cert_document: Identity document - :param from_pubkey: Pubkey of the certifier + :param identity: Identity document instance + :param signing_key: Signing key of the certifier :rtype: Certification """ @@ -72,9 +72,10 @@ def get_certification_document( return Certification( version=10, currency=current_block["currency"], - pubkey_from=from_pubkey, - identity=self_cert_document, + pubkey_from=signing_key.pubkey, + identity=identity, timestamp=BlockUID(current_block["number"], current_block["hash"]), + signing_key=signing_key, ) @@ -97,7 +98,6 @@ def send_certification(): # create key from credentials key = SigningKey.from_credentials(salt, password) - pubkey_from = key.pubkey # prompt entry pubkey_to = input("Enter pubkey to certify: ") @@ -113,10 +113,7 @@ def send_certification(): return # send the Certification document to the node - certification = get_certification_document(current_block, identity, pubkey_from) - - # sign document - certification.sign([key]) + certification = get_certification_document(current_block, identity, key) # Here we request for the path wot/certify try: diff --git a/examples/send_identity.py b/examples/send_identity.py index cefcecc5..4b58bfc8 100644 --- a/examples/send_identity.py +++ b/examples/send_identity.py @@ -57,11 +57,9 @@ def get_identity_document( pubkey=key.pubkey, uid=uid, timestamp=timestamp, + signing_key=key, ) - # sign document - identity.sign(key) - return identity diff --git a/examples/send_membership.py b/examples/send_membership.py index 3d49beb9..e47526cd 100644 --- a/examples/send_membership.py +++ b/examples/send_membership.py @@ -65,11 +65,9 @@ def get_membership_document( membership_type=membership_type, uid=uid, identity_ts=identity_timestamp, + signing_key=key, ) - # sign document - membership.sign(key) - return membership diff --git a/examples/send_transaction.py b/examples/send_transaction.py index 983c17a2..537ba3fa 100644 --- a/examples/send_transaction.py +++ b/examples/send_transaction.py @@ -42,7 +42,11 @@ TRANSACTION_VERSION = 10 def get_transaction_document( - current_block: dict, source: dict, from_pubkey: str, to_pubkey: str + current_block: dict, + source: dict, + from_pubkey: str, + to_pubkey: str, + signing_key: SigningKey, ) -> Transaction: """ Return a Transaction document @@ -51,6 +55,7 @@ def get_transaction_document( :param source: Source to send :param from_pubkey: Public key of the issuer :param to_pubkey: Public key of the receiver + :param signing_key: Signing key of the issuer :return: Transaction """ @@ -97,6 +102,7 @@ def get_transaction_document( unlocks=unlocks, outputs=outputs, comment="", + signing_key=signing_key, ) return transaction @@ -141,12 +147,9 @@ def send_transaction(): # create the transaction document transaction = get_transaction_document( - current_block, source, pubkey_from, pubkey_to + current_block, source, pubkey_from, pubkey_to, key ) - # sign document - transaction.sign([key]) - # send the Transaction document to the node try: client(bma.tx.process, transaction.signed_raw()) -- GitLab