Skip to content
Snippets Groups Projects
Commit 717b63fb 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:
Raise Exception instead of NotImplentedError to not having to disable pylint:W0223
https://stackoverflow.com/a/22224042

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
2 merge requests!157v1.0.0rc0: merge dev into master,!150#173: Make Transaction multi-sign by default, and add Tx.check_signatures() test on multi-sig tx
Pipeline #13076 waiting for manual action
......@@ -549,7 +549,7 @@ class Transaction(Document):
outputs: List[OutputSource],
comment: str,
time: Optional[int] = None,
signing_key: SigningKey = None,
signing_keys: Optional[Union[SigningKey, List[SigningKey]]] = None,
version: int = VERSION,
currency: str = G1_CURRENCY_CODENAME,
) -> None:
......@@ -564,7 +564,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)
:param signing_keys: SigningKey or list of SigningKey instances to sign the document (default=None)
:param version: Document version (default=transaction.VERSION)
:param currency: Currency codename (default=constants.CURRENCY_CODENAME_G1)
"""
......@@ -579,8 +579,8 @@ class Transaction(Document):
self.time = time
self.signatures: List[str] = list()
if signing_key is not None:
self.sign(signing_key)
if signing_keys:
self.multi_sign(signing_keys)
def __eq__(self, other: Any) -> bool:
"""
......@@ -591,7 +591,6 @@ class Transaction(Document):
return (
self.version == other.version
and self.currency == other.currency
and self.signature == other.signature
and self.signatures == other.signatures
and self.blockstamp == other.blockstamp
and self.locktime == other.locktime
......@@ -608,7 +607,6 @@ class Transaction(Document):
(
self.version,
self.currency,
self.signature,
self.signatures,
self.blockstamp,
self.locktime,
......@@ -744,10 +742,7 @@ Comment: {comment}
)
# return transaction with signatures
if len(signatures) > 1:
transaction.signatures = signatures
else:
transaction.signature = signatures[0]
return transaction
@classmethod
......@@ -837,11 +832,7 @@ Comment: {comment}
)
# return transaction with signatures
if len(signatures) > 1:
transaction.signatures = signatures
else:
transaction.signature = signatures[0]
return transaction
def raw(self) -> str:
......@@ -917,9 +908,6 @@ Currency: {1}
doc += "{0}\n".format(o.inline())
if 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:
doc += "{0}\n".format(signature)
......@@ -931,44 +919,49 @@ Currency: {1}
:return:
"""
if self.signature is None and len(self.signatures) == 0:
if not self.signatures:
raise MalformedDocumentError("No signature, can not create raw format")
raw = self.raw()
if self.signature is not None:
signed_raw = raw + self.signature + "\n"
else:
signed_raw = raw
signed_raw = self.raw()
for signature in self.signatures:
signed_raw += "{0}\n".format(signature)
signed_raw += f"{signature}\n"
return signed_raw
def multi_sign(self, keys: List[SigningKey]) -> None:
def sign(self, key: SigningKey) -> None:
raise Exception("sign() is not implemented, use multi_sign()")
def multi_sign(self, keys: Union[SigningKey, List[SigningKey]]) -> None:
"""
Sign the current document with multiple keys
Warning : current signatures will be replaced with the new ones.
:param keys: List of libnacl keys instance
:param keys: Libnacl key or list of them
"""
self.signatures = list()
if isinstance(keys, SigningKey):
keys = [keys]
for key in keys:
signature = base64.b64encode(key.signature(bytes(self.raw(), "ascii")))
logging.debug("Signature : \n%s", signature.decode("ascii"))
self.signatures.append(signature.decode("ascii"))
def check_signatures(self, pubkeys: List[str]):
def check_signature(self, pubkey: str):
raise Exception("check_signature() is not implemented, use check_signatures()")
def check_signatures(self, pubkeys: Union[str, List[str]]):
"""
Check if the signatures matches the pubkeys
:param pubkeys: List of Base58 public keys
:param pubkeys: Base58 public key or list of them
:return:
"""
if len(self.signatures) == 0:
if not self.signatures:
raise Exception("No signatures, can not check signatures")
if isinstance(pubkeys, str):
pubkeys = [pubkeys]
if len(self.signatures) != len(pubkeys):
raise Exception("Number of pubkeys not equal to number of signatures")
......@@ -998,7 +991,7 @@ class SimpleTransaction(Transaction):
outputs: List[OutputSource],
comment: str,
time: int = 0,
signing_key: SigningKey = None,
signing_keys: Optional[Union[SigningKey, List[SigningKey]]] = None,
version: int = VERSION,
currency: str = G1_CURRENCY_CODENAME,
) -> None:
......@@ -1013,7 +1006,7 @@ class SimpleTransaction(Transaction):
:param outputs: List of OutputSource instances
:param comment: Comment field
: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 currency: Currency codename (default=constants.CURRENCY_CODENAME_G1)
"""
......@@ -1026,7 +1019,7 @@ class SimpleTransaction(Transaction):
outputs,
comment,
time=time,
signing_key=signing_key,
signing_keys=signing_keys,
version=version,
currency=currency,
)
......
......@@ -15,6 +15,7 @@
import getpass
import urllib
from typing import List, Union
from duniterpy.api import bma
from duniterpy.api.client import Client
......@@ -42,7 +43,7 @@ def get_transaction_document(
source: dict,
from_pubkey: str,
to_pubkey: str,
signing_key: SigningKey,
signing_keys: Union[SigningKey, List[SigningKey]],
) -> Transaction:
"""
Return a Transaction document
......@@ -96,7 +97,7 @@ def get_transaction_document(
unlocks=unlocks,
outputs=outputs,
comment="",
signing_key=signing_key,
signing_keys=signing_keys,
currency=current_block["currency"],
)
......
......@@ -147,4 +147,4 @@ Solde huile Millepertuis
rgjOmzFH5h+hkDbJLk1b88X7Z83HMgTa5rBckeMSdF/yZtItN3zMn09MphcXjffdrKcK+MebwoisLJqV+jXrDg==
"""
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