pset: add value and asset proofs
This commit is contained in:
parent
567903b018
commit
75230360b6
@ -124,6 +124,8 @@ class LOutputScope(OutputScope):
|
||||
self.blinding_pubkey = None
|
||||
self.asset = None
|
||||
self.blinder_index = None
|
||||
self.value_proof = None
|
||||
self.asset_proof = None
|
||||
if vout:
|
||||
self.asset = vout.asset
|
||||
self._verified = False
|
||||
@ -140,18 +142,39 @@ class LOutputScope(OutputScope):
|
||||
e = PSBTError("Invalid commitments")
|
||||
if self.asset and self.asset_commitment:
|
||||
# we can't verify asset
|
||||
if not self.asset_blinding_factor:
|
||||
raise e
|
||||
gen = secp256k1.generator_generate_blinded(self.asset, self.asset_blinding_factor)
|
||||
if self.asset_commitment != secp256k1.generator_serialize(gen):
|
||||
if not self.asset_blinding_factor and not self.asset_proof:
|
||||
raise e
|
||||
gen = secp256k1.generator_parse(self.asset_commitment)
|
||||
# we have blinding factor
|
||||
if self.asset_blinding_factor:
|
||||
if gen != secp256k1.generator_generate_blinded(self.asset, self.asset_blinding_factor):
|
||||
raise e
|
||||
# otherwise use asset proof
|
||||
else:
|
||||
surj_proof = secp256k1.surjectionproof_parse(self.asset_proof)
|
||||
gen_asset = secp256k1.generator_generate(self.asset)
|
||||
if not secp256k1.surjectionproof_verify(surj_proof, [gen_asset], gen):
|
||||
raise e
|
||||
|
||||
if self.value and self.value_commitment:
|
||||
if gen is None:
|
||||
raise e
|
||||
value_commitment = secp256k1.pedersen_commit(self.value_blinding_factor, self.value, gen)
|
||||
if self.value_commitment != secp256k1.pedersen_commitment_serialize(value_commitment):
|
||||
if not gen or not (self.value_blinding_factor or self.value_proof):
|
||||
raise e
|
||||
# we have blinding factor
|
||||
if self.value_blinding_factor:
|
||||
value_commitment = secp256k1.pedersen_commit(self.value_blinding_factor, self.value, gen)
|
||||
if self.value_commitment != secp256k1.pedersen_commitment_serialize(value_commitment):
|
||||
raise e
|
||||
# otherwise use value proof
|
||||
else:
|
||||
value_commitment = secp256k1.pedersen_commitment_parse(self.value_commitment)
|
||||
min_value, max_value = secp256k1.rangeproof_verify(
|
||||
self.value_proof,
|
||||
value_commitment,
|
||||
b"",
|
||||
gen,
|
||||
)
|
||||
if (min_value != max_value) or (self.value != min_value):
|
||||
raise e
|
||||
self._verified = True
|
||||
return self._verified
|
||||
|
||||
@ -162,6 +185,8 @@ class LOutputScope(OutputScope):
|
||||
self.surjection_proof = None
|
||||
self.value_blinding_factor = None
|
||||
self.asset_blinding_factor = None
|
||||
self.asset_proof = None
|
||||
self.value_proof = None
|
||||
if self.value_commitment:
|
||||
self.value = None
|
||||
if self.asset_commitment:
|
||||
@ -244,6 +269,10 @@ class LOutputScope(OutputScope):
|
||||
self.ecdh_pubkey = v
|
||||
elif k == b"\xfc\x04pset\x08":
|
||||
self.blinder_index = int.from_bytes(v, 'little')
|
||||
elif k == b'\xfc\x04pset\x09':
|
||||
self.value_proof = v
|
||||
elif k == b'\xfc\x04pset\x0a':
|
||||
self.asset_proof = v
|
||||
else:
|
||||
self.unknown[k] = v
|
||||
|
||||
@ -305,6 +334,12 @@ class LOutputScope(OutputScope):
|
||||
if self.blinder_index is not None:
|
||||
r += ser_string(stream, b"\xfc\x04pset\x08")
|
||||
r += ser_string(stream, self.blinder_index.to_bytes(4, 'little'))
|
||||
if self.value_proof is not None:
|
||||
r += ser_string(stream, b"\xfc\x04pset\x09")
|
||||
r += ser_string(stream, self.value_proof)
|
||||
if self.asset_proof is not None:
|
||||
r += ser_string(stream, b"\xfc\x04pset\x0a")
|
||||
r += ser_string(stream, self.asset_proof)
|
||||
# separator
|
||||
if not skip_separator:
|
||||
r += stream.write(b"\x00")
|
||||
@ -390,6 +425,24 @@ class PSET(PSBT):
|
||||
rangeproof_nonce = hashes.tagged_hash("liquid/range_proof", txseed+i.to_bytes(4,'little'))
|
||||
out.reblind(rangeproof_nonce)
|
||||
|
||||
# generate asset proof
|
||||
gen_asset = secp256k1.generator_generate(out.asset)
|
||||
proof, idx = secp256k1.surjectionproof_initialize([out.asset], out.asset, b"\x00"*32, 1, 1)
|
||||
proof = secp256k1.surjectionproof_generate(proof, idx, [gen_asset], gen, b"\x00"*32, out.asset_blinding_factor)
|
||||
out.asset_proof = secp256k1.surjectionproof_serialize(proof)
|
||||
|
||||
# generate value proof
|
||||
value_proof_nonce = hashes.tagged_hash("liquid/value_proof", txseed+i.to_bytes(4,'little'))
|
||||
out.value_proof = secp256k1.rangeproof_sign(
|
||||
value_proof_nonce, out.value, value_commitment,
|
||||
out.value_blinding_factor, b"",
|
||||
b"", gen,
|
||||
out.value, # min_value
|
||||
-1, # exp
|
||||
0, # min bits
|
||||
)
|
||||
|
||||
|
||||
def fee(self):
|
||||
fee = 0
|
||||
for out in self.tx.vout:
|
||||
|
||||
@ -297,13 +297,37 @@ def _init(flags=(CONTEXT_SIGN | CONTEXT_VERIFY)):
|
||||
secp256k1.secp256k1_pedersen_verify_tally.restype = c_int
|
||||
|
||||
# rangeproof
|
||||
secp256k1.secp256k1_rangeproof_rewind.argtypes = [c_void_p, c_char_p, POINTER(c_uint64), c_char_p, POINTER(c_uint64),
|
||||
c_char_p, POINTER(c_uint64), POINTER(c_uint64),
|
||||
c_char_p, c_char_p, c_uint64,
|
||||
c_char_p, c_uint64,
|
||||
c_char_p]
|
||||
secp256k1.secp256k1_rangeproof_rewind.argtypes = [
|
||||
c_void_p, # ctx
|
||||
c_char_p, # vbf out
|
||||
POINTER(c_uint64), # value out
|
||||
c_char_p, # message out
|
||||
POINTER(c_uint64), # msg out len
|
||||
c_char_p, # nonce
|
||||
POINTER(c_uint64), # min value
|
||||
POINTER(c_uint64), # max value
|
||||
c_char_p, # pedersen commitment
|
||||
c_char_p, # range proof
|
||||
c_uint64, # proof len
|
||||
c_char_p, # extra commitment (scriptpubkey)
|
||||
c_uint64, # extra len
|
||||
c_char_p, # generator
|
||||
]
|
||||
secp256k1.secp256k1_rangeproof_rewind.restype = c_int
|
||||
|
||||
secp256k1.secp256k1_rangeproof_verify.argtypes = [
|
||||
c_void_p, # ctx
|
||||
POINTER(c_uint64), # min value
|
||||
POINTER(c_uint64), # max value
|
||||
c_char_p, # pedersen commitment
|
||||
c_char_p, # proof
|
||||
c_uint64, # proof len
|
||||
c_char_p, # extra
|
||||
c_uint64, # extra len
|
||||
c_char_p, # generator
|
||||
]
|
||||
secp256k1.secp256k1_rangeproof_verify.restype = c_int
|
||||
|
||||
secp256k1.secp256k1_rangeproof_sign.argtypes = [
|
||||
c_void_p, # ctx
|
||||
c_char_p, # proof
|
||||
@ -836,8 +860,9 @@ def rangeproof_rewind(proof, nonce, value_commitment, script_pubkey, generator,
|
||||
if len(value_commitment)!=64:
|
||||
raise ValueError("Value commitment should be 64 bytes long")
|
||||
|
||||
msg = b"\x00"*message_length
|
||||
pointer = POINTER(c_uint64)
|
||||
|
||||
msg = b"\x00"*message_length
|
||||
msglen = pointer(c_uint64(len(msg)))
|
||||
|
||||
vbf_out = b"\x00"*32
|
||||
@ -854,6 +879,31 @@ def rangeproof_rewind(proof, nonce, value_commitment, script_pubkey, generator,
|
||||
raise RuntimeError("Failed to rewind the proof")
|
||||
return value_out.contents.value, vbf_out, msg[:msglen.contents.value], min_value.contents.value, max_value.contents.value
|
||||
|
||||
# rangeproof
|
||||
|
||||
@locked
|
||||
def rangeproof_verify(proof, value_commitment, script_pubkey, generator, context=_secp.ctx):
|
||||
if len(generator)!=64:
|
||||
raise ValueError("Generator should be 64 bytes long")
|
||||
if len(value_commitment)!=64:
|
||||
raise ValueError("Value commitment should be 64 bytes long")
|
||||
|
||||
pointer = POINTER(c_uint64)
|
||||
min_value = pointer(c_uint64(0))
|
||||
max_value = pointer(c_uint64(0))
|
||||
res = _secp.secp256k1_rangeproof_verify(
|
||||
context,
|
||||
min_value,
|
||||
max_value,
|
||||
value_commitment,
|
||||
proof, len(proof),
|
||||
script_pubkey, len(script_pubkey),
|
||||
generator,
|
||||
)
|
||||
if res != 1:
|
||||
raise RuntimeError("Failed to verify the proof")
|
||||
return min_value.contents.value, max_value.contents.value
|
||||
|
||||
@locked
|
||||
def rangeproof_sign(nonce, value, value_commitment, vbf, message, extra, gen, min_value=1, exp=0, min_bits=52, context=_secp.ctx):
|
||||
if value == 0:
|
||||
|
||||
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user