7 from __future__
import annotations
12 OwnershipKeyPair, ownership_key_as_hex
14 EncryptionPublicKey, EncryptionSecretKey, InvalidSignature, \
15 generate_encryption_keypair, encrypt, decrypt
23 int64_to_hex, message_to_bytes, eth_address_to_bytes32, to_zeth_units, \
24 get_contracts_dir, hex_to_uint256_list
26 import zeth.api.zeth_messages_pb2
as api
31 from Crypto
import Random
32 from hashlib
import blake2s, sha256
35 from typing
import Tuple, Dict, List, Iterator, Callable, Optional, Any
38 ZERO_UNITS_HEX =
"0000000000000000"
39 ZERO_ADDRESS =
"0x0000000000000000000000000000000000000000"
46 ComputeHSigCB = Callable[[List[bytes], JoinsplitSigVerificationKey], bytes]
51 High-level description of a call to the mixer contract. Holds information
52 used when generating the ProofInputs, ZK-proof and final MixParameters
58 sender_ownership_keypair: OwnershipKeyPair,
59 inputs: List[Tuple[int, api.ZethNote]],
60 outputs: List[Tuple[ZethAddressPub, EtherValue]],
63 compute_h_sig_cb: Optional[ComputeHSigCB] =
None):
64 assert len(inputs) <= constants.JS_INPUTS
65 assert len(outputs) <= constants.JS_OUTPUTS
79 sender_a_pk = sender_ownership_keypair.a_pk
83 for _
in range(constants.JS_INPUTS - len(inputs))]
86 if len(outputs) < constants.JS_OUTPUTS:
92 for _
in range(constants.JS_OUTPUTS - len(outputs))]
99 All data required to call the mixer, with no further processing required
100 (except creation of a transaction). This is the result of fully processing
101 a MixCallDescription, generating appropriate secret data and ZK-proof and
106 extended_proof: ExtendedProof,
107 public_data: List[int],
109 signature: signing.Signature,
110 ciphertexts: List[bytes]):
118 def from_json(zksnark: IZKSnarkProvider, params_json: str) -> MixParameters:
119 return MixParameters.from_json_dict(zksnark, json.loads(params_json))
127 signature_vk_json = [
129 signing.verification_key_as_mix_parameter(self.
signature_vk)]
130 signature_json =
str(signing.signature_as_mix_parameter(self.
signature))
131 ciphertexts_json = [x.hex()
for x
in self.
ciphertexts]
133 "extended_proof": ext_proof_json,
134 "public_data": public_data,
135 "signature_vk": signature_vk_json,
136 "signature": signature_json,
137 "ciphertexts": ciphertexts_json,
142 zksnark: IZKSnarkProvider,
143 json_dict: Dict[str, Any]) -> MixParameters:
144 ext_proof = ExtendedProof.from_json_dict(
145 zksnark, json_dict[
"extended_proof"])
146 public_data = [
int(x, 16)
for x
in json_dict[
"public_data"]]
147 signature_pk_param = [
int(x)
for x
in json_dict[
"signature_vk"]]
148 signature_pk = signing.verification_key_from_mix_parameter(
150 signature = signing.signature_from_mix_parameter(
151 int(json_dict[
"signature"]))
152 ciphertexts = [bytes.fromhex(x)
for x
in json_dict[
"ciphertexts"]]
154 ext_proof, public_data, signature_pk, signature, ciphertexts)
158 zksnark: IZKSnarkProvider,
159 pp: PairingParameters,
160 mix_parameters: MixParameters) -> List[Any]:
162 Convert MixParameters to a list of eth ABI objects which can be passed to
163 the contract's mix method.
165 proof_contract_params = zksnark.proof_to_contract_parameters(
166 mix_parameters.extended_proof.proof, pp)
168 proof_contract_params,
169 signing.verification_key_as_mix_parameter(mix_parameters.signature_vk),
170 signing.signature_as_mix_parameter(mix_parameters.signature),
171 mix_parameters.public_data,
172 mix_parameters.ciphertexts,
178 Encode parameters from mix_parameters into an array of uint256 values,
179 compatible with the `dispatch` method on Mixer. This conforms to the
180 `IZecaleApplication` solidity interface of Zecale
181 (https://github.com/clearmatics/zecale)
183 vk_param = signing.verification_key_as_mix_parameter(
184 mix_parameters.signature_vk)
185 sigma_param = signing.signature_as_mix_parameter(mix_parameters.signature)
186 public_data = mix_parameters.public_data
187 ciphertexts = mix_parameters.ciphertexts
188 return eth_abi.encode_abi(
189 [
'uint256[4]',
'uint256',
'uint256[]',
'bytes[]'],
190 [vk_param, sigma_param, public_data, ciphertexts])
195 Event data for a single joinsplit output. Holds address (in merkle tree),
196 commitment and ciphertext.
199 self, commitment: bytes, ciphertext: bytes):
206 Data structure representing the result of the mix call.
210 new_merkle_root: bytes,
211 nullifiers: List[bytes],
212 output_events: List[MixOutputEvents]):
219 mix_out_args = zip(event_args.commitments, event_args.ciphertexts)
222 new_merkle_root=event_args.root,
223 nullifiers=event_args.nullifiers,
224 output_events=out_events)
228 merkle_path: List[str],
231 a_sk: OwnershipSecretKey,
232 nullifier: bytes) -> api.JoinsplitInput:
233 return api.JoinsplitInput(
234 merkle_path=merkle_path,
238 nullifier=nullifier.hex())
242 a_pk: OwnershipPublicKey) -> Tuple[int, api.ZethNote]:
244 Create a zeth note and address, for use as circuit inputs where there is no
247 dummy_note = api.ZethNote(
249 value=ZERO_UNITS_HEX,
250 rho=_get_dummy_rho(),
251 trap_r=_trap_r_randomness())
255 dummy_note_address = 0
256 return (dummy_note_address, dummy_note)
261 Interface to operations on the Mixer contract.
266 prover_config: ProverConfiguration,
267 mixer_instance: Any):
275 prover_client: ProverClient,
276 deployer_eth_address: str,
277 deployer_eth_private_key: Optional[bytes],
278 token_address: Optional[str] =
None,
279 permitted_dispatcher: Optional[str] =
None,
280 vk_hash: Optional[str] =
None,
281 deploy_gas: Optional[int] =
None
284 Deploy Zeth contracts.
286 prover_config = prover_client.get_configuration()
287 vk = prover_client.get_verification_key()
291 pp = prover_config.pairing_parameters
292 mixer_name = zksnark.get_contract_name(pp)
293 mixer_src = os.path.join(contracts_dir, mixer_name +
".sol")
295 assert len(vk_hash_evm) == 2
301 constructor_parameters: List[Any] = [
302 constants.ZETH_MERKLE_TREE_DEPTH,
303 token_address
or ZERO_ADDRESS,
304 zksnark.verification_key_to_contract_parameters(vk, pp),
305 permitted_dispatcher
or ZERO_ADDRESS,
308 mixer_description = contracts.InstanceDescription.deploy(
312 deployer_eth_address,
313 deployer_eth_private_key,
316 args=constructor_parameters)
317 mixer_instance = mixer_description.instantiate(web3)
318 client =
MixerClient(web3, prover_config, mixer_instance)
319 return client, mixer_description
323 prover_client: ProverClient,
325 zeth_address: ZethAddress,
326 sender_eth_address: str,
327 sender_eth_private_key: Optional[bytes],
328 eth_amount: EtherValue,
329 outputs: Optional[List[Tuple[ZethAddressPub, EtherValue]]] =
None,
330 tx_value: Optional[EtherValue] =
None
332 if not outputs
or len(outputs) == 0:
333 outputs = [(zeth_address.addr_pk, eth_amount)]
337 sender_ownership_keypair=zeth_address.ownership_keypair(),
338 sender_eth_address=sender_eth_address,
339 sender_eth_private_key=sender_eth_private_key,
348 prover_client: ProverClient,
350 sender_ownership_keypair: OwnershipKeyPair,
351 sender_eth_address: str,
352 sender_eth_private_key: Optional[bytes],
353 inputs: List[Tuple[int, api.ZethNote]],
354 outputs: List[Tuple[ZethAddressPub, EtherValue]],
357 tx_value: Optional[EtherValue] =
None,
358 compute_h_sig_cb: Optional[ComputeHSigCB] =
None) -> str:
360 Create and broadcast a transactions that calls the mixer with the given
361 parameters. Requires a ProverClient for proof generation.
366 sender_ownership_keypair,
376 sender_eth_private_key,
381 mix_params: MixParameters,
382 sender_eth_address: str,
383 sender_eth_private_key: Optional[bytes],
384 tx_value: EtherValue,
385 call_gas: Optional[int] =
None) -> str:
387 Given a MixParameters object, create and broadcast a transaction
388 performing the appropriate mix call.
391 tx_hash = contracts.send_contract_call(
395 sender_eth_private_key,
402 mix_params: MixParameters,
403 sender_eth_address: str,
404 tx_value: EtherValue,
405 call_gas: Optional[int] =
None) -> bool:
407 Call the mix method (executes on the RPC host without creating a
408 transaction). Returns True if the call succeeds. False, otherwise.
412 contracts.local_contract_call(
420 print(
"error executing mix call:")
421 traceback.print_exc()
425 def _create_mix_call(
427 mix_parameters: MixParameters) -> Any:
429 Given a MixParameters object and other transaction properties, create a
430 web3 call object, which can be used to create a transaction or a query.
435 zksnark, pp, mix_parameters)
440 mix_call_desc: MixCallDescription
443 Given the basic parameters for a mix call, compute the input to the prover
444 server, and the signing key pair.
448 mk_tree = mix_call_desc.mk_tree
449 sender_ask = mix_call_desc.sender_ownership_keypair.a_sk
451 def _create_api_input(
453 input_note: api.ZethNote) -> api.JoinsplitInput:
463 inputs = mix_call_desc.inputs
464 api_inputs = [_create_api_input(addr, note)
for addr, note
in inputs]
466 mk_root = mk_tree.get_root()
469 outputs_with_a_pk = \
471 for (zeth_addr, value)
in mix_call_desc.outputs]
474 public_in_value_zeth_units =
to_zeth_units(mix_call_desc.v_in)
475 public_out_value_zeth_units =
to_zeth_units(mix_call_desc.v_out)
478 signing_keypair = signing.gen_signing_keypair()
481 compute_h_sig_cb = mix_call_desc.compute_h_sig_cb
or compute_h_sig
482 h_sig = compute_h_sig_cb(
483 [bytes.fromhex(input.nullifier)
for input
in api_inputs],
485 phi = _phi_randomness()
488 api_outputs = _create_api_zeth_notes(phi, h_sig, outputs_with_a_pk)
490 proof_inputs = api.ProofInputs(
491 mk_root=mk_root.hex(),
492 js_inputs=api_inputs,
493 js_outputs=api_outputs,
495 pub_out_value=
int64_to_hex(public_out_value_zeth_units),
498 return (proof_inputs, signing_keypair)
502 mix_call_desc: MixCallDescription,
503 prover_inputs: api.ProofInputs,
505 ext_proof: ExtendedProof,
506 public_data: List[int],
507 sender_eth_address: str,
508 for_dispatch_call: bool =
False
511 Create the MixParameters from MixCallDescription, signing keypair, sender
512 address and derived data (prover inputs and proof). This includes
513 creating and encrypting the plaintext messages, and generating the
516 If for_dispatch_call is set, the parameters are to be passed to the
517 Mixer's `dispatch` call in a later operation (in which proof data is
518 not available), hence proof is omitted from the signature.
522 outputs_and_notes = zip(mix_call_desc.outputs, prover_inputs.js_outputs) \
524 output_notes_with_k_pk: List[Tuple[api.ZethNote, EncryptionPublicKey]] = \
525 [(note, zeth_addr.k_pk)
526 for ((zeth_addr, _), note)
in outputs_and_notes]
542 ext_proof, public_data, signing_keypair.vk, signature, ciphertexts)
547 prover_client: ProverClient,
549 sender_ownership_keypair: OwnershipKeyPair,
550 sender_eth_address: str,
551 inputs: List[Tuple[int, api.ZethNote]],
552 outputs: List[Tuple[ZethAddressPub, EtherValue]],
555 compute_h_sig_cb: Optional[ComputeHSigCB] =
None,
556 for_dispatch_call: bool =
False
557 ) -> Tuple[MixParameters, JoinsplitSigKeyPair]:
559 Convenience function around creation of MixCallDescription, ProofInputs,
560 Proof and MixParameters. If for_dispatch_call is set, the parameters
561 are to be passed to the Mixer's `dispatch` call in a later operation
562 (in which proof data is not available), hence proof is omitted from
568 sender_ownership_keypair,
574 assert len(mix_call_desc.inputs) == constants.JS_INPUTS
575 assert len(mix_call_desc.outputs) == constants.JS_OUTPUTS
577 prover_inputs, signing_keypair = MixerClient.create_prover_inputs(
581 assert len(prover_inputs.js_inputs) == constants.JS_INPUTS
582 assert len(prover_inputs.js_outputs) == constants.JS_OUTPUTS
586 ext_proof, public_data = prover_client.get_proof(prover_inputs)
598 return mix_params, signing_keypair
602 notes: List[Tuple[api.ZethNote, EncryptionPublicKey]]) -> List[bytes]:
604 Encrypts a set of output notes to be decrypted by the respective receivers.
605 Returns the ciphertexts corresponding to each note.
609 out_note: api.ZethNote, pub_key: EncryptionPublicKey) -> bytes:
610 out_note_bytes = proto_utils.zeth_note_to_bytes(out_note)
612 return encrypt(out_note_bytes, pub_key)
614 ciphertexts = [_encrypt_note(note, pk)
for (note, pk)
in notes]
619 out_ev: MixOutputEvents,
620 receiver_k_sk: EncryptionSecretKey
621 ) -> Optional[Tuple[bytes, api.ZethNote]]:
623 Given the receivers secret key, and the event data from a transaction
624 (encrypted notes), decrypt any that are intended for the receiver. Return
625 tuples `(<address-in-merkle-tree>, ZethNote)`. Callers should record the
626 address-in-merkle-tree along with ZethNote information, for convenience
627 when spending the notes.
630 plaintext =
decrypt(out_ev.ciphertext, receiver_k_sk)
633 proto_utils.zeth_note_from_bytes(plaintext))
634 except InvalidSignature:
645 batch_size: Optional[int] =
None) -> Iterator[MixResult]:
647 Iterator for all events generated by 'mix' executions, over some block
648 range (inclusive of `end_block`). Batch eth RPC calls to avoid too many
649 calls, and holding huge lists of events in memory.
651 logs = contracts.get_event_logs(
652 web3, mixer_instance,
"LogMix", start_block, end_block, batch_size)
653 for event_data
in logs:
658 zksnark: IZKSnarkProvider,
659 pp: PairingParameters,
660 signing_keypair: JoinsplitSigKeyPair,
661 sender_eth_address: str,
662 ciphertexts: List[bytes],
663 extproof: ExtendedProof,
664 public_data: List[int],
665 for_dispatch_call: bool =
False) -> int:
667 Generate a signature on the hash of the ciphertexts, proofs and primary
668 inputs. This is used to solve transaction malleability. We chose to sign
669 the hash and not the values themselves for modularity (to use the same code
670 regardless of whether GROTH16 or PGHR13 proof system is chosen), and sign
671 the hash of the ciphers and inputs for consistency. If for_dispatch_call is
672 set, the parameters are to be passed to the Mixer's `dispatch` call in a
673 later operation (in which proof data is not available), hence proof is
674 omitted from the signature.
676 assert len(ciphertexts) == constants.JS_INPUTS
685 for ciphertext
in ciphertexts:
688 proof_bytes, pub_inputs_bytes = _proof_and_inputs_to_bytes(
689 zksnark, pp, extproof, public_data)
693 if not for_dispatch_call:
694 h.update(proof_bytes)
696 h.update(pub_inputs_bytes)
697 message_digest = h.digest()
698 return signing.sign(signing_keypair.sk, message_digest)
703 Used by the recipient of a payment to recompute the commitment and check
704 the membership in the tree to confirm the validity of a payment
708 blake.update(bytes.fromhex(zeth_note.trap_r))
709 blake.update(bytes.fromhex(zeth_note.apk))
710 blake.update(bytes.fromhex(zeth_note.rho))
711 blake.update(bytes.fromhex(zeth_note.value))
714 cm_field = int.from_bytes(cm, byteorder=
"big") % pp.scalar_field_mod()
715 return cm_field.to_bytes(
int(constants.DIGEST_LENGTH/8), byteorder=
"big")
719 zeth_note: api.ZethNote,
720 spending_authority_ask: OwnershipSecretKey) -> bytes:
722 Returns nf = blake2s(1110 || [a_sk]_252 || rho)
725 first_252bits_ask = binary_ask[:252]
726 left_leg_bin =
"1110" + first_252bits_ask
727 left_leg =
int(left_leg_bin, 2).to_bytes(32, byteorder=
'big')
728 blake_hash = blake2s()
729 blake_hash.update(left_leg)
730 blake_hash.update(bytes.fromhex(zeth_note.rho))
731 return blake_hash.digest()
735 nullifiers: List[bytes],
736 sign_vk: JoinsplitSigVerificationKey) -> bytes:
738 Compute h_sig = sha256(nf_1 || ... || nf_{JS_INPUTS} || sign_vk)
741 for nf
in nullifiers:
743 h.update(sign_vk.to_bytes())
747 def _create_api_zeth_notes(
750 outputs: List[Tuple[OwnershipPublicKey, int]]
751 ) -> List[api.ZethNote]:
753 Create ordered list of api.ZethNote objects from the output descriptions.
755 def _create_api_zeth_note(
756 out_index: int, recipient: OwnershipPublicKey, value: int
758 rho = _compute_rho_i(phi, hsig, out_index)
759 trap_r = _trap_r_randomness()
767 _create_api_zeth_note(idx, recipient, value)
768 for idx, (recipient, value)
in enumerate(outputs)]
771 def _proof_and_inputs_to_bytes(
772 snark: IZKSnarkProvider,
773 pp: PairingParameters,
774 extproof: ExtendedProof,
775 public_data: List[int]) -> Tuple[bytes, bytes]:
777 Given a proof object, compute the byte encodings of the properties
778 excluding "inputs", and the byte encoding of the "inputs". These are used
779 when hashing the mixer call parameters for signing, so must match what
780 happens in the mixer contract.
783 proof = extproof.proof
789 def _trap_r_randomness() -> str:
791 Compute randomness `r`
793 assert (constants.TRAPR_LENGTH_BYTES << 3) == constants.TRAPR_LENGTH
794 return bytes(Random.get_random_bytes(constants.TRAPR_LENGTH_BYTES)).hex()
797 def _compute_rho_i(phi: bytes, hsig: bytes, i: int) -> bytes:
800 rho_i = blake2s(0 || i || 10 || phi_truncated || hsig)
801 where i is encoded in the smallest number of bits (index_bits) required to
802 hold values 0, ..., JS_OUTPUTS-1, and phi_truncated is binary
803 representation of phi, truncated to 256 - index_bits - 3 bits.
805 See: Zcash protocol spec p. 57, Section 5.4.2 Pseudo Random Functions
807 assert i < constants.JS_OUTPUTS
814 index_bits = math.ceil(math.log(constants.JS_OUTPUTS, 2))
816 index_bin =
"0" * (index_bits - len(index_bin)) + index_bin
817 assert len(index_bin) == index_bits, \
818 f
"index_bits: {index_bits}, index_bin: {index_bin}, i: {i}"
820 phi_truncated_bits = 256 - 1 - index_bits - 2
822 assert len(phi_truncated_bin) == phi_truncated_bits
824 left_leg_bin =
"0" + index_bin +
"10" + phi_truncated_bin
825 assert len(left_leg_bin) == 256
828 blake_hash = blake2s()
829 blake_hash.update(
int(left_leg_bin, 2).to_bytes(32, byteorder=
'big'))
830 blake_hash.update(hsig)
831 return blake_hash.digest()
834 def _get_dummy_rho() -> str:
835 assert (constants.RHO_LENGTH_BYTES << 3) == constants.RHO_LENGTH
836 return bytes(Random.get_random_bytes(constants.RHO_LENGTH_BYTES)).hex()
839 def _phi_randomness() -> bytes:
841 Compute the transaction randomness "phi", used for computing the new rhoS
843 return bytes(Random.get_random_bytes(constants.PHI_LENGTH_BYTES))