Zeth - Zerocash on Ethereum  0.8
Reference implementation of the Zeth protocol by Clearmatics
mixer_client.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 
3 # Copyright (c) 2015-2022 Clearmatics Technologies Ltd
4 #
5 # SPDX-License-Identifier: LGPL-3.0+
6 
7 from __future__ import annotations
8 from zeth.core import contracts
9 from zeth.core import constants
10 from zeth.core.zeth_address import ZethAddressPub, ZethAddress
11 from zeth.core.ownership import OwnershipPublicKey, OwnershipSecretKey, \
12  OwnershipKeyPair, ownership_key_as_hex
13 from zeth.core.encryption import \
14  EncryptionPublicKey, EncryptionSecretKey, InvalidSignature, \
15  generate_encryption_keypair, encrypt, decrypt
16 from zeth.core.merkle_tree import MerkleTree, compute_merkle_path
17 from zeth.core.pairing import PairingParameters
18 from zeth.core import signing
19 from zeth.core import proto_utils
20 from zeth.core.zksnark import IZKSnarkProvider, get_zksnark_provider, \
21  ExtendedProof
22 from zeth.core.utils import EtherValue, digest_to_binary_string, \
23  int64_to_hex, message_to_bytes, eth_address_to_bytes32, to_zeth_units, \
24  get_contracts_dir, hex_to_uint256_list
25 from zeth.core.prover_client import ProverConfiguration, ProverClient
26 import zeth.api.zeth_messages_pb2 as api
27 
28 import os
29 import json
30 import math
31 from Crypto import Random
32 from hashlib import blake2s, sha256
33 import traceback
34 import eth_abi
35 from typing import Tuple, Dict, List, Iterator, Callable, Optional, Any
36 
37 
38 ZERO_UNITS_HEX = "0000000000000000"
39 ZERO_ADDRESS = "0x0000000000000000000000000000000000000000"
40 
41 # JoinSplit Signature Keys definitions
42 JoinsplitSigVerificationKey = signing.SigningVerificationKey
43 JoinsplitSigSecretKey = signing.SigningSecretKey
44 JoinsplitSigKeyPair = signing.SigningKeyPair
45 
46 ComputeHSigCB = Callable[[List[bytes], JoinsplitSigVerificationKey], bytes]
47 
48 
50  """
51  High-level description of a call to the mixer contract. Holds information
52  used when generating the ProofInputs, ZK-proof and final MixParameters
53  object.
54  """
55  def __init__(
56  self,
57  mk_tree: MerkleTree,
58  sender_ownership_keypair: OwnershipKeyPair,
59  inputs: List[Tuple[int, api.ZethNote]],
60  outputs: List[Tuple[ZethAddressPub, EtherValue]],
61  v_in: EtherValue,
62  v_out: EtherValue,
63  compute_h_sig_cb: Optional[ComputeHSigCB] = None):
64  assert len(inputs) <= constants.JS_INPUTS
65  assert len(outputs) <= constants.JS_OUTPUTS
66 
67  self.mk_tree = mk_tree
68  self.sender_ownership_keypair = sender_ownership_keypair
69  self.v_in = v_in
70  self.v_out = v_out
71  self.compute_h_sig_cb = compute_h_sig_cb
72 
73  # Perform some cleaning and minimal pre-processing of the data. Compute
74  # and store data that is not derivable from the ProverInput or Proof
75  # structs (such as the encryption keys for receivers), making it
76  # available to MixerClient calls.
77 
78  # Expand inputs with dummy entries and compute merkle paths.
79  sender_a_pk = sender_ownership_keypair.a_pk
80  self.inputs = \
81  inputs + \
82  [get_dummy_input_and_address(sender_a_pk)
83  for _ in range(constants.JS_INPUTS - len(inputs))]
84 
85  # Pad the list of outputs if necessary
86  if len(outputs) < constants.JS_OUTPUTS:
87  dummy_k_pk = generate_encryption_keypair().k_pk
88  dummy_addr_pk = ZethAddressPub(sender_a_pk, dummy_k_pk)
89  self.outputs = \
90  outputs + \
91  [(dummy_addr_pk, EtherValue(0))
92  for _ in range(constants.JS_OUTPUTS - len(outputs))]
93  else:
94  self.outputs = outputs
95 
96 
98  """
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
102  signing the result.
103  """
104  def __init__(
105  self,
106  extended_proof: ExtendedProof,
107  public_data: List[int],
109  signature: signing.Signature,
110  ciphertexts: List[bytes]):
111  self.extended_proof = extended_proof
112  self.public_data = public_data
113  self.signature_vk = signature_vk
114  self.signature = signature
115  self.ciphertexts = ciphertexts
116 
117  @staticmethod
118  def from_json(zksnark: IZKSnarkProvider, params_json: str) -> MixParameters:
119  return MixParameters.from_json_dict(zksnark, json.loads(params_json))
120 
121  def to_json(self) -> str:
122  return json.dumps(self.to_json_dict())
123 
124  def to_json_dict(self) -> Dict[str, Any]:
125  ext_proof_json = self.extended_proof.to_json_dict()
126  public_data = [hex(x) for x in self.public_data]
127  signature_vk_json = [
128  str(x) for x in
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]
132  return {
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,
138  }
139 
140  @staticmethod
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(
149  signature_pk_param)
150  signature = signing.signature_from_mix_parameter(
151  int(json_dict["signature"]))
152  ciphertexts = [bytes.fromhex(x) for x in json_dict["ciphertexts"]]
153  return MixParameters(
154  ext_proof, public_data, signature_pk, signature, ciphertexts)
155 
156 
158  zksnark: IZKSnarkProvider,
159  pp: PairingParameters,
160  mix_parameters: MixParameters) -> List[Any]:
161  """
162  Convert MixParameters to a list of eth ABI objects which can be passed to
163  the contract's mix method.
164  """
165  proof_contract_params = zksnark.proof_to_contract_parameters(
166  mix_parameters.extended_proof.proof, pp)
167  return [
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,
173  ]
174 
175 
176 def mix_parameters_to_dispatch_parameters(mix_parameters: MixParameters) -> bytes:
177  """
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)
182  """
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]) # type: ignore
191 
192 
194  """
195  Event data for a single joinsplit output. Holds address (in merkle tree),
196  commitment and ciphertext.
197  """
198  def __init__(
199  self, commitment: bytes, ciphertext: bytes):
200  self.commitment = commitment
201  self.ciphertext = ciphertext
202 
203 
204 class MixResult:
205  """
206  Data structure representing the result of the mix call.
207  """
208  def __init__(
209  self,
210  new_merkle_root: bytes,
211  nullifiers: List[bytes],
212  output_events: List[MixOutputEvents]):
213  self.new_merkle_root = new_merkle_root
214  self.nullifiers = nullifiers
215  self.output_events = output_events
216 
217 
218 def event_args_to_mix_result(event_args: Any) -> MixResult:
219  mix_out_args = zip(event_args.commitments, event_args.ciphertexts)
220  out_events = [MixOutputEvents(c, ciph) for (c, ciph) in mix_out_args]
221  return MixResult(
222  new_merkle_root=event_args.root,
223  nullifiers=event_args.nullifiers,
224  output_events=out_events)
225 
226 
228  merkle_path: List[str],
229  address: int,
230  note: api.ZethNote,
231  a_sk: OwnershipSecretKey,
232  nullifier: bytes) -> api.JoinsplitInput:
233  return api.JoinsplitInput(
234  merkle_path=merkle_path,
235  address=address,
236  note=note,
237  spending_ask=ownership_key_as_hex(a_sk),
238  nullifier=nullifier.hex())
239 
240 
242  a_pk: OwnershipPublicKey) -> Tuple[int, api.ZethNote]:
243  """
244  Create a zeth note and address, for use as circuit inputs where there is no
245  real input.
246  """
247  dummy_note = api.ZethNote(
248  apk=ownership_key_as_hex(a_pk),
249  value=ZERO_UNITS_HEX,
250  rho=_get_dummy_rho(),
251  trap_r=_trap_r_randomness())
252  # Note that the Merkle path is not fully checked against the root by the
253  # circuit since the note value is 0. Hence the address used here is
254  # arbitrary.
255  dummy_note_address = 0
256  return (dummy_note_address, dummy_note)
257 
258 
260  """
261  Interface to operations on the Mixer contract.
262  """
263  def __init__(
264  self,
265  web3: Any,
266  prover_config: ProverConfiguration,
267  mixer_instance: Any):
268  self.web3 = web3
269  self.prover_config = prover_config
270  self.mixer_instance = mixer_instance
271 
272  @staticmethod
273  def deploy(
274  web3: 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
282  ) -> Tuple[MixerClient, contracts.InstanceDescription]:
283  """
284  Deploy Zeth contracts.
285  """
286  prover_config = prover_client.get_configuration()
287  vk = prover_client.get_verification_key()
288 
289  contracts_dir = get_contracts_dir()
290  zksnark = get_zksnark_provider(prover_config.zksnark_name)
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")
294  vk_hash_evm = list(hex_to_uint256_list(vk_hash)) if vk_hash else [0, 0]
295  assert len(vk_hash_evm) == 2
296 
297  # Constructor parameters have the form:
298  # uint256 mk_depth
299  # address token
300  # ... snark-specific key data ...
301  constructor_parameters: List[Any] = [
302  constants.ZETH_MERKLE_TREE_DEPTH, # mk_depth
303  token_address or ZERO_ADDRESS, # token
304  zksnark.verification_key_to_contract_parameters(vk, pp), # vk
305  permitted_dispatcher or ZERO_ADDRESS, # permitted_dispatcher
306  vk_hash_evm # vk_hash
307  ]
308  mixer_description = contracts.InstanceDescription.deploy(
309  web3,
310  mixer_src,
311  mixer_name,
312  deployer_eth_address,
313  deployer_eth_private_key,
314  deploy_gas,
315  compiler_flags={},
316  args=constructor_parameters)
317  mixer_instance = mixer_description.instantiate(web3)
318  client = MixerClient(web3, prover_config, mixer_instance)
319  return client, mixer_description
320 
321  def deposit(
322  self,
323  prover_client: ProverClient,
324  mk_tree: MerkleTree,
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
331  ) -> str:
332  if not outputs or len(outputs) == 0:
333  outputs = [(zeth_address.addr_pk, eth_amount)]
334  return self.joinsplit(
335  prover_client,
336  mk_tree,
337  sender_ownership_keypair=zeth_address.ownership_keypair(),
338  sender_eth_address=sender_eth_address,
339  sender_eth_private_key=sender_eth_private_key,
340  inputs=[],
341  outputs=outputs,
342  v_in=eth_amount,
343  v_out=EtherValue(0),
344  tx_value=tx_value)
345 
347  self,
348  prover_client: ProverClient,
349  mk_tree: MerkleTree,
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]],
355  v_in: EtherValue,
356  v_out: EtherValue,
357  tx_value: Optional[EtherValue] = None,
358  compute_h_sig_cb: Optional[ComputeHSigCB] = None) -> str:
359  """
360  Create and broadcast a transactions that calls the mixer with the given
361  parameters. Requires a ProverClient for proof generation.
362  """
363  mix_params, _ = self.create_mix_parameters_and_signing_key(
364  prover_client,
365  mk_tree,
366  sender_ownership_keypair,
367  sender_eth_address,
368  inputs,
369  outputs,
370  v_in,
371  v_out,
372  compute_h_sig_cb)
373  return self.mix(
374  mix_params,
375  sender_eth_address,
376  sender_eth_private_key,
377  tx_value or v_in)
378 
379  def mix(
380  self,
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:
386  """
387  Given a MixParameters object, create and broadcast a transaction
388  performing the appropriate mix call.
389  """
390  mixer_call = self._create_mix_call(mix_params)
391  tx_hash = contracts.send_contract_call(
392  self.web3,
393  mixer_call,
394  sender_eth_address,
395  sender_eth_private_key,
396  tx_value,
397  call_gas)
398  return tx_hash.hex()
399 
400  def mix_call(
401  self,
402  mix_params: MixParameters,
403  sender_eth_address: str,
404  tx_value: EtherValue,
405  call_gas: Optional[int] = None) -> bool:
406  """
407  Call the mix method (executes on the RPC host without creating a
408  transaction). Returns True if the call succeeds. False, otherwise.
409  """
410  mixer_call = self._create_mix_call(mix_params)
411  try:
412  contracts.local_contract_call(
413  mixer_call,
414  sender_eth_address,
415  tx_value,
416  call_gas)
417  return True
418 
419  except ValueError:
420  print("error executing mix call:")
421  traceback.print_exc()
422 
423  return False
424 
425  def _create_mix_call(
426  self,
427  mix_parameters: MixParameters) -> Any:
428  """
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.
431  """
432  zksnark = get_zksnark_provider(self.prover_config.zksnark_name)
433  pp = self.prover_config.pairing_parameters
434  mix_params_eth = mix_parameters_to_contract_arguments(
435  zksnark, pp, mix_parameters)
436  return self.mixer_instance.functions.mix(*mix_params_eth)
437 
438  @staticmethod
440  mix_call_desc: MixCallDescription
441  ) -> Tuple[api.ProofInputs, signing.SigningKeyPair]:
442  """
443  Given the basic parameters for a mix call, compute the input to the prover
444  server, and the signing key pair.
445  """
446 
447  # Compute Merkle paths
448  mk_tree = mix_call_desc.mk_tree
449  sender_ask = mix_call_desc.sender_ownership_keypair.a_sk
450 
451  def _create_api_input(
452  input_address: int,
453  input_note: api.ZethNote) -> api.JoinsplitInput:
454  mk_path = compute_merkle_path(input_address, mk_tree)
455  input_nullifier = compute_nullifier(input_note, sender_ask)
457  mk_path,
458  input_address,
459  input_note,
460  sender_ask,
461  input_nullifier)
462 
463  inputs = mix_call_desc.inputs
464  api_inputs = [_create_api_input(addr, note) for addr, note in inputs]
465 
466  mk_root = mk_tree.get_root()
467 
468  # Extract (<ownership-address>, <value>) tuples
469  outputs_with_a_pk = \
470  [(zeth_addr.a_pk, to_zeth_units(value))
471  for (zeth_addr, value) in mix_call_desc.outputs]
472 
473  # Public input and output values as Zeth units
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)
476 
477  # Generate the signing key
478  signing_keypair = signing.gen_signing_keypair()
479 
480  # Use the specified or default h_sig computation
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],
484  signing_keypair.vk)
485  phi = _phi_randomness()
486 
487  # Create the api.ZethNote objects
488  api_outputs = _create_api_zeth_notes(phi, h_sig, outputs_with_a_pk)
489 
490  proof_inputs = api.ProofInputs(
491  mk_root=mk_root.hex(),
492  js_inputs=api_inputs,
493  js_outputs=api_outputs,
494  pub_in_value=int64_to_hex(public_in_value_zeth_units),
495  pub_out_value=int64_to_hex(public_out_value_zeth_units),
496  h_sig=h_sig.hex(),
497  phi=phi.hex())
498  return (proof_inputs, signing_keypair)
499 
501  self,
502  mix_call_desc: MixCallDescription,
503  prover_inputs: api.ProofInputs,
504  signing_keypair: signing.SigningKeyPair,
505  ext_proof: ExtendedProof,
506  public_data: List[int],
507  sender_eth_address: str,
508  for_dispatch_call: bool = False
509  ) -> MixParameters:
510  """
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
514  one-time signature.
515 
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.
519  """
520 
521  # Encrypt the notes
522  outputs_and_notes = zip(mix_call_desc.outputs, prover_inputs.js_outputs) \
523  # pylint: disable=no-member
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]
527  ciphertexts = encrypt_notes(output_notes_with_k_pk)
528 
529  # Sign
530  zksnark = get_zksnark_provider(self.prover_config.zksnark_name)
531  signature = joinsplit_sign(
532  zksnark,
533  self.prover_config.pairing_parameters,
534  signing_keypair,
535  sender_eth_address,
536  ciphertexts,
537  ext_proof,
538  public_data,
539  for_dispatch_call)
540 
541  mix_params = MixParameters(
542  ext_proof, public_data, signing_keypair.vk, signature, ciphertexts)
543  return mix_params
544 
546  self,
547  prover_client: ProverClient,
548  mk_tree: MerkleTree,
549  sender_ownership_keypair: OwnershipKeyPair,
550  sender_eth_address: str,
551  inputs: List[Tuple[int, api.ZethNote]],
552  outputs: List[Tuple[ZethAddressPub, EtherValue]],
553  v_in: EtherValue,
554  v_out: EtherValue,
555  compute_h_sig_cb: Optional[ComputeHSigCB] = None,
556  for_dispatch_call: bool = False
557  ) -> Tuple[MixParameters, JoinsplitSigKeyPair]:
558  """
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
563  the signature.
564  """
565  # Generate prover inputs and signing key
566  mix_call_desc = MixCallDescription(
567  mk_tree,
568  sender_ownership_keypair,
569  inputs,
570  outputs,
571  v_in,
572  v_out,
573  compute_h_sig_cb)
574  assert len(mix_call_desc.inputs) == constants.JS_INPUTS
575  assert len(mix_call_desc.outputs) == constants.JS_OUTPUTS
576 
577  prover_inputs, signing_keypair = MixerClient.create_prover_inputs(
578  mix_call_desc)
579 
580  # pylint: disable=no-member
581  assert len(prover_inputs.js_inputs) == constants.JS_INPUTS
582  assert len(prover_inputs.js_outputs) == constants.JS_OUTPUTS
583  # pylint: enable=no-member
584 
585  # Query the prover-server for the related proof
586  ext_proof, public_data = prover_client.get_proof(prover_inputs)
587 
588  # Create the final MixParameters object
589  mix_params = self.create_mix_parameters_from_proof(
590  mix_call_desc,
591  prover_inputs,
592  signing_keypair,
593  ext_proof,
594  public_data,
595  sender_eth_address,
596  for_dispatch_call)
597 
598  return mix_params, signing_keypair
599 
600 
602  notes: List[Tuple[api.ZethNote, EncryptionPublicKey]]) -> List[bytes]:
603  """
604  Encrypts a set of output notes to be decrypted by the respective receivers.
605  Returns the ciphertexts corresponding to each note.
606  """
607 
608  def _encrypt_note(
609  out_note: api.ZethNote, pub_key: EncryptionPublicKey) -> bytes:
610  out_note_bytes = proto_utils.zeth_note_to_bytes(out_note)
611 
612  return encrypt(out_note_bytes, pub_key)
613 
614  ciphertexts = [_encrypt_note(note, pk) for (note, pk) in notes]
615  return ciphertexts
616 
617 
619  out_ev: MixOutputEvents,
620  receiver_k_sk: EncryptionSecretKey
621 ) -> Optional[Tuple[bytes, api.ZethNote]]:
622  """
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.
628  """
629  try:
630  plaintext = decrypt(out_ev.ciphertext, receiver_k_sk)
631  return (
632  out_ev.commitment,
633  proto_utils.zeth_note_from_bytes(plaintext))
634  except InvalidSignature:
635  return None
636  except ValueError:
637  return None
638 
639 
641  web3: Any,
642  mixer_instance: Any,
643  start_block: int,
644  end_block: int,
645  batch_size: Optional[int] = None) -> Iterator[MixResult]:
646  """
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.
650  """
651  logs = contracts.get_event_logs(
652  web3, mixer_instance, "LogMix", start_block, end_block, batch_size)
653  for event_data in logs:
654  yield event_args_to_mix_result(event_data.args)
655 
656 
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:
666  """
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.
675  """
676  assert len(ciphertexts) == constants.JS_INPUTS
677 
678  # The message to sign consists of (in order):
679  # - senders Ethereum address
680  # - ciphertexts
681  # - proof elements (if for_dispatch_call is False)
682  # - public input elements
683  h = sha256()
684  h.update(eth_address_to_bytes32(sender_eth_address))
685  for ciphertext in ciphertexts:
686  h.update(ciphertext)
687 
688  proof_bytes, pub_inputs_bytes = _proof_and_inputs_to_bytes(
689  zksnark, pp, extproof, public_data)
690 
691  # If for_dispatch_call is set, omit proof from the signature. See
692  # AbstractMixer.sol.
693  if not for_dispatch_call:
694  h.update(proof_bytes)
695 
696  h.update(pub_inputs_bytes)
697  message_digest = h.digest()
698  return signing.sign(signing_keypair.sk, message_digest)
699 
700 
701 def compute_commitment(zeth_note: api.ZethNote, pp: PairingParameters) -> bytes:
702  """
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
705  """
706  # inner_k = blake2s(r || a_pk || rho || v)
707  blake = blake2s()
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))
712  cm = blake.digest()
713 
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")
716 
717 
719  zeth_note: api.ZethNote,
720  spending_authority_ask: OwnershipSecretKey) -> bytes:
721  """
722  Returns nf = blake2s(1110 || [a_sk]_252 || rho)
723  """
724  binary_ask = digest_to_binary_string(spending_authority_ask)
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()
732 
733 
735  nullifiers: List[bytes],
736  sign_vk: JoinsplitSigVerificationKey) -> bytes:
737  """
738  Compute h_sig = sha256(nf_1 || ... || nf_{JS_INPUTS} || sign_vk)
739  """
740  h = sha256()
741  for nf in nullifiers:
742  h.update(nf)
743  h.update(sign_vk.to_bytes())
744  return h.digest()
745 
746 
747 def _create_api_zeth_notes(
748  phi: bytes,
749  hsig: bytes,
750  outputs: List[Tuple[OwnershipPublicKey, int]]
751 ) -> List[api.ZethNote]:
752  """
753  Create ordered list of api.ZethNote objects from the output descriptions.
754  """
755  def _create_api_zeth_note(
756  out_index: int, recipient: OwnershipPublicKey, value: int
757  ) -> api.ZethNote:
758  rho = _compute_rho_i(phi, hsig, out_index)
759  trap_r = _trap_r_randomness()
760  return api.ZethNote(
761  apk=ownership_key_as_hex(recipient),
762  value=int64_to_hex(value),
763  rho=rho.hex(),
764  trap_r=trap_r)
765 
766  return [
767  _create_api_zeth_note(idx, recipient, value)
768  for idx, (recipient, value) in enumerate(outputs)]
769 
770 
771 def _proof_and_inputs_to_bytes(
772  snark: IZKSnarkProvider,
773  pp: PairingParameters,
774  extproof: ExtendedProof,
775  public_data: List[int]) -> Tuple[bytes, bytes]:
776  """
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.
781  """
782  # TODO: avoid duplicating this encoding to evm parameters
783  proof = extproof.proof
784  return \
785  message_to_bytes(snark.proof_to_contract_parameters(proof, pp)), \
786  message_to_bytes(public_data)
787 
788 
789 def _trap_r_randomness() -> str:
790  """
791  Compute randomness `r`
792  """
793  assert (constants.TRAPR_LENGTH_BYTES << 3) == constants.TRAPR_LENGTH
794  return bytes(Random.get_random_bytes(constants.TRAPR_LENGTH_BYTES)).hex()
795 
796 
797 def _compute_rho_i(phi: bytes, hsig: bytes, i: int) -> bytes:
798  """
799  Returns
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.
804 
805  See: Zcash protocol spec p. 57, Section 5.4.2 Pseudo Random Functions
806  """
807  assert i < constants.JS_OUTPUTS
808 
809  # Compute the number of bits required to represent the input index, and
810  # truncate phi so that:
811  # left_leg = 0 || i || 10 || phi_truncated
812  # occupies exactly 256 bits
813 
814  index_bits = math.ceil(math.log(constants.JS_OUTPUTS, 2))
815  index_bin = f"{i:b}"
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}"
819 
820  phi_truncated_bits = 256 - 1 - index_bits - 2
821  phi_truncated_bin = digest_to_binary_string(phi)[:phi_truncated_bits]
822  assert len(phi_truncated_bin) == phi_truncated_bits
823 
824  left_leg_bin = "0" + index_bin + "10" + phi_truncated_bin
825  assert len(left_leg_bin) == 256
826 
827  # Compute blake2s(left_leg || hsig)
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()
832 
833 
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()
837 
838 
839 def _phi_randomness() -> bytes:
840  """
841  Compute the transaction randomness "phi", used for computing the new rhoS
842  """
843  return bytes(Random.get_random_bytes(constants.PHI_LENGTH_BYTES))
zeth.core.mixer_client.MixParameters.to_json_dict
Dict[str, Any] to_json_dict(self)
Definition: mixer_client.py:124
zeth.core.mixer_client.mix_parameters_to_contract_arguments
List[Any] mix_parameters_to_contract_arguments(IZKSnarkProvider zksnark, PairingParameters pp, MixParameters mix_parameters)
Definition: mixer_client.py:157
zeth.core.mixer_client.MixParameters.from_json
MixParameters from_json(IZKSnarkProvider zksnark, str params_json)
Definition: mixer_client.py:118
zeth.core.mixer_client.MixerClient
Definition: mixer_client.py:259
test_commands.mock.str
str
Definition: mock.py:18
zeth.core.mixer_client.create_api_joinsplit_input
api.JoinsplitInput create_api_joinsplit_input(List[str] merkle_path, int address, api.ZethNote note, OwnershipSecretKey a_sk, bytes nullifier)
Definition: mixer_client.py:227
zeth.core.mixer_client.MixerClient.deploy
Tuple[MixerClient, contracts.InstanceDescription] deploy(Any web3, ProverClient prover_client, str deployer_eth_address, Optional[bytes] deployer_eth_private_key, Optional[str] token_address=None, Optional[str] permitted_dispatcher=None, Optional[str] vk_hash=None, Optional[int] deploy_gas=None)
Definition: mixer_client.py:273
zeth.core.ownership.ownership_key_as_hex
str ownership_key_as_hex(bytes a_sk)
Definition: ownership.py:37
zeth.core.mixer_client.MixParameters.from_json_dict
MixParameters from_json_dict(IZKSnarkProvider zksnark, Dict[str, Any] json_dict)
Definition: mixer_client.py:141
zeth.core.mixer_client.MixerClient.prover_config
prover_config
Definition: mixer_client.py:265
zeth.core.mixer_client.MixCallDescription.outputs
outputs
Definition: mixer_client.py:81
zeth.core.mixer_client.MixParameters.ciphertexts
ciphertexts
Definition: mixer_client.py:109
zeth.core.mixer_client.MixCallDescription.v_in
v_in
Definition: mixer_client.py:61
zeth.core.mixer_client.receive_note
Optional[Tuple[bytes, api.ZethNote]] receive_note(MixOutputEvents out_ev, EncryptionSecretKey receiver_k_sk)
Definition: mixer_client.py:618
zeth.cli.zeth_deploy.int
int
Definition: zeth_deploy.py:27
zeth.core.contracts.InstanceDescription
Definition: contracts.py:21
zeth.core.merkle_tree
Definition: merkle_tree.py:1
zeth.core.mixer_client.MixerClient.create_mix_parameters_from_proof
MixParameters create_mix_parameters_from_proof(self, MixCallDescription mix_call_desc, api.ProofInputs prover_inputs, signing.SigningKeyPair signing_keypair, ExtendedProof ext_proof, List[int] public_data, str sender_eth_address, bool for_dispatch_call=False)
Definition: mixer_client.py:500
zeth.core.mixer_client.MixerClient.web3
web3
Definition: mixer_client.py:264
zeth.core.mixer_client.MixResult
Definition: mixer_client.py:204
zeth.core.mixer_client.encrypt_notes
List[bytes] encrypt_notes(List[Tuple[api.ZethNote, EncryptionPublicKey]] notes)
Definition: mixer_client.py:601
zeth.core.mixer_client.MixParameters.__init__
def __init__(self, ExtendedProof extended_proof, List[int] public_data, signing.SigningVerificationKey signature_vk, signing.Signature signature, List[bytes] ciphertexts)
Definition: mixer_client.py:104
zeth.core.mixer_client.MixCallDescription
Definition: mixer_client.py:49
zeth.core.mixer_client.MixCallDescription.v_out
v_out
Definition: mixer_client.py:62
zeth.core.signing.SigningSecretKey
Definition: signing.py:49
zeth.core.mixer_client.get_dummy_input_and_address
Tuple[int, api.ZethNote] get_dummy_input_and_address(OwnershipPublicKey a_pk)
Definition: mixer_client.py:241
zeth.core.encryption
Definition: encryption.py:1
zeth.core.mixer_client.MixParameters
Definition: mixer_client.py:97
zeth.core.prover_client
Definition: prover_client.py:1
zeth.core.encryption.bytes
bytes
Definition: encryption.py:87
zeth.core.mixer_client.MixerClient.deposit
str deposit(self, ProverClient prover_client, MerkleTree mk_tree, ZethAddress zeth_address, str sender_eth_address, Optional[bytes] sender_eth_private_key, EtherValue eth_amount, Optional[List[Tuple[ZethAddressPub, EtherValue]]] outputs=None, Optional[EtherValue] tx_value=None)
Definition: mixer_client.py:321
zeth.core.mixer_client.MixOutputEvents
Definition: mixer_client.py:193
zeth.core.mixer_client.MixCallDescription.sender_ownership_keypair
sender_ownership_keypair
Definition: mixer_client.py:60
zeth.core.merkle_tree.compute_merkle_path
List[str] compute_merkle_path(int address, MerkleTree mk_tree)
Definition: merkle_tree.py:188
zeth.core.mixer_client.MixerClient.mix_call
bool mix_call(self, MixParameters mix_params, str sender_eth_address, EtherValue tx_value, Optional[int] call_gas=None)
Definition: mixer_client.py:400
zeth.core.mixer_client.MixCallDescription.compute_h_sig_cb
compute_h_sig_cb
Definition: mixer_client.py:63
zeth.core.encryption.encrypt
bytes encrypt(bytes message, EncryptionPublicKey pk_receiver)
Definition: encryption.py:168
zeth.core.mixer_client.MixParameters.signature_vk
signature_vk
Definition: mixer_client.py:107
zeth.core.mixer_client.MixCallDescription.inputs
inputs
Definition: mixer_client.py:72
zeth.core.mixer_client.MixerClient.mix
str mix(self, MixParameters mix_params, str sender_eth_address, Optional[bytes] sender_eth_private_key, EtherValue tx_value, Optional[int] call_gas=None)
Definition: mixer_client.py:379
zeth.core.mixer_client.MixResult.nullifiers
nullifiers
Definition: mixer_client.py:210
zeth.core.signing.SigningKeyPair
Definition: signing.py:72
zeth.core.mixer_client.MixerClient.mixer_instance
mixer_instance
Definition: mixer_client.py:266
zeth.core.mixer_client.MixParameters.extended_proof
extended_proof
Definition: mixer_client.py:105
zeth.core.mixer_client.mix_parameters_to_dispatch_parameters
bytes mix_parameters_to_dispatch_parameters(MixParameters mix_parameters)
Definition: mixer_client.py:176
zeth.core.mixer_client.MixerClient.joinsplit
str joinsplit(self, ProverClient prover_client, MerkleTree mk_tree, OwnershipKeyPair sender_ownership_keypair, str sender_eth_address, Optional[bytes] sender_eth_private_key, List[Tuple[int, api.ZethNote]] inputs, List[Tuple[ZethAddressPub, EtherValue]] outputs, EtherValue v_in, EtherValue v_out, Optional[EtherValue] tx_value=None, Optional[ComputeHSigCB] compute_h_sig_cb=None)
Definition: mixer_client.py:346
zeth.core.mixer_client.MixOutputEvents.commitment
commitment
Definition: mixer_client.py:199
zeth.core.mixer_client.MixCallDescription.mk_tree
mk_tree
Definition: mixer_client.py:59
zeth.core.mixer_client.MixResult.output_events
output_events
Definition: mixer_client.py:211
zeth.core.zeth_address.ZethAddressPub
Definition: zeth_address.py:18
zeth.core.mixer_client.MixerClient.create_mix_parameters_and_signing_key
Tuple[MixParameters, JoinsplitSigKeyPair] create_mix_parameters_and_signing_key(self, ProverClient prover_client, MerkleTree mk_tree, OwnershipKeyPair sender_ownership_keypair, str sender_eth_address, List[Tuple[int, api.ZethNote]] inputs, List[Tuple[ZethAddressPub, EtherValue]] outputs, EtherValue v_in, EtherValue v_out, Optional[ComputeHSigCB] compute_h_sig_cb=None, bool for_dispatch_call=False)
Definition: mixer_client.py:545
zeth.core.utils.eth_address_to_bytes32
bytes eth_address_to_bytes32(str eth_addr)
Definition: utils.py:114
zeth.core.mixer_client.MixParameters.to_json
str to_json(self)
Definition: mixer_client.py:121
zeth.core.utils
Definition: utils.py:1
zeth.core.mixer_client.MixResult.__init__
def __init__(self, bytes new_merkle_root, List[bytes] nullifiers, List[MixOutputEvents] output_events)
Definition: mixer_client.py:208
zeth.core.pairing
Definition: pairing.py:1
zeth.core.utils.digest_to_binary_string
str digest_to_binary_string(bytes digest)
Definition: utils.py:168
zeth.core.mixer_client.joinsplit_sign
int joinsplit_sign(IZKSnarkProvider zksnark, PairingParameters pp, JoinsplitSigKeyPair signing_keypair, str sender_eth_address, List[bytes] ciphertexts, ExtendedProof extproof, List[int] public_data, bool for_dispatch_call=False)
Definition: mixer_client.py:657
zeth.core.mixer_client.compute_h_sig
bytes compute_h_sig(List[bytes] nullifiers, JoinsplitSigVerificationKey sign_vk)
Definition: mixer_client.py:734
zeth.core.ownership
Definition: ownership.py:1
zeth.core.utils.to_zeth_units
int to_zeth_units(EtherValue value)
Definition: utils.py:220
zeth.core.mixer_client.compute_nullifier
bytes compute_nullifier(api.ZethNote zeth_note, OwnershipSecretKey spending_authority_ask)
Definition: mixer_client.py:718
zeth.core.mixer_client.MixOutputEvents.ciphertext
ciphertext
Definition: mixer_client.py:200
zeth.core.utils.EtherValue
Definition: utils.py:46
zeth.core.utils.message_to_bytes
bytes message_to_bytes(Any message_list)
Definition: utils.py:277
zeth.core.encryption.decrypt
bytes decrypt(bytes encrypted_message, EncryptionSecretKey sk_receiver)
Definition: encryption.py:203
zeth.core.mixer_client.event_args_to_mix_result
MixResult event_args_to_mix_result(Any event_args)
Definition: mixer_client.py:218
zeth.core.mixer_client.MixerClient.__init__
def __init__(self, Any web3, ProverConfiguration prover_config, Any mixer_instance)
Definition: mixer_client.py:263
zeth.core
Definition: __init__.py:1
zeth.core.mixer_client.compute_commitment
bytes compute_commitment(api.ZethNote zeth_note, PairingParameters pp)
Definition: mixer_client.py:701
zeth.core.zksnark
Definition: zksnark.py:1
zeth.core.mixer_client.MixerClient._create_mix_call
Any _create_mix_call(self, MixParameters mix_parameters)
Definition: mixer_client.py:425
zeth.core.zeth_address
Definition: zeth_address.py:1
zeth.core.utils.get_contracts_dir
str get_contracts_dir()
Definition: utils.py:255
zeth.core.mixer_client.get_mix_results
Iterator[MixResult] get_mix_results(Any web3, Any mixer_instance, int start_block, int end_block, Optional[int] batch_size=None)
Definition: mixer_client.py:640
zeth.core.mixer_client.MixResult.new_merkle_root
new_merkle_root
Definition: mixer_client.py:209
zeth.core.encryption.generate_encryption_keypair
EncryptionKeyPair generate_encryption_keypair()
Definition: encryption.py:163
zeth.core.utils.int64_to_hex
str int64_to_hex(int number)
Definition: utils.py:158
zeth.core.mixer_client.MixCallDescription.__init__
def __init__(self, MerkleTree mk_tree, OwnershipKeyPair sender_ownership_keypair, List[Tuple[int, api.ZethNote]] inputs, List[Tuple[ZethAddressPub, EtherValue]] outputs, EtherValue v_in, EtherValue v_out, Optional[ComputeHSigCB] compute_h_sig_cb=None)
Definition: mixer_client.py:55
zeth.core.mixer_client.MixerClient.create_prover_inputs
Tuple[api.ProofInputs, signing.SigningKeyPair] create_prover_inputs(MixCallDescription mix_call_desc)
Definition: mixer_client.py:439
zeth.core.utils.hex_to_uint256_list
Iterable[int] hex_to_uint256_list(str hex_str)
Definition: utils.py:172
zeth.core.mixer_client.MixParameters.public_data
public_data
Definition: mixer_client.py:106
zeth.core.zksnark.get_zksnark_provider
IZKSnarkProvider get_zksnark_provider(str zksnark_name)
Definition: zksnark.py:486
zeth.core.signing.SigningVerificationKey
Definition: signing.py:25
zeth.core.mixer_client.MixOutputEvents.__init__
def __init__(self, bytes commitment, bytes ciphertext)
Definition: mixer_client.py:198
zeth.core.mixer_client.MixParameters.signature
signature
Definition: mixer_client.py:108