Skip to content
Snippets Groups Projects
Commit 43d71a24 authored by Moul's avatar Moul
Browse files

[mod] #173: Make Transaction handy with single and multi-sign

Store SigningKey and Signature arguments/attributes in Lists.
Allow to pass an instance or a list of SigningKey and pubkey
for single and multi signing
in Transaction.{__init__(),multi_sign(),check_signatures()}
if instance: store in a list

Overwrite Document.{sign(),check_signature()} as undefined.
Disable pylint error at Transaction class scope:
https://stackoverflow.com/a/22224042
Because of NotImplementedError exception, not happening with Exception

Do not erase previous signatures when signing
To allow different key owner to sign additonally the Tx doc
parent 13527de4
No related branches found
No related tags found
No related merge requests found
Pipeline #13054 passed
...@@ -467,6 +467,7 @@ class Unlock: ...@@ -467,6 +467,7 @@ class Unlock:
TransactionType = TypeVar("TransactionType", bound="Transaction") TransactionType = TypeVar("TransactionType", bound="Transaction")
# pylint: disable=W0223
class Transaction(Document): class Transaction(Document):
""" """
.. note:: A transaction document is specified by the following format : .. note:: A transaction document is specified by the following format :
...@@ -549,7 +550,7 @@ class Transaction(Document): ...@@ -549,7 +550,7 @@ class Transaction(Document):
outputs: List[OutputSource], outputs: List[OutputSource],
comment: str, comment: str,
time: Optional[int] = None, time: Optional[int] = None,
signing_key: SigningKey = None, signing_keys: Optional[Union[SigningKey, List[SigningKey]]] = None,
version: int = VERSION, version: int = VERSION,
currency: str = G1_CURRENCY_CODENAME, currency: str = G1_CURRENCY_CODENAME,
) -> None: ) -> None:
...@@ -564,7 +565,7 @@ class Transaction(Document): ...@@ -564,7 +565,7 @@ class Transaction(Document):
:param outputs: List of OutputSource instances :param outputs: List of OutputSource instances
:param comment: Comment field :param comment: Comment field
:param time: time when the transaction enters the blockchain :param time: time when the transaction enters the blockchain
:param signing_key: SigningKey instance to sign the document (default=None) :param signing_keys: SigningKey or list of SigningKey instances to sign the document (default=None)
:param version: Document version (default=transaction.VERSION) :param version: Document version (default=transaction.VERSION)
:param currency: Currency codename (default=constants.CURRENCY_CODENAME_G1) :param currency: Currency codename (default=constants.CURRENCY_CODENAME_G1)
""" """
...@@ -579,8 +580,8 @@ class Transaction(Document): ...@@ -579,8 +580,8 @@ class Transaction(Document):
self.time = time self.time = time
self.signatures: List[str] = list() self.signatures: List[str] = list()
if signing_key is not None: if signing_keys:
self.sign(signing_key) self.multi_sign(signing_keys)
def __eq__(self, other: Any) -> bool: def __eq__(self, other: Any) -> bool:
""" """
...@@ -591,7 +592,6 @@ class Transaction(Document): ...@@ -591,7 +592,6 @@ class Transaction(Document):
return ( return (
self.version == other.version self.version == other.version
and self.currency == other.currency and self.currency == other.currency
and self.signature == other.signature
and self.signatures == other.signatures and self.signatures == other.signatures
and self.blockstamp == other.blockstamp and self.blockstamp == other.blockstamp
and self.locktime == other.locktime and self.locktime == other.locktime
...@@ -608,7 +608,6 @@ class Transaction(Document): ...@@ -608,7 +608,6 @@ class Transaction(Document):
( (
self.version, self.version,
self.currency, self.currency,
self.signature,
self.signatures, self.signatures,
self.blockstamp, self.blockstamp,
self.locktime, self.locktime,
...@@ -744,10 +743,7 @@ Comment: {comment} ...@@ -744,10 +743,7 @@ Comment: {comment}
) )
# return transaction with signatures # return transaction with signatures
if len(signatures) > 1:
transaction.signatures = signatures transaction.signatures = signatures
else:
transaction.signature = signatures[0]
return transaction return transaction
@classmethod @classmethod
...@@ -837,11 +833,7 @@ Comment: {comment} ...@@ -837,11 +833,7 @@ Comment: {comment}
) )
# return transaction with signatures # return transaction with signatures
if len(signatures) > 1:
transaction.signatures = signatures transaction.signatures = signatures
else:
transaction.signature = signatures[0]
return transaction return transaction
def raw(self) -> str: def raw(self) -> str:
...@@ -917,9 +909,6 @@ Currency: {1} ...@@ -917,9 +909,6 @@ Currency: {1}
doc += "{0}\n".format(o.inline()) doc += "{0}\n".format(o.inline())
if self.comment != "": if self.comment != "":
doc += "{0}\n".format(self.comment) doc += "{0}\n".format(self.comment)
if self.signature is not None:
doc += "{0}\n".format(self.signature)
else:
for signature in self.signatures: for signature in self.signatures:
doc += "{0}\n".format(signature) doc += "{0}\n".format(signature)
...@@ -931,44 +920,51 @@ Currency: {1} ...@@ -931,44 +920,51 @@ Currency: {1}
:return: :return:
""" """
if self.signature is None and len(self.signatures) == 0: if not self.signatures:
raise MalformedDocumentError("No signature, can not create raw format") raise MalformedDocumentError("No signature, can not create raw format")
raw = self.raw()
if self.signature is not None: signed_raw = self.raw()
signed_raw = raw + self.signature + "\n"
else:
signed_raw = raw
for signature in self.signatures: for signature in self.signatures:
signed_raw += "{0}\n".format(signature) signed_raw += f"{signature}\n"
return signed_raw return signed_raw
def multi_sign(self, keys: List[SigningKey]) -> None: def sign(self, key: SigningKey) -> None:
raise NotImplementedError("sign() is not implemented, use multi_sign()")
def multi_sign(self, keys: Union[SigningKey, List[SigningKey]]) -> None:
""" """
Sign the current document with multiple keys Sign the current document with multiple keys
Warning : current signatures will be replaced with the new ones. :param keys: Libnacl key or list of them
:param keys: List of libnacl keys instance
""" """
self.signatures = list() if isinstance(keys, SigningKey):
keys = [keys]
for key in keys: for key in keys:
signature = base64.b64encode(key.signature(bytes(self.raw(), "ascii"))) signature = base64.b64encode(key.signature(bytes(self.raw(), "ascii")))
logging.debug("Signature : \n%s", signature.decode("ascii")) logging.debug("Signature : \n%s", signature.decode("ascii"))
self.signatures.append(signature.decode("ascii")) self.signatures.append(signature.decode("ascii"))
def check_signatures(self, pubkeys: List[str]): def check_signature(self, pubkey: str):
raise NotImplementedError(
"check_signature() is not implemented, use check_signatures()"
)
def check_signatures(self, pubkeys: Union[str, List[str]]):
""" """
Check if the signatures matches the pubkeys Check if the signatures matches the pubkeys
:param pubkeys: List of Base58 public keys :param pubkeys: Base58 public key or list of them
:return: :return:
""" """
if len(self.signatures) == 0: if not self.signatures:
raise Exception("No signatures, can not check signatures") raise Exception("No signatures, can not check signatures")
if isinstance(pubkeys, str):
pubkeys = [pubkeys]
if len(self.signatures) != len(pubkeys): if len(self.signatures) != len(pubkeys):
raise Exception("Number of pubkeys not equal to number of signatures") raise Exception("Number of pubkeys not equal to number of signatures")
...@@ -998,7 +994,7 @@ class SimpleTransaction(Transaction): ...@@ -998,7 +994,7 @@ class SimpleTransaction(Transaction):
outputs: List[OutputSource], outputs: List[OutputSource],
comment: str, comment: str,
time: int = 0, time: int = 0,
signing_key: SigningKey = None, signing_keys: Optional[Union[SigningKey, List[SigningKey]]] = None,
version: int = VERSION, version: int = VERSION,
currency: str = G1_CURRENCY_CODENAME, currency: str = G1_CURRENCY_CODENAME,
) -> None: ) -> None:
...@@ -1013,7 +1009,7 @@ class SimpleTransaction(Transaction): ...@@ -1013,7 +1009,7 @@ class SimpleTransaction(Transaction):
:param outputs: List of OutputSource instances :param outputs: List of OutputSource instances
:param comment: Comment field :param comment: Comment field
:param time: time when the transaction enters the blockchain (default=0) :param time: time when the transaction enters the blockchain (default=0)
:param signing_key: SigningKey instance to sign the document (default=None) :param signing_keys: SigningKey instance to sign the document (default=None)
:param version: Document version (default=transaction.VERSION) :param version: Document version (default=transaction.VERSION)
:param currency: Currency codename (default=constants.CURRENCY_CODENAME_G1) :param currency: Currency codename (default=constants.CURRENCY_CODENAME_G1)
""" """
...@@ -1026,7 +1022,7 @@ class SimpleTransaction(Transaction): ...@@ -1026,7 +1022,7 @@ class SimpleTransaction(Transaction):
outputs, outputs,
comment, comment,
time=time, time=time,
signing_key=signing_key, signing_keys=signing_keys,
version=version, version=version,
currency=currency, currency=currency,
) )
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
import getpass import getpass
import urllib import urllib
from typing import List, Union
from duniterpy.api import bma from duniterpy.api import bma
from duniterpy.api.client import Client from duniterpy.api.client import Client
...@@ -42,7 +43,7 @@ def get_transaction_document( ...@@ -42,7 +43,7 @@ def get_transaction_document(
source: dict, source: dict,
from_pubkey: str, from_pubkey: str,
to_pubkey: str, to_pubkey: str,
signing_key: SigningKey, signing_keys: Union[SigningKey, List[SigningKey]],
) -> Transaction: ) -> Transaction:
""" """
Return a Transaction document Return a Transaction document
...@@ -96,7 +97,7 @@ def get_transaction_document( ...@@ -96,7 +97,7 @@ def get_transaction_document(
unlocks=unlocks, unlocks=unlocks,
outputs=outputs, outputs=outputs,
comment="", comment="",
signing_key=signing_key, signing_keys=signing_keys,
currency=current_block["currency"], currency=current_block["currency"],
) )
......
...@@ -147,4 +147,4 @@ Solde huile Millepertuis ...@@ -147,4 +147,4 @@ Solde huile Millepertuis
rgjOmzFH5h+hkDbJLk1b88X7Z83HMgTa5rBckeMSdF/yZtItN3zMn09MphcXjffdrKcK+MebwoisLJqV+jXrDg== rgjOmzFH5h+hkDbJLk1b88X7Z83HMgTa5rBckeMSdF/yZtItN3zMn09MphcXjffdrKcK+MebwoisLJqV+jXrDg==
""" """
tx = Transaction.from_compact(transaction_document, "g1") tx = Transaction.from_compact(transaction_document, "g1")
self.assertTrue(tx.check_signature(tx.issuers[0])) self.assertTrue(tx.check_signatures(tx.issuers))
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment