# ZKHack IV Puzzles #1 Gamma Ray Given a Merkle tree and a secret corresponding to a leaf. First it will prove that the leaf is on the Merkle tree, and then ask for a new secret to ensure that its corresponding leaf is also on the Merkle tree. Origin Problem: https://github.com/ZK-Hack/puzzle-gamma-ray ## background It is assumed that you already know about elliptic curve, such as its point addition, scalar multiplication, ECDLP, Generator, order. See [these four articles](https://andrea.corbellini.name/2015/05/17/elliptic-curve-cryptography-a-gentle-introduction/) for detail. There is a fact that, in affine space the graph of the elliptic curve $y^2= x^3 + ax +b$ is symmetrical about the $x$-axis. For a certain point $P_0(x_0,y_0)$ above, there must be a point $P_1(x_0,-y_0)$ on the curve. After$\mod q$, the $y$ value of points below the $x$-axis will be added to $q$, and the points of their elliptic curves are symmetrical about $y = \frac{q}{2}$. In the definition of elliptic curve group, they are inverse elements of each other. Here we introduce CycleCurve. As an example, this is the parameter of Secp256k1: $y^2 \equiv x^3 + 7 \mod q$, where `q=0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE FFFFFC2F`, the generator is point G, the amount of elements is `order`, and `order=0xFFFFFFFF FFFFFFFF FFFFFFFF FFFFFFFE BAAEDCE6 AF48A03B BFD25E8C D0364141`. The Fields generated by `mod q` is the `base field`, related to $(x,y)$, and by `mod order` is the `scalar field`, which is related to the coefficient of the point, such as $aG$. $a$ is in the scalar field. In SNARK, the witness prover input is the element in the scalar field, and the proof after instantiation is the point $(x, y)$ related. In the recursive snark, we take the point $(x, y)$ as input and repeat the process, but it can be noted that $q$ is not equal `order`. At this time, we need to prepare two curves, and their `q` and `order` cross-equality. If you need a more detailed description, you can refer to [here](https://blog.icme.io/can-we-avoid-cycles-of-curves/) When looking at the source code in `main.rs`, you can find that two curves are used: `ark-mnt4-753` and `ark-mnt6-753`. According to the description of [ark-mnt4-753](https://docs.rs/ark-mnt4-753/latest/ark_mnt4_753/): > The main feature of this curve is that its scalar field and base field respectively equal the base field and scalar field of MNT6_753. Then I noticed that this is a MerkleTree, which is widely used in existence proof scenarios, such as Tornado Cash. arkworks provides a [merkle-tree-example](https://github.com/arkworks-rs/r1cs-tutorial/tree/main/merkle-tree-example). If you have seen this example before, it will reduce your unfamiliarity. In `SpendCircuit`, the only things we customize are `secret` and `nullifier`. So we need to pay attention to how they are handled when they are inside the MerkleTree: We look at the implementation of `generate_constraints()` in the `ConstraintSynthesizer` trait. 1. Evaluate the secret with Tree's paramaters. The result need be equal to the input nullifier. We do not need to know what operations are performed in the `evaluate`. If we can get the correct secret, we can get the correct value by printing `nullifier_in_circuit.value()`. If you want to figure out what evaluate does, you can check [code](https://github.com/arkworks-rs/crypto-primitives/blob/main/src/crh/poseidon/constraints.rs#L28-L54), It just hashes the secret. ```rust= let nullifier_in_circuit = <LeafHG as CRHSchemeGadget<LeafH, _>>::evaluate(&leaf_crh_params_var, &[secret])?; nullifier_in_circuit.enforce_equal(&nullifier)?; ``` 2. Get the generator point of `G1Affine` and multiply the secret to get a new point `pk`. Note that `G1Affine` is defined as the mnt6 curve in `ark-mnt6-753`. ```rust= let base = G1Var::new_constant(ark_relations::ns!(cs, "base"), G1Affine::generator())?; let pk = base.scalar_mul_le(secret_bits.iter())?.to_affine()?; # https://github.com/arkworks-rs/curves/blob/master/mnt6_753/src/curves/g1.rs#L9 pub type G1Affine = mnt6::G1Affine<crate::Config>; ``` 3. Check that `pk.x` is indeed on the leaf of the Tree. ```rust= // Allocate Leaf let leaf_g: Vec<_> = vec![pk.x]; // Allocate Merkle Tree Path let cw: PathVar<MntMerkleTreeParams, ConstraintF, MntMerkleTreeParamsVar> = PathVar::new_witness(ark_relations::ns!(cs, "new_witness"), || Ok(&self.proof))?; cw.verify_membership( &leaf_crh_params_var, &two_to_one_crh_params_var, &root, &leaf_g, )? .enforce_equal(&Boolean::constant(true))?; ``` 4. It is required to use different `nullifier`, and the hidden condition is to use different `secret`. ```rust= assert_ne!(nullifier, nullifier_hack); ``` Therefore, we can find the inverse element of the point corresponding to `secret` under `mnt6`(because the Generator is `mnt6`), then convert it to `MNT4BigFr`, and then print out `nullifier`, then used it as `nullifier_hack`. ```rust= let zero_mnt6 = MNT6BigFr::from(0); let leaked_secret_mnt6 = MNT6BigFr::from(leaked_secret.into_bigint()); let secret_hack_mnt6 = zero_mnt6 - leaked_secret_mnt6.clone(); let secret_hack = MNT4BigFr::from(secret_hack_mnt6.into_bigint()); let nullifier_hack = MNT4BigFr::from(BigInt([ 3973337740569432013, 13414245347258487558, 2397428639358863969, 9003710485710992633, 7549792233030202438, 12021247171312009477, 10014790687727384398, 10719001394312000749, 12147986067925283906, 7048510174634313048, 13041399962513988848, 81761383246681, ])); ``` The whole solutions see here: https://github.com/flyq/puzzle-gamma-ray.