Money Contract Source Code

BZHKGQ26bzmBithTQYTJtjo2QdCqpkR9tjSBopT4yf4o
proof/burn_v1.zk
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
# The k parameter defining the number of rows used in our circuit (2^k)
k = 11;
field = "pallas";

# The constants we define for our circuit
constant "Burn_V1" {
    EcFixedPointShort VALUE_COMMIT_VALUE,
    EcFixedPoint VALUE_COMMIT_RANDOM,
    EcFixedPointBase NULLIFIER_K,
}

# The witness coin_values we define for our circuit
witness "Burn_V1" {
    # Secret key used to derive nullifier and coin's public key
    Base coin_secret,

    # The coin_value of this coin
    Base coin_value,
    # The coin_token_id ID
    Base coin_token_id,
    # Allows composing this ZK proof to invoke other contracts
    Base coin_spend_hook,
    # Data passed from this coin to the invoked contract
    Base coin_user_data,
    # Unique serial number corresponding to this coin
    Base coin_blind,

    # Random blinding factor for coin_value commitment
    Scalar value_blind,
    # Random blinding factor for the coin_token_id ID
    Base token_id_blind,
    # Blinding factor for the encrypted coin_user_data
    Base user_data_blind,

    # Leaf position of the coin in the Merkle tree of coins
    Uint32 leaf_pos,
    # Merkle path to the coin
    MerklePath path,

    # Secret key used to derive public key for the tx signature
    Base signature_secret,
}

# The definition of our circuit
circuit "Burn_V1" {
    # Derive the public key used in the coin from its secret counterpart
    pub = ec_mul_base(coin_secret, NULLIFIER_K);
    # Coin hash
    coin = poseidon_hash(
        ec_get_x(pub),
        ec_get_y(pub),
        coin_value,
        coin_token_id,
        coin_spend_hook,
        coin_user_data,
        coin_blind,
    );

    # Poseidon hash of the nullifier
    nullifier = poseidon_hash(coin_secret, coin);
    constrain_instance(nullifier);

    # Pedersen commitment for coin's coin_value
    vcv = ec_mul_short(coin_value, VALUE_COMMIT_VALUE);
    vcr = ec_mul(value_blind, VALUE_COMMIT_RANDOM);
    coin_value_commit = ec_add(vcv, vcr);
    # Since coin_value_commit is a curve point, we fetch its coordinates
    # and constrain them:
    constrain_instance(ec_get_x(coin_value_commit));
    constrain_instance(ec_get_y(coin_value_commit));

    # Commitment for coin's coin_token_id ID. We do a poseidon hash since it's
    # cheaper than EC operations and doesn't need the homomorphic prop.
    coin_token_id_commit = poseidon_hash(coin_token_id, token_id_blind);
    constrain_instance(coin_token_id_commit);

    # With this, we can actually produce a fake coin of coin_value 0
    # above and use it as a dummy input. The inclusion merkle tree
    # has a 0x00 leaf at position 0, so zero_cond will output coin_value
    # if coin_value is 0 - which is equivalent to 0x00 so that's the
    # trick we use to make the inclusion proof.
    coin_incl = zero_cond(coin_value, coin);

    # Merkle root
    root = merkle_root(leaf_pos, path, coin_incl);
    constrain_instance(root);

    # Export coin_user_data
    coin_user_data_enc = poseidon_hash(coin_user_data, user_data_blind);
    constrain_instance(coin_user_data_enc);

    # Reveal coin_spend_hook
    constrain_instance(coin_spend_hook);

    # Finally, we derive a public key for the signature and
    # constrain its coordinates:
    signature_public = ec_mul_base(signature_secret, NULLIFIER_K);
    constrain_instance(ec_get_x(signature_public));
    constrain_instance(ec_get_y(signature_public));

    # At this point we've enforced all of our public inputs.
}