mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-04-28 03:00:18 -04:00
daod: tx verification
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import hashlib
|
||||
import sys
|
||||
from collections import namedtuple
|
||||
from classnamespace import ClassNamespace
|
||||
@@ -38,15 +39,16 @@ class TransactionBuilder:
|
||||
return total % self.ec.order
|
||||
|
||||
def build(self):
|
||||
tx = Transaction()
|
||||
tx = Transaction(self.ec)
|
||||
token_blind = self.ec.random_scalar()
|
||||
|
||||
for input in self.clear_inputs:
|
||||
tx_clear_input = ClassNamespace()
|
||||
tx_clear_input.__name__ = "TransactionClearInput"
|
||||
tx_clear_input.value = input.value
|
||||
tx_clear_input.token_id = input.token_id
|
||||
tx_clear_input.value_blind = self.ec.random_scalar()
|
||||
tx_clear_input.token_blind = input.token_blind
|
||||
tx_clear_input.token_blind = token_blind
|
||||
tx_clear_input.signature_public = self.ec.multiply(
|
||||
input.signature_secret, self.ec.G)
|
||||
tx.clear_inputs.append(tx_clear_input)
|
||||
@@ -72,24 +74,40 @@ class TransactionBuilder:
|
||||
note.token_id = output.token_id
|
||||
note.coin_blind = self.ec.random_base()
|
||||
note.value_blind = value_blind
|
||||
note.token_blind = token_blind
|
||||
|
||||
tx_output = ClassNamespace()
|
||||
tx_output.__name__ = "TransactionOutput"
|
||||
|
||||
tx_output.mint_proof = MintProof(
|
||||
note.value, note.token_id, note.value_blind,
|
||||
token_blind, tx_output.serial, coin_blind, public)
|
||||
note.token_blind, note.serial, note.coin_blind,
|
||||
output.public, self.ec)
|
||||
tx_output.revealed = tx_output.mint_proof.get_revealed()
|
||||
assert tx_output.mint_proof.verify(tx_output.revealed)
|
||||
|
||||
# Is normally encrypted
|
||||
tx_output.enc_note = note
|
||||
tx_output.enc_note.__name__ = "TransactionOutputEncryptedNote"
|
||||
|
||||
tx.outputs.append(tx_output)
|
||||
|
||||
unsigned_tx_data = tx.partial_encode()
|
||||
for (input, info) in zip(tx.clear_inputs, self.clear_inputs):
|
||||
secret = info.signature_secret
|
||||
signature = sign(unsigned_tx_data, secret, self.ec)
|
||||
input.signature = signature
|
||||
for (input, info) in zip(tx.inputs, self.inputs):
|
||||
secret = info.signature_secret
|
||||
signature = sign(unsigned_tx_data, secret, self.ec)
|
||||
input.signature = signature
|
||||
|
||||
return tx
|
||||
|
||||
class MintProof:
|
||||
|
||||
def __init__(self, value, token_id, value_blind, token_blind, serial,
|
||||
coin_blind, public):
|
||||
coin_blind, public, ec):
|
||||
self.value = value
|
||||
self.token_id = token_id
|
||||
self.value_blind = value_blind
|
||||
@@ -98,26 +116,181 @@ class MintProof:
|
||||
self.coin_blind = coin_blind
|
||||
self.public = public
|
||||
|
||||
self.ec = ec
|
||||
|
||||
def get_revealed(self):
|
||||
revealed = ClassNamespace()
|
||||
revealed.coin = ff_hash(
|
||||
self.ec.p,
|
||||
self.public[0],
|
||||
self.public[1],
|
||||
self.value,
|
||||
self.token_id,
|
||||
self.serial,
|
||||
self.coin_blind
|
||||
)
|
||||
|
||||
revealed.value_commit = pedersen_encrypt(
|
||||
self.value, self.value_blind, self.ec
|
||||
)
|
||||
revealed.token_commit = pedersen_encrypt(
|
||||
self.token_id, self.token_blind, self.ec
|
||||
)
|
||||
|
||||
return revealed
|
||||
|
||||
def verify(self):
|
||||
pass
|
||||
def verify(self, public):
|
||||
revealed = self.get_revealed()
|
||||
return all([
|
||||
revealed.coin == public.coin,
|
||||
revealed.value_commit == public.value_commit,
|
||||
revealed.token_commit == public.token_commit
|
||||
])
|
||||
|
||||
def pedersen_encrypt(x, y, ec):
|
||||
vcv = ec.multiply(x, ec.G)
|
||||
vcr = ec.multiply(y, ec.H)
|
||||
return ec.add(vcv, vcr)
|
||||
|
||||
def ff_hash(p, *args):
|
||||
hasher = hashlib.sha256()
|
||||
for arg in args:
|
||||
match arg:
|
||||
case int() as arg:
|
||||
hasher.update(arg.to_bytes(32, byteorder="little"))
|
||||
case bytes() as arg:
|
||||
hasher.update(arg)
|
||||
case _:
|
||||
raise Exception(f"unknown hash arg '{arg}' type: {type(arg)}")
|
||||
value = int.from_bytes(hasher.digest(), byteorder="little")
|
||||
return value % p
|
||||
|
||||
def hash_point(point, message=None):
|
||||
hasher = hashlib.sha256()
|
||||
for x_i in point:
|
||||
hasher.update(x_i.to_bytes(32, byteorder="little"))
|
||||
# Optional message
|
||||
if message is not None:
|
||||
hasher.update(message)
|
||||
value = int.from_bytes(hasher.digest(), byteorder="little")
|
||||
return value
|
||||
|
||||
def sign(message, secret, ec):
|
||||
ephem_secret = ec.random_scalar()
|
||||
ephem_public = ec.multiply(ephem_secret, ec.G)
|
||||
challenge = hash_point(ephem_public, message) % ec.order
|
||||
response = (ephem_secret + challenge * secret) % ec.order
|
||||
return ephem_public, response
|
||||
|
||||
def verify(message, signature, public, ec):
|
||||
ephem_public, response = signature
|
||||
challenge = hash_point(ephem_public, message) % ec.order
|
||||
# sG
|
||||
lhs = ec.multiply(response, ec.G)
|
||||
# R + cP
|
||||
rhs_cP = ec.multiply(challenge, public)
|
||||
rhs = ec.add(ephem_public, rhs_cP)
|
||||
return lhs == rhs
|
||||
|
||||
class Transaction:
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, ec):
|
||||
self.clear_inputs = []
|
||||
self.inputs = []
|
||||
self.outputs = []
|
||||
|
||||
self.ec = ec
|
||||
|
||||
def partial_encode(self):
|
||||
return b"hello"
|
||||
|
||||
def verify(self):
|
||||
if not self._check_value_commits():
|
||||
return False, "value commits do not match"
|
||||
|
||||
if not self._check_proofs():
|
||||
return False, "proofs failed to verify"
|
||||
|
||||
if not self._verify_token_commitments():
|
||||
return False, "token ID mismatch"
|
||||
|
||||
unsigned_tx_data = self.partial_encode()
|
||||
for input in self.clear_inputs:
|
||||
public = input.signature_public
|
||||
if not verify(unsigned_tx_data, input.signature, public, self.ec):
|
||||
return False
|
||||
for input in self.inputs:
|
||||
public = input.revealed.signature_public
|
||||
if not verify(unsigned_tx_data, input.signature, public, self.ec):
|
||||
return False
|
||||
|
||||
return True, None
|
||||
|
||||
def _check_value_commits(self):
|
||||
valcom_total = (0, 1, 0)
|
||||
|
||||
for input in self.clear_inputs:
|
||||
value_commit = pedersen_encrypt(input.value, input.value_blind,
|
||||
self.ec)
|
||||
valcom_total = self.ec.add(valcom_total, value_commit)
|
||||
for input in self.inputs:
|
||||
value_commit = input.revealed.value_commit
|
||||
valcom_total = self.ec.add(valcom_total, value_commit)
|
||||
for output in self.outputs:
|
||||
v = output.revealed.value_commit
|
||||
value_commit = (v[0], -v[1], v[2])
|
||||
valcom_total = self.ec.add(valcom_total, value_commit)
|
||||
|
||||
return valcom_total == (0, 1, 0)
|
||||
|
||||
def _check_proofs(self):
|
||||
for input in self.inputs:
|
||||
if not input.burn_proof.verify(input.revealed):
|
||||
return False
|
||||
for output in self.outputs:
|
||||
if not output.mint_proof.verify(output.revealed):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _verify_token_commitments(self):
|
||||
assert len(self.outputs) > 0
|
||||
token_commit_value = self.outputs[0].revealed.token_commit
|
||||
for input in self.clear_inputs:
|
||||
token_commit = pedersen_encrypt(input.token_id, input.token_blind,
|
||||
self.ec)
|
||||
if token_commit != token_commit_value:
|
||||
return False
|
||||
for input in self.inputs:
|
||||
if input.revealed.token_commit != token_commit_value:
|
||||
return False
|
||||
for output in self.outputs:
|
||||
if output.revealed.token_commit != token_commit_value:
|
||||
return False
|
||||
return True
|
||||
|
||||
def main(argv):
|
||||
ec = pallas_curve()
|
||||
|
||||
secret = ec.random_scalar()
|
||||
public = ec.multiply(secret, ec.G)
|
||||
|
||||
initial_supply = 21000
|
||||
token_id = 110
|
||||
|
||||
signature_secret = ec.random_scalar()
|
||||
|
||||
builder = TransactionBuilder(ec)
|
||||
builder.add_output(44, 110, "1234")
|
||||
builder.add_clear_input(initial_supply, token_id, signature_secret)
|
||||
builder.add_output(initial_supply, token_id, public)
|
||||
tx = builder.build()
|
||||
|
||||
is_verify, reason = tx.verify()
|
||||
if not is_verify:
|
||||
print(f"tx verify failed: {reason}", file=sys.stderr)
|
||||
return -1
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main(sys.argv))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user