cl/ptx-note-proofs: start data modelling input and outputs

This commit is contained in:
David Rusu 2024-05-30 16:01:41 +04:00
parent 71ede291d2
commit 8e98d89a0b
4 changed files with 80 additions and 26 deletions

View File

@ -10,7 +10,7 @@ from crypto import (
hash_to_curve,
)
from constraints import Constraint
from constraints import Constraint, Proof
# TODO: is this used?
@ -39,6 +39,8 @@ def balance_commitment(value: Field, tx_rand: Field, funge: Point):
class InnerNote:
value: Field
unit: str
# TODO: inner notes should hold commitments to constraints.
# Constraints themselves will be stored in a key-value store
birth_constraint: Constraint
death_constraints: list[Constraint]
state: Field
@ -63,12 +65,23 @@ class InnerNote:
assert isinstance(self.nonce, Field), f"nonce is {type(self.nonce)}"
assert isinstance(self.rand, Field), f"rand is {type(self.rand)}"
def r(self, index: int):
prf("CL_NOTE_COMM_RAND", self.rand, index)
def verify_death(self, death_cm: Field, death_proof: Proof) -> bool:
constraint = [d for d in self.death_constraints if d.hash() == deah_cm][0]
# TODO: verifying the death constraint should include a commitment to the
# partial transaction.
return constraint.verify(death_proof)
def verify_birth(self, birth_proof: Proof) -> bool:
# TODO: Should verifying the birth constraint include a commitment
# to the partial transaction?
return self.birth_constraint.verify(birth_proof)
def verify_value(self) -> bool:
return 0 <= self.value and value <= 2**64
def r(self, index: int):
return prf("CL_NOTE_COMM_RAND", self.rand, index)
@property
def fungibility_domain(self) -> Field:
"""The fungibility domain of this note"""

View File

@ -1,5 +1,9 @@
* Open Issues
** Note.verify() should take a proof
We should have two variants, verifyDeath(deathProof) verifyBirth(birthProof)
** provided commitment to zero may removing the blinding of the pederson commitment
Since you can subtract the randomness from the commitment to get just the binding part.

View File

@ -1,9 +1,30 @@
from dataclasses import dataclass
from constraints import Proof
from note import PublicNote, SecretNote
from crypto import Field, Point
@dataclass
class InputNote:
note: SecretNote
death_proof: Proof
def verify(self):
return self.note.verify_death(self.death_proof)
@dataclass
class OutputNote:
note: PublicNote
birth_proof: Proof
def verify(self):
return self.note.verify_birth(self.birth_proof)
# TODO: is this used?
@dataclass
class Output:
note: PublicNote
@ -15,20 +36,23 @@ class Output:
@dataclass(unsafe_hash=True)
class PartialTransaction:
inputs: list[SecretNote]
outputs: list[Output]
inputs: list[InputNote]
outputs: list[OutputNote]
rand: Field
def verify(self) -> bool:
raise NotImplementedError()
valid_inputs = all(i.verify() for i in self.inputs)
valid_outputs = all(o.verify() for o in self.outputs)
return valid_inputs and valid_output
def balance(self) -> Point:
output_balance = sum((n.balance for n in self.outputs), start=Point.zero())
# TODO: once again just mentioning this inefficiency. we are converting our private
# inputs to public inputs to compute the balance, so we don't need an Output class,
# we can directly compute the balance commitment from the public output notes.
output_balance = sum(
(n.note.balance(self.rand) for n in self.outputs),
start=Point.zero(),
)
input_balance = sum(
(n.to_public().balance(self.rand) for n in self.inputs), start=Point.zero()
(n.note.to_public().balance(self.rand) for n in self.inputs),
start=Point.zero(),
)
return output_balance + input_balance.negate()
@ -37,12 +61,13 @@ class PartialTransaction:
return sum(outputs.blinding(self.rand)) - sum(outputs.blinding(self.rand))
def zero(self) -> Field:
output_zero = sum((n.zero for n in self.outputs), start=Point.zero())
# TODO: once again just mentioning this inefficiency. we are converting our private
# inputs to public inputs to compute the zero commitment, so we don't need an Output class,
# we can directly compute the zero commitment from the public output notes.
output_zero = sum(
(n.note.zero(self.rand) for n in self.outputs),
start=Point.zero(),
)
input_zero = sum(
(n.to_public().zero(self.rand) for n in self.inputs), start=Point.zero()
(n.note.to_public().zero(self.rand) for n in self.inputs),
start=Point.zero(),
)
return output_zero + input_zero.negate()

View File

@ -3,7 +3,7 @@ from dataclasses import dataclass
from crypto import Field, prf
from note import InnerNote, PublicNote, SecretNote, nf_pk
from partial_transaction import PartialTransaction, Output
from partial_transaction import PartialTransaction, InputNote, OutputNote
from transaction_bundle import TransactionBundle
import constraints
@ -50,17 +50,29 @@ class TestTransfer(TestCase):
),
nf_pk=bob.pk,
)
tx_output = Output(
note=bobs_note,
# TODO: why do we need an Output struct if we can
# compute the balance and zero commitment form the
# PublicNote itself?
balance=bobs_note.balance(tx_rand),
zero=bobs_note.zero(tx_rand),
)
# tx_output = Output(
# note=bobs_note,
# # TODO: why do we need an Output struct if we can
# # compute the balance and zero commitment form the
# # PublicNote itself?
# balance=bobs_note.balance(tx_rand),
# zero=bobs_note.zero(tx_rand),
# )
ptx = PartialTransaction(
inputs=[alices_note], outputs=[tx_output], rand=tx_rand
inputs=[
InputNote(
note=alices_note,
death_proof=constraints.Vacuous().prove(),
)
],
outputs=[
OutputNote(
note=bobs_note,
birth_proof=constraints.Vacuous().prove(),
)
],
rand=tx_rand,
)
bundle = TransactionBundle(bundle=[ptx])