Firstly, in this code, a circuit verification is performed using Groth16. The logic of the circuit is to verify the Merkle proof of the second leaf node of a Merkle tree with four leaf nodes, as well as the hash value (nullifier
) of this leaf node.
The value of this leaf node is derived from the x-value of an elliptic curve point obtained by scalar multiplication on G1, using a secret value as the scalar value.
All values on the Scalar Field are defined as scalar values on MNT4, while G1 is defined as the Base point on MNT6.
The intention of this task is to construct a different value secret_hack
to replace the secret
, whose hash value nullifier_hack is different from the original secret's hash value nullifier, and can still pass the circuit verification when secret_hack and nullifier_hack are input.
First, since the hash value nullifier_hack
of secret_hack
must be different from the hash value nullifier
of the secret
, it implies that secret_hack
and secret
cannot be the same.
Then, as the secret needs to be used as a scalar value to perform scalar multiplication on G1 to obtain the x-value of the elliptic curve point (defined as pointA), if a secret_hack value can be obtained such that the x-value of the elliptic curve point (defined as pointB) obtained by performing scalar multiplication of secret_hack on G1 is the same as the x-value of pointA, the circuit verification can be passed.
let base = G1Var::new_constant(ark_relations::ns!(cs, "base"), G1Affine::generator())?;
let pk = base.scalar_mul_le(secret_bits.iter())?.to_affine()?;
// Allocate Leaf
let leaf_g: Vec<_> = vec![pk.x];
On the elliptic curve, if pointA.x = pointB.x
, it indicates that pointA is the same as pointB, or pointA.y = - pointB.y.
Therefore, if we can find a way to construct a pointB, such that it is the symmetric point of pointA, we can achieve the final goal.
Since pointA = base^secret
, then pointB = base^(-secret)
, i.e., secret_hack = -secret = r_6 - secret
. Here, r_6
is the order of the Scalar Field on the MNT6 curve.
And all Scalar Field values in the code are represented on the Scalar Field of MNT4. Therefore, it is necessary to convert the value of r_6 - secret
on the Scalar Field of MNT6 to the value on the Scalar Field of MNT4.
The parameters are as follows; the orders r of the two Scalar Fields are different, so it is necessary to convert secret_hack to represent the correct value of secret_hack on the Scalar Field of MNT.
// mnt4
// * Scalar field: r_4 = 0x01C4C62D92C41110229022EEE2CDADB7F997505B8FAFED5EB7E8F96C97D87307FDB925E8A0ED8D99D124D9A15AF79DB26C5C28C859A99B3EEBCA9429212636B9DFF97634993AA4D6C381BC3F0057974EA099170FA13A4FD90776E240000001
//mnt6
// * Scalar field: r_6 = 0x01C4C62D92C41110229022EEE2CDADB7F997505B8FAFED5EB7E8F96C97D87307FDB925E8A0ED8D99D124D9A15AF79DB117E776F218059DB80F0DA5CB537E38685ACCE9767254A4638810719AC425F0E39D54522CDD119F5E9063DE245E8001
secret_hack = r_6 - secret = (r_6 - r_4) + (r_4 - secret)
Let delta = r_6 - r_4
, then it can be calculated in the following way:
let delta = MNT4BigFr::from(MNT6BigFr::from(- 1).into_bigint()) - MNT4BigFr::from(-1);
Thus, secret_hack is calculated through the following steps:
let delta = MNT4BigFr::from(-1) - MNT4BigFr::from(MNT6BigFr::from(- 1).into_bigint());
let secret_hack: MNT4BigFr = - leaked_secret - delta;
Then continue to calculate nullifier_hack:
let nullifier_hack = <LeafH as CRHScheme>::evaluate(&leaf_crh_params, vec![secret_hack]).unwrap();