Skip to content
Snippets Groups Projects
Commit 8bd0c7c9 authored by Moul's avatar Moul
Browse files

[mod] #173: Make Transaction multi-sign by default

Store SigningKey and Signature arguments/attributes in Lists.
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
parent 53affdfe
No related branches found
No related tags found
No related merge requests found
...@@ -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[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 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)
""" """
...@@ -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 is not None:
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,25 +920,22 @@ Currency: {1} ...@@ -931,25 +920,22 @@ 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 sign(self, key: SigningKey) -> None:
raise NotImplementedError("sign() is not implemented, use multi_sign([key])")
def multi_sign(self, keys: List[SigningKey]) -> None: def multi_sign(self, keys: 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: List of libnacl keys instance :param keys: List of libnacl keys instance
""" """
self.signatures = list() self.signatures = list()
...@@ -958,6 +944,11 @@ Currency: {1} ...@@ -958,6 +944,11 @@ Currency: {1}
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_signature(self, pubkey: str):
raise NotImplementedError(
"check_signature() is not implemented, use check_signatures([pubkey])"
)
def check_signatures(self, pubkeys: List[str]): def check_signatures(self, pubkeys: List[str]):
""" """
Check if the signatures matches the pubkeys Check if the signatures matches the pubkeys
...@@ -966,7 +957,7 @@ Currency: {1} ...@@ -966,7 +957,7 @@ Currency: {1}
: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 len(self.signatures) != len(pubkeys): if len(self.signatures) != len(pubkeys):
...@@ -1000,7 +991,7 @@ class SimpleTransaction(Transaction): ...@@ -1000,7 +991,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[List[SigningKey]] = None,
version: int = VERSION, version: int = VERSION,
currency: str = G1_CURRENCY_CODENAME, currency: str = G1_CURRENCY_CODENAME,
) -> None: ) -> None:
...@@ -1015,7 +1006,7 @@ class SimpleTransaction(Transaction): ...@@ -1015,7 +1006,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)
""" """
...@@ -1028,7 +1019,7 @@ class SimpleTransaction(Transaction): ...@@ -1028,7 +1019,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
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: 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"],
) )
...@@ -142,7 +143,7 @@ def send_transaction(): ...@@ -142,7 +143,7 @@ def send_transaction():
# create the transaction document # create the transaction document
transaction = get_transaction_document( transaction = get_transaction_document(
current_block, source, pubkey_from, pubkey_to, key current_block, source, pubkey_from, pubkey_to, [key]
) )
# send the Transaction document to the node # send the Transaction document to the node
......
...@@ -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