Skip to content

Commit

Permalink
added delegated signing (#6)
Browse files Browse the repository at this point in the history
Signed-off-by: Jürgen Eckel <juergen@riddleandcode.com>
  • Loading branch information
eckelj authored Dec 2, 2022
1 parent eaabb03 commit 648d0e7
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 3 deletions.
114 changes: 113 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "planetmint-transactions"
version = "0.2.2"
version = "0.3.0"
description = "Python implementation of the planetmint transactions spec"
authors = ["Lorenz Herzberger <lorenzherzberger@gmail.com>"]
readme = "README.md"
Expand All @@ -23,6 +23,10 @@ PyYAML = "^6.0"
hypothesis = "^6.54.6"
pytest = "^7.1.3"


[tool.poetry.group.dev.dependencies]
black = {version = "^22.10.0", allow-prereleases = true}

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
33 changes: 33 additions & 0 deletions tests/common/test_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,39 @@ def test_validate_tx_simple_create_signature(user_input, user_output, user_priv,
validate_transaction_model(tx)


def test_validate_tx_simple_create_signature_signing_delegation(user_input, user_output, user_priv, asset_definition):
from .utils import validate_transaction_model

# create TX & sign to have a reference signature
tx = Transaction(Transaction.CREATE, asset_definition, [user_input], [user_output])
expected = deepcopy(user_output)
tx_dict = tx.to_dict()
tx_dict["inputs"][0]["fulfillment"] = None
serialized_tx = json.dumps(tx_dict, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
message = sha3_256(serialized_tx.encode()).digest()
expected.fulfillment.sign(message, b58decode(user_priv))
tx.sign([user_priv])
signature = tx.inputs[0].fulfillment.signature

def signing_callback(message, digest):
return signature

# recreate the same TX and sign via the signing callback
tx = Transaction(Transaction.CREATE, asset_definition, [user_input], [user_output])
expected = deepcopy(user_output)
tx_dict = tx.to_dict()
tx_dict["inputs"][0]["fulfillment"] = None
serialized_tx = json.dumps(tx_dict, sort_keys=True, separators=(",", ":"), ensure_ascii=True)
message = sha3_256(serialized_tx.encode()).digest()
expected.fulfillment.sign(message, b58decode(user_priv))

tx.delegate_signing(signing_callback)
assert tx.inputs[0].to_dict()["fulfillment"] == expected.fulfillment.serialize_uri()
assert tx.inputs_valid() is True

validate_transaction_model(tx)


def test_invoke_simple_signature_fulfillment_with_invalid_params(utx, user_input):
from transactions.common.exceptions import KeypairMismatchException

Expand Down
2 changes: 1 addition & 1 deletion transactions/common/memoize.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def memoize_to_dict(func: Callable):
@functools.wraps(func)
def memoized_func(*args, **kwargs):

if args[0].id:
if len(args) > 0 and args[0] and args[0].id:
return to_dict(func, ToDictWrapper(args[0]))
else:
return func(*args, **kwargs)
Expand Down
26 changes: 26 additions & 0 deletions transactions/common/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,32 @@ def gen_public_key(private_key):

return self

def delegate_signing(self, callback):
"""Fulfills a previous Transaction's Output by signing Inputs using callback.
Note:
This method works only for the following Cryptoconditions
currently:
- Ed25519Fulfillment
Args:
callback (function): A callback used to sign inputs. Callback
takes input dict and message to sign as arguments
and returns signature (bytes).
Returns:
:class:`~planetmint.common.transaction.Transaction`
"""
tx_dict = self.to_dict()
tx_dict = Transaction._remove_signatures(tx_dict)
tx_serialized = Transaction._to_str(tx_dict)
message = sha3_256(tx_serialized.encode())
for input_ in self.inputs:
if input_.fulfills:
message.update("{}{}".format(input_.fulfills.txid, input_.fulfills.output).encode())
signature = callback(input_.to_dict(), message.digest())
input_.fulfillment.signature = signature
self._hash()
return self

@classmethod
def _sign_input(cls, input_: Input, message: str, key_pairs: dict) -> Input:
"""Signs a single Input.
Expand Down

0 comments on commit 648d0e7

Please sign in to comment.