Files
nutshell/cashu/core/p2pk.py
callebtc d827579e65 Coalesce all witness fields to Proof.witness (#342)
* call proofs field witness

* test p2pk sig_all=True

* outputs also use witness field
2023-10-13 21:33:21 +02:00

100 lines
3.3 KiB
Python

import hashlib
import time
from typing import List, Union
from loguru import logger
from .crypto.secp import PrivateKey, PublicKey
from .secret import Secret, SecretKind
class SigFlags:
SIG_INPUTS = ( # require signatures only on the inputs (default signature flag)
"SIG_INPUTS"
)
SIG_ALL = "SIG_ALL" # require signatures on inputs and outputs
class P2PKSecret(Secret):
@classmethod
def from_secret(cls, secret: Secret):
assert secret.kind == SecretKind.P2PK, "Secret is not a P2PK secret"
# NOTE: exclude tags in .dict() because it doesn't deserialize it properly
# need to add it back in manually with tags=secret.tags
return cls(**secret.dict(exclude={"tags"}), tags=secret.tags)
def get_p2pk_pubkey_from_secret(self) -> List[str]:
"""Gets the P2PK pubkey from a Secret depending on the locktime
Args:
secret (Secret): P2PK Secret in ecash token
Returns:
str: pubkey to use for P2PK, empty string if anyone can spend (locktime passed)
"""
# the pubkey in the data field is the pubkey to use for P2PK
pubkeys: List[str] = [self.data]
# get all additional pubkeys from tags for multisig
pubkeys += self.tags.get_tag_all("pubkeys")
# check if locktime is passed and if so, only return refund pubkeys
now = time.time()
if self.locktime and self.locktime < now:
logger.trace(f"p2pk locktime ran out ({self.locktime}<{now}).")
# check tags if a refund pubkey is present.
# If yes, we demand the signature to be from the refund pubkey
return self.tags.get_tag_all("refund")
return pubkeys
@property
def locktime(self) -> Union[None, int]:
locktime = self.tags.get_tag("locktime")
return int(locktime) if locktime else None
@property
def sigflag(self) -> Union[None, str]:
return self.tags.get_tag("sigflag")
@property
def n_sigs(self) -> Union[None, int]:
n_sigs = self.tags.get_tag("n_sigs")
return int(n_sigs) if n_sigs else None
def sign_p2pk_sign(message: bytes, private_key: PrivateKey):
# ecdsa version
# signature = private_key.ecdsa_serialize(private_key.ecdsa_sign(message))
signature = private_key.schnorr_sign(
hashlib.sha256(message).digest(), None, raw=True
)
return signature.hex()
def verify_p2pk_signature(message: bytes, pubkey: PublicKey, signature: bytes):
# ecdsa version
# return pubkey.ecdsa_verify(message, pubkey.ecdsa_deserialize(signature))
return pubkey.schnorr_verify(
hashlib.sha256(message).digest(), signature, None, raw=True
)
if __name__ == "__main__":
# generate keys
private_key_bytes = b"12300000000000000000000000000123"
private_key = PrivateKey(private_key_bytes, raw=True)
print(private_key.serialize())
public_key = private_key.pubkey
assert public_key
print(public_key.serialize().hex())
# sign message (=pubkey)
message = public_key.serialize()
signature = private_key.ecdsa_serialize(private_key.ecdsa_sign(message))
print(signature.hex())
# verify
pubkey_verify = PublicKey(message, raw=True)
print(public_key.ecdsa_verify(message, pubkey_verify.ecdsa_deserialize(signature)))