Refactor secret conditions (#350)

* refactor spending conditions and add comments

* fix kind enum deserialization
This commit is contained in:
callebtc
2023-10-21 16:51:12 +02:00
committed by GitHub
parent 0490f20932
commit 48f732e9e7
8 changed files with 301 additions and 227 deletions

View File

@@ -132,17 +132,12 @@ class Proof(BaseModel):
@property @property
def p2pksigs(self) -> List[str]: def p2pksigs(self) -> List[str]:
assert self.witness, "Witness is missing" assert self.witness, "Witness is missing for p2pk signature"
return P2PKWitness.from_witness(self.witness).signatures return P2PKWitness.from_witness(self.witness).signatures
@property
def p2shscript(self) -> P2SHWitness:
assert self.witness, "Witness is missing"
return P2SHWitness.from_witness(self.witness)
@property @property
def htlcpreimage(self) -> Union[str, None]: def htlcpreimage(self) -> Union[str, None]:
assert self.witness, "Witness is missing" assert self.witness, "Witness is missing for htlc preimage"
return HTLCWitness.from_witness(self.witness).preimage return HTLCWitness.from_witness(self.witness).preimage
@@ -162,7 +157,7 @@ class BlindedMessage(BaseModel):
@property @property
def p2pksigs(self) -> List[str]: def p2pksigs(self) -> List[str]:
assert self.witness, "Witness is missing" assert self.witness, "Witness missing in output"
return P2PKWitness.from_witness(self.witness).signatures return P2PKWitness.from_witness(self.witness).signatures

View File

@@ -6,7 +6,7 @@ from .secret import Secret, SecretKind
class HTLCSecret(Secret): class HTLCSecret(Secret):
@classmethod @classmethod
def from_secret(cls, secret: Secret): def from_secret(cls, secret: Secret):
assert secret.kind == SecretKind.HTLC, "Secret is not a HTLC secret" assert SecretKind(secret.kind) == SecretKind.HTLC, "Secret is not a HTLC secret"
# NOTE: exclude tags in .dict() because it doesn't deserialize it properly # NOTE: exclude tags in .dict() because it doesn't deserialize it properly
# need to add it back in manually with tags=secret.tags # need to add it back in manually with tags=secret.tags
return cls(**secret.dict(exclude={"tags"}), tags=secret.tags) return cls(**secret.dict(exclude={"tags"}), tags=secret.tags)

View File

@@ -1,5 +1,6 @@
import hashlib import hashlib
import time import time
from enum import Enum
from typing import List, Union from typing import List, Union
from loguru import logger from loguru import logger
@@ -8,23 +9,26 @@ from .crypto.secp import PrivateKey, PublicKey
from .secret import Secret, SecretKind from .secret import Secret, SecretKind
class SigFlags: class SigFlags(Enum):
SIG_INPUTS = ( # require signatures only on the inputs (default signature flag) # require signatures only on the inputs (default signature flag)
"SIG_INPUTS" SIG_INPUTS = "SIG_INPUTS"
) # require signatures on inputs and outputs
SIG_ALL = "SIG_ALL" # require signatures on inputs and outputs SIG_ALL = "SIG_ALL"
class P2PKSecret(Secret): class P2PKSecret(Secret):
@classmethod @classmethod
def from_secret(cls, secret: Secret): def from_secret(cls, secret: Secret):
assert secret.kind == SecretKind.P2PK, "Secret is not a P2PK secret" assert SecretKind(secret.kind) == SecretKind.P2PK, "Secret is not a P2PK secret"
# NOTE: exclude tags in .dict() because it doesn't deserialize it properly # NOTE: exclude tags in .dict() because it doesn't deserialize it properly
# need to add it back in manually with tags=secret.tags # need to add it back in manually with tags=secret.tags
return cls(**secret.dict(exclude={"tags"}), tags=secret.tags) return cls(**secret.dict(exclude={"tags"}), tags=secret.tags)
def get_p2pk_pubkey_from_secret(self) -> List[str]: def get_p2pk_pubkey_from_secret(self) -> List[str]:
"""Gets the P2PK pubkey from a Secret depending on the locktime """Gets the P2PK pubkey from a Secret depending on the locktime.
If locktime is passed, only the refund pubkeys are returned.
Else, the pubkeys in the data field and in the 'pubkeys' tag are returned.
Args: Args:
secret (Secret): P2PK Secret in ecash token secret (Secret): P2PK Secret in ecash token
@@ -54,8 +58,9 @@ class P2PKSecret(Secret):
return int(locktime) if locktime else None return int(locktime) if locktime else None
@property @property
def sigflag(self) -> Union[None, str]: def sigflag(self) -> Union[None, SigFlags]:
return self.tags.get_tag("sigflag") sigflag = self.tags.get_tag("sigflag")
return SigFlags(sigflag) if sigflag else None
@property @property
def n_sigs(self) -> Union[None, int]: def n_sigs(self) -> Union[None, int]:

View File

@@ -1,4 +1,5 @@
import json import json
from enum import Enum
from typing import Any, Dict, List, Optional, Union from typing import Any, Dict, List, Optional, Union
from loguru import logger from loguru import logger
@@ -7,7 +8,7 @@ from pydantic import BaseModel
from .crypto.secp import PrivateKey from .crypto.secp import PrivateKey
class SecretKind: class SecretKind(Enum):
P2PK = "P2PK" P2PK = "P2PK"
HTLC = "HTLC" HTLC = "HTLC"

View File

@@ -19,11 +19,184 @@ from ..core.secret import Secret, SecretKind
class LedgerSpendingConditions: class LedgerSpendingConditions:
def _verify_p2pk_spending_conditions(self, proof: Proof, secret: Secret) -> bool:
"""
Verify P2PK spending condition for a single input.
We return True:
- if the secret is not a P2PKSecret spending condition
- if the locktime has passed and no refund pubkey is present
We raise an exception:
- if the pubkeys in the secret are not unique
- if no signatures are present
- if the signatures are not unique
- if n_sigs is not positive
- if n_sigs is larger than the number of provided signatures
- if no valid signatures are present
- if the signature threshold is not met
"""
if SecretKind(secret.kind) != SecretKind.P2PK:
# not a P2PK secret
return True
p2pk_secret = P2PKSecret.from_secret(secret)
# extract pubkeys that we require signatures from depending on whether the
# locktime has passed (refund) or not (pubkeys in secret.data and in tags)
# This is implemented in get_p2pk_pubkey_from_secret()
pubkeys = p2pk_secret.get_p2pk_pubkey_from_secret()
# we will get an empty list if the locktime has passed and no refund pubkey is present
if not pubkeys:
return True
assert len(set(pubkeys)) == len(pubkeys), "pubkeys must be unique."
logger.trace(f"pubkeys: {pubkeys}")
# verify that signatures are present
if not proof.p2pksigs:
# no signature present although secret indicates one
logger.error(f"no p2pk signatures in proof: {proof.p2pksigs}")
raise TransactionError("no p2pk signatures in proof.")
# we make sure that there are no duplicate signatures
if len(set(proof.p2pksigs)) != len(proof.p2pksigs):
raise TransactionError("p2pk signatures must be unique.")
# we parse the secret as a P2PK commitment
# assert len(proof.secret.split(":")) == 5, "p2pk secret format invalid."
# INPUTS: check signatures proof.p2pksigs against pubkey
# we expect the signature to be on the pubkey (=message) itself
n_sigs_required = p2pk_secret.n_sigs or 1
assert n_sigs_required > 0, "n_sigs must be positive."
# check if enough signatures are present
assert (
len(proof.p2pksigs) >= n_sigs_required
), f"not enough signatures provided: {len(proof.p2pksigs)} < {n_sigs_required}."
n_valid_sigs_per_output = 0
# loop over all signatures in output
for input_sig in proof.p2pksigs:
for pubkey in pubkeys:
logger.trace(f"verifying signature {input_sig} by pubkey {pubkey}.")
logger.trace(f"Message: {p2pk_secret.serialize().encode('utf-8')}")
if verify_p2pk_signature(
message=p2pk_secret.serialize().encode("utf-8"),
pubkey=PublicKey(bytes.fromhex(pubkey), raw=True),
signature=bytes.fromhex(input_sig),
):
n_valid_sigs_per_output += 1
logger.trace(
f"p2pk signature on input is valid: {input_sig} on {pubkey}."
)
# check if we have enough valid signatures
assert n_valid_sigs_per_output, "no valid signature provided for input."
assert n_valid_sigs_per_output >= n_sigs_required, (
f"signature threshold not met. {n_valid_sigs_per_output} <"
f" {n_sigs_required}."
)
logger.trace(
f"{n_valid_sigs_per_output} of {n_sigs_required} valid signatures found."
)
logger.trace(proof.p2pksigs)
logger.trace("p2pk signature on inputs is valid.")
return True
def _verify_htlc_spending_conditions(self, proof: Proof, secret: Secret) -> bool:
"""
Verify HTLC spending condition for a single input.
We return True:
- if the secret is not a HTLCSecret spending condition
We first verify the time lock. If the locktime has passed, we require
a valid signature if a 'refund' pubkey is present. If it isn't present,
anyone can spend.
We return True:
- if 'refund' pubkeys are present and a valid signature is provided for one of them
We raise an exception:
- if 'refund' but no valid signature is present
We then verify the hash lock. We require a valid preimage. We require a valid
signature if 'pubkeys' are present. If they aren't present, anyone who provides
a valid preimage can spend.
We raise an exception:
- if no preimage is provided
- if preimage does not match the hash lock in the secret
We return True:
- if 'pubkeys' are present and a valid signature is provided for one of them
We raise an exception:
- if 'pubkeys' are present but no valid signature is provided
"""
if SecretKind(secret.kind) != SecretKind.HTLC:
# not a P2PK secret
return True
htlc_secret = HTLCSecret.from_secret(secret)
# time lock
# check if locktime is in the past
if htlc_secret.locktime and htlc_secret.locktime < time.time():
refund_pubkeys = htlc_secret.tags.get_tag_all("refund")
if refund_pubkeys:
assert proof.witness, TransactionError("no HTLC refund signature.")
signature = HTLCWitness.from_witness(proof.witness).signature
assert signature, TransactionError("no HTLC refund signature provided")
for pubkey in refund_pubkeys:
if verify_p2pk_signature(
message=htlc_secret.serialize().encode("utf-8"),
pubkey=PublicKey(bytes.fromhex(pubkey), raw=True),
signature=bytes.fromhex(signature),
):
# a signature matches
return True
raise TransactionError("HTLC refund signatures did not match.")
# no pubkeys given in secret, anyone can spend
return True
# hash lock
assert proof.htlcpreimage, TransactionError("no HTLC preimage provided")
# first we check whether a correct preimage was included
if not hashlib.sha256(
bytes.fromhex(proof.htlcpreimage)
).digest() == bytes.fromhex(htlc_secret.data):
raise TransactionError("HTLC preimage does not match.")
# then we check whether a signature is required
hashlock_pubkeys = htlc_secret.tags.get_tag_all("pubkeys")
if hashlock_pubkeys:
assert proof.witness, TransactionError("no HTLC hash lock signature.")
signature = HTLCWitness.from_witness(proof.witness).signature
assert signature, TransactionError("HTLC no hash lock signatures provided.")
for pubkey in hashlock_pubkeys:
if verify_p2pk_signature(
message=htlc_secret.serialize().encode("utf-8"),
pubkey=PublicKey(bytes.fromhex(pubkey), raw=True),
signature=bytes.fromhex(signature),
):
# a signature matches
return True
# none of the pubkeys had a match
raise TransactionError("HTLC hash lock signatures did not match.")
# no pubkeys were included, anyone can spend
return True
def _verify_input_spending_conditions(self, proof: Proof) -> bool: def _verify_input_spending_conditions(self, proof: Proof) -> bool:
""" """
Verify spending conditions: Verify spending conditions:
Condition: P2PK - Witness: proof.p2pksigs Condition: P2PK - Checks if signature in proof.witness is valid for pubkey in proof.secret
Condition: HTLC - Witness: proof.htlcpreimage, proof.htlcsignature Condition: HTLC - Checks if preimage in proof.witness is valid for hash in proof.secret
""" """
try: try:
@@ -35,134 +208,118 @@ class LedgerSpendingConditions:
return True return True
# P2PK # P2PK
if secret.kind == SecretKind.P2PK: if SecretKind(secret.kind) == SecretKind.P2PK:
p2pk_secret = P2PKSecret.from_secret(secret) return self._verify_p2pk_spending_conditions(proof, secret)
# check if locktime is in the past
pubkeys = p2pk_secret.get_p2pk_pubkey_from_secret()
assert len(set(pubkeys)) == len(pubkeys), "pubkeys must be unique."
logger.trace(f"pubkeys: {pubkeys}")
# we will get an empty list if the locktime has passed and no refund pubkey is present
if not pubkeys:
return True
# now we check the signature # HTLC
if not proof.p2pksigs: if SecretKind(secret.kind) == SecretKind.HTLC:
# no signature present although secret indicates one return self._verify_htlc_spending_conditions(proof, secret)
logger.error(f"no p2pk signatures in proof: {proof.p2pksigs}")
raise TransactionError("no p2pk signatures in proof.")
# we make sure that there are no duplicate signatures # no spending condition present
if len(set(proof.p2pksigs)) != len(proof.p2pksigs): return True
raise TransactionError("p2pk signatures must be unique.")
# we parse the secret as a P2PK commitment # ------ output spending conditions ------
# assert len(proof.secret.split(":")) == 5, "p2pk secret format invalid."
# INPUTS: check signatures proof.p2pksigs against pubkey def _verify_output_p2pk_spending_conditions(
self, proofs: List[Proof], outputs: List[BlindedMessage]
) -> bool:
"""
If sigflag==SIG_ALL in proof.secret, check if outputs
contain valid signatures for pubkeys in proof.secret.
We return True
- if not all proof.secret are Secret spending condition
- if not all secrets are P2PKSecret spending condition
- if not all signature.sigflag are SIG_ALL
We raise an exception:
- if not all pubkeys in all secrets are the same
- if not all n_sigs in all secrets are the same
- if not all signatures in all outputs are unique
- if not all signatures in all outputs are valid
- if no valid signatures are present
- if the signature threshold is not met
We return True if we successfully validated the spending condition.
"""
try:
secrets_generic = [Secret.deserialize(p.secret) for p in proofs]
p2pk_secrets = [
P2PKSecret.from_secret(secret) for secret in secrets_generic
]
except Exception:
# secret is not a spending condition so we treat is a normal secret
return True
# check if all secrets are P2PK
# NOTE: This is redundant, because P2PKSecret.from_secret() already checks for the kind
# Leaving it in for explicitness
if not all(
[SecretKind(secret.kind) == SecretKind.P2PK for secret in p2pk_secrets]
):
# not all secrets are P2PK
return True
# check if all secrets are sigflag==SIG_ALL
if not all([secret.sigflag == SigFlags.SIG_ALL for secret in p2pk_secrets]):
# not all secrets have sigflag==SIG_ALL
return True
# extract all pubkeys and n_sigs from secrets
pubkeys_per_proof = [
secret.get_p2pk_pubkey_from_secret() for secret in p2pk_secrets
]
n_sigs_per_proof = [secret.n_sigs for secret in p2pk_secrets]
# all pubkeys and n_sigs must be the same
assert (
len(set([tuple(pubs_output) for pubs_output in pubkeys_per_proof])) == 1
), "pubkeys in all proofs must match."
assert len(set(n_sigs_per_proof)) == 1, "n_sigs in all proofs must match."
# TODO: add limit for maximum number of pubkeys
# validation successful
pubkeys: List[str] = pubkeys_per_proof[0]
# if n_sigs is None, we set it to 1
n_sigs: int = n_sigs_per_proof[0] or 1
logger.trace(f"pubkeys: {pubkeys}")
# loop over all outputs and check if the signatures are valid for pubkeys with a threshold of n_sig
for output in outputs:
# we expect the signature to be on the pubkey (=message) itself # we expect the signature to be on the pubkey (=message) itself
n_sigs_required = p2pk_secret.n_sigs or 1 p2pksigs = output.p2pksigs
assert n_sigs_required > 0, "n_sigs must be positive." assert p2pksigs, "no signatures in output."
# TODO: add limit for maximum number of signatures
# check if enough signatures are present # we check whether any signature is duplicate
assert len(proof.p2pksigs) >= n_sigs_required, ( assert len(set(p2pksigs)) == len(
f"not enough signatures provided: {len(proof.p2pksigs)} <" p2pksigs
f" {n_sigs_required}." ), "duplicate signatures in output."
)
n_valid_sigs_per_output = 0 n_valid_sigs_per_output = 0
# loop over all signatures in output # loop over all signatures in output
for input_sig in proof.p2pksigs: for sig in p2pksigs:
for pubkey in pubkeys: for pubkey in pubkeys:
logger.trace(f"verifying signature {input_sig} by pubkey {pubkey}.")
logger.trace(f"Message: {p2pk_secret.serialize().encode('utf-8')}")
if verify_p2pk_signature( if verify_p2pk_signature(
message=p2pk_secret.serialize().encode("utf-8"), message=output.B_.encode("utf-8"),
pubkey=PublicKey(bytes.fromhex(pubkey), raw=True), pubkey=PublicKey(bytes.fromhex(pubkey), raw=True),
signature=bytes.fromhex(input_sig), signature=bytes.fromhex(sig),
): ):
n_valid_sigs_per_output += 1 n_valid_sigs_per_output += 1
logger.trace( assert n_valid_sigs_per_output, "no valid signature provided for output."
f"p2pk signature on input is valid: {input_sig} on" assert (
f" {pubkey}." n_valid_sigs_per_output >= n_sigs
) ), f"signature threshold not met. {n_valid_sigs_per_output} < {n_sigs}."
continue
else:
logger.trace(
f"p2pk signature on input is invalid: {input_sig} on"
f" {pubkey}."
)
# check if we have enough valid signatures
assert n_valid_sigs_per_output, "no valid signature provided for input."
assert n_valid_sigs_per_output >= n_sigs_required, (
f"signature threshold not met. {n_valid_sigs_per_output} <"
f" {n_sigs_required}."
)
logger.trace( logger.trace(
f"{n_valid_sigs_per_output} of {n_sigs_required} valid signatures" f"{n_valid_sigs_per_output} of {n_sigs} valid signatures found."
" found."
) )
logger.trace(p2pksigs)
logger.trace(proof.p2pksigs) logger.trace("p2pk signatures on output is valid.")
logger.trace("p2pk signature on inputs is valid.")
return True
# HTLC
if secret.kind == SecretKind.HTLC:
htlc_secret = HTLCSecret.from_secret(secret)
# time lock
# check if locktime is in the past
if htlc_secret.locktime and htlc_secret.locktime < time.time():
refund_pubkeys = htlc_secret.tags.get_tag_all("refund")
if refund_pubkeys:
assert proof.witness, TransactionError("no HTLC refund signature.")
signature = HTLCWitness.from_witness(proof.witness).signature
assert signature, TransactionError(
"no HTLC refund signature provided"
)
for pubkey in refund_pubkeys:
if verify_p2pk_signature(
message=htlc_secret.serialize().encode("utf-8"),
pubkey=PublicKey(bytes.fromhex(pubkey), raw=True),
signature=bytes.fromhex(signature),
):
# a signature matches
return True
raise TransactionError("HTLC refund signatures did not match.")
# no pubkeys given in secret, anyone can spend
return True
# hash lock
assert proof.htlcpreimage, TransactionError("no HTLC preimage provided")
# first we check whether a correct preimage was included
if not hashlib.sha256(
bytes.fromhex(proof.htlcpreimage)
).digest() == bytes.fromhex(htlc_secret.data):
raise TransactionError("HTLC preimage does not match.")
# then we check whether a signature is required
hashlock_pubkeys = htlc_secret.tags.get_tag_all("pubkeys")
if hashlock_pubkeys:
assert proof.witness, TransactionError("no HTLC hash lock signature.")
signature = HTLCWitness.from_witness(proof.witness).signature
assert signature, TransactionError(
"HTLC no hash lock signatures provided."
)
for pubkey in hashlock_pubkeys:
if verify_p2pk_signature(
message=htlc_secret.serialize().encode("utf-8"),
pubkey=PublicKey(bytes.fromhex(pubkey), raw=True),
signature=bytes.fromhex(signature),
):
# a signature matches
return True
# none of the pubkeys had a match
raise TransactionError("HTLC hash lock signatures did not match.")
# no pubkeys were included, anyone can spend
return True
# no spending condition present
return True return True
def _verify_output_spending_conditions( def _verify_output_spending_conditions(
@@ -170,86 +327,7 @@ class LedgerSpendingConditions:
) -> bool: ) -> bool:
""" """
Verify spending conditions: Verify spending conditions:
Condition: P2PK - Witness: output.p2pksigs Condition: P2PK - If sigflag==SIG_ALL in proof.secret, check if outputs contain valid signatures for pubkeys in proof.secret.
""" """
# P2PK return self._verify_output_p2pk_spending_conditions(proofs, outputs)
pubkeys_per_proof = []
n_sigs = []
for proof in proofs:
try:
secret = P2PKSecret.deserialize(proof.secret)
# get all p2pk pubkeys from secrets
pubkeys_per_proof.append(secret.get_p2pk_pubkey_from_secret())
# get signature threshold from secrets
n_sigs.append(secret.n_sigs)
except Exception:
# secret is not a spending condition so we treat is a normal secret
return True
# for all proofs all pubkeys must be the same
assert (
len(set([tuple(pubs_output) for pubs_output in pubkeys_per_proof])) == 1
), "pubkeys in all proofs must match."
pubkeys = pubkeys_per_proof[0]
if not pubkeys:
# no pubkeys present
return True
logger.trace(f"pubkeys: {pubkeys}")
# TODO: add limit for maximum number of pubkeys
# for all proofs all n_sigs must be the same
assert len(set(n_sigs)) == 1, "n_sigs in all proofs must match."
n_sigs_required = n_sigs[0] or 1
# first we check if all secrets are P2PK
if not all(
[Secret.deserialize(p.secret).kind == SecretKind.P2PK for p in proofs]
):
# not all secrets are P2PK
return True
# now we check if any of the secrets has sigflag==SIG_ALL
if not any(
[
P2PKSecret.deserialize(p.secret).sigflag == SigFlags.SIG_ALL
for p in proofs
]
):
# no secret has sigflag==SIG_ALL
return True
# loop over all outputs and check if the signatures are valid for pubkeys with a threshold of n_sig
for output in outputs:
# we expect the signature to be on the pubkey (=message) itself
assert output.p2pksigs, "no signatures in output."
# TODO: add limit for maximum number of signatures
# we check whether any signature is duplicate
assert len(set(output.p2pksigs)) == len(
output.p2pksigs
), "duplicate signatures in output."
n_valid_sigs_per_output = 0
# loop over all signatures in output
for output_sig in output.p2pksigs:
for pubkey in pubkeys:
if verify_p2pk_signature(
message=output.B_.encode("utf-8"),
pubkey=PublicKey(bytes.fromhex(pubkey), raw=True),
signature=bytes.fromhex(output_sig),
):
n_valid_sigs_per_output += 1
assert n_valid_sigs_per_output, "no valid signature provided for output."
assert n_valid_sigs_per_output >= n_sigs_required, (
f"signature threshold not met. {n_valid_sigs_per_output} <"
f" {n_sigs_required}."
)
logger.trace(
f"{n_valid_sigs_per_output} of {n_sigs_required} valid signatures"
" found."
)
logger.trace(output.p2pksigs)
logger.trace("p2pk signatures on output is valid.")
return True

View File

@@ -40,7 +40,7 @@ class WalletHTLC(SupportsDb):
tags["pubkeys"] = hashlock_pubkey tags["pubkeys"] = hashlock_pubkey
return HTLCSecret( return HTLCSecret(
kind=SecretKind.HTLC, kind=SecretKind.HTLC.value,
data=preimage_hash, data=preimage_hash,
tags=tags, tags=tags,
) )

View File

@@ -49,12 +49,14 @@ class WalletP2PK(SupportsPrivateKey, SupportsDb):
tags["locktime"] = str( tags["locktime"] = str(
int((datetime.now() + timedelta(seconds=locktime_seconds)).timestamp()) int((datetime.now() + timedelta(seconds=locktime_seconds)).timestamp())
) )
tags["sigflag"] = SigFlags.SIG_ALL if sig_all else SigFlags.SIG_INPUTS tags["sigflag"] = (
SigFlags.SIG_ALL.value if sig_all else SigFlags.SIG_INPUTS.value
)
if n_sigs > 1: if n_sigs > 1:
tags["n_sigs"] = str(n_sigs) tags["n_sigs"] = str(n_sigs)
logger.debug(f"After tags: {tags}") logger.debug(f"After tags: {tags}")
return P2PKSecret( return P2PKSecret(
kind=SecretKind.P2PK, kind=SecretKind.P2PK.value,
data=pubkey, data=pubkey,
tags=tags, tags=tags,
) )
@@ -182,7 +184,9 @@ class WalletP2PK(SupportsPrivateKey, SupportsDb):
return proofs return proofs
logger.debug("Spending conditions detected.") logger.debug("Spending conditions detected.")
# P2PK signatures # P2PK signatures
if all([Secret.deserialize(p.secret).kind == SecretKind.P2PK for p in proofs]): if all(
[Secret.deserialize(p.secret).kind == SecretKind.P2PK.value for p in proofs]
):
logger.debug("P2PK redemption detected.") logger.debug("P2PK redemption detected.")
proofs = await self.add_p2pk_witnesses_to_proofs(proofs) proofs = await self.add_p2pk_witnesses_to_proofs(proofs)

View File

@@ -73,7 +73,6 @@ async def test_htlc_split(wallet1: Wallet, wallet2: Wallet):
preimage = "00000000000000000000000000000000" preimage = "00000000000000000000000000000000"
preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
secret = await wallet1.create_htlc_lock(preimage=preimage) secret = await wallet1.create_htlc_lock(preimage=preimage)
# p2pk test
_, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret)
for p in send_proofs: for p in send_proofs:
assert HTLCSecret.deserialize(p.secret).data == preimage_hash assert HTLCSecret.deserialize(p.secret).data == preimage_hash
@@ -86,7 +85,6 @@ async def test_htlc_redeem_with_preimage(wallet1: Wallet, wallet2: Wallet):
preimage = "00000000000000000000000000000000" preimage = "00000000000000000000000000000000"
# preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest() # preimage_hash = hashlib.sha256(bytes.fromhex(preimage)).hexdigest()
secret = await wallet1.create_htlc_lock(preimage=preimage) secret = await wallet1.create_htlc_lock(preimage=preimage)
# p2pk test
_, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret)
for p in send_proofs: for p in send_proofs:
p.witness = HTLCWitness(preimage=preimage).json() p.witness = HTLCWitness(preimage=preimage).json()
@@ -102,7 +100,6 @@ async def test_htlc_redeem_with_wrong_preimage(wallet1: Wallet, wallet2: Wallet)
secret = await wallet1.create_htlc_lock( secret = await wallet1.create_htlc_lock(
preimage=preimage[:-5] + "11111" preimage=preimage[:-5] + "11111"
) # wrong preimage ) # wrong preimage
# p2pk test
_, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret)
for p in send_proofs: for p in send_proofs:
p.witness = HTLCWitness(preimage=preimage).json() p.witness = HTLCWitness(preimage=preimage).json()
@@ -121,7 +118,6 @@ async def test_htlc_redeem_with_no_signature(wallet1: Wallet, wallet2: Wallet):
secret = await wallet1.create_htlc_lock( secret = await wallet1.create_htlc_lock(
preimage=preimage, hashlock_pubkey=pubkey_wallet1 preimage=preimage, hashlock_pubkey=pubkey_wallet1
) )
# p2pk test
_, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret)
for p in send_proofs: for p in send_proofs:
p.witness = HTLCWitness(preimage=preimage).json() p.witness = HTLCWitness(preimage=preimage).json()
@@ -141,8 +137,6 @@ async def test_htlc_redeem_with_wrong_signature(wallet1: Wallet, wallet2: Wallet
secret = await wallet1.create_htlc_lock( secret = await wallet1.create_htlc_lock(
preimage=preimage, hashlock_pubkey=pubkey_wallet1 preimage=preimage, hashlock_pubkey=pubkey_wallet1
) )
# p2pk test
_, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret)
signatures = await wallet1.sign_p2pk_proofs(send_proofs) signatures = await wallet1.sign_p2pk_proofs(send_proofs)
for p, s in zip(send_proofs, signatures): for p, s in zip(send_proofs, signatures):
@@ -166,7 +160,6 @@ async def test_htlc_redeem_with_correct_signature(wallet1: Wallet, wallet2: Wall
secret = await wallet1.create_htlc_lock( secret = await wallet1.create_htlc_lock(
preimage=preimage, hashlock_pubkey=pubkey_wallet1 preimage=preimage, hashlock_pubkey=pubkey_wallet1
) )
# p2pk test
_, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret)
signatures = await wallet1.sign_p2pk_proofs(send_proofs) signatures = await wallet1.sign_p2pk_proofs(send_proofs)
@@ -192,7 +185,6 @@ async def test_htlc_redeem_hashlock_wrong_signature_timelock_correct_signature(
locktime_seconds=5, locktime_seconds=5,
locktime_pubkey=pubkey_wallet1, locktime_pubkey=pubkey_wallet1,
) )
# p2pk test
_, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret)
signatures = await wallet1.sign_p2pk_proofs(send_proofs) signatures = await wallet1.sign_p2pk_proofs(send_proofs)
@@ -226,7 +218,6 @@ async def test_htlc_redeem_hashlock_wrong_signature_timelock_wrong_signature(
locktime_seconds=5, locktime_seconds=5,
locktime_pubkey=pubkey_wallet1, locktime_pubkey=pubkey_wallet1,
) )
# p2pk test
_, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret) _, send_proofs = await wallet1.split_to_send(wallet1.proofs, 8, secret_lock=secret)
signatures = await wallet1.sign_p2pk_proofs(send_proofs) signatures = await wallet1.sign_p2pk_proofs(send_proofs)