Files
darkfi/src/contract/consensus/proof/consensus_proposal_v1.zk

158 lines
4.7 KiB
Plaintext

k = 13;
field = "pallas";
constant "ConsensusProposal_V1" {
EcFixedPointShort VALUE_COMMIT_VALUE,
EcFixedPoint VALUE_COMMIT_RANDOM,
EcFixedPointBase NULLIFIER_K,
}
witness "ConsensusProposal_V1" {
# Burnt coin secret key
Base input_secret_key,
# Unique serial number corresponding to the burnt coin
Base input_serial,
# The value of the burnt coin
Base input_value,
# The epoch the burnt coin was minted on
Base epoch,
# The reward value
Base reward,
# Random blinding factor for the value commitment
Scalar input_value_blind,
# Leaf position of the coin in the Merkle tree of coins
Uint32 leaf_pos,
# Merkle path to the coin
MerklePath path,
# Random blinding factor for the value commitment of the new coin
Scalar output_value_blind,
# Election seed y
Base mu_y,
# Election seed rho
Base mu_rho,
# Sigma1
Base sigma1,
# Sigma2
Base sigma2,
# Lottery headstart
Base headstart,
}
circuit "ConsensusProposal_V1" {
# Witnessed constants
ZERO = witness_base(0);
SERIAL_PREFIX = witness_base(2);
SEED_PREFIX = witness_base(3);
SECRET_PREFIX = witness_base(4);
# =============
# Burn old coin
# =============
# Poseidon hash of the nullifier
nullifier = poseidon_hash(input_secret_key, input_serial);
constrain_instance(nullifier);
# Constrain the epoch this coin was minted on.
# We use this as our timelock mechanism.
constrain_instance(epoch);
# We derive the coin's public key for the signature and
# VRF proof verification and constrain its coordinates:
input_pub = ec_mul_base(input_secret_key, NULLIFIER_K);
pub_x = ec_get_x(input_pub);
pub_y = ec_get_y(input_pub);
constrain_instance(pub_x);
constrain_instance(pub_y);
# Construct the burned coin
C = poseidon_hash(
pub_x,
pub_y,
input_value,
epoch,
input_serial,
);
# Merkle inclusion proof
root = merkle_root(leaf_pos, path, C);
constrain_instance(root);
# Pedersen commitment for burned coin's value
vcv = ec_mul_short(input_value, VALUE_COMMIT_VALUE);
vcr = ec_mul(input_value_blind, VALUE_COMMIT_RANDOM);
value_commit = ec_add(vcv, vcr);
# Since value_commit is a curve point, we fetch its coordinates
# and constrain them:
constrain_instance(ec_get_x(value_commit));
constrain_instance(ec_get_y(value_commit));
# =============
# Mint new coin
# =============
# Constrain reward value
constrain_instance(reward);
# Pedersen commitment for new coin's value (old value + reward)
output_value = base_add(input_value, reward);
nvcv = ec_mul_short(output_value, VALUE_COMMIT_VALUE);
nvcr = ec_mul(output_value_blind, VALUE_COMMIT_RANDOM);
output_value_commit = ec_add(nvcv, nvcr);
# Since the new value commit is also a curve point, we'll do the same
# coordinate dance:
constrain_instance(ec_get_x(output_value_commit));
constrain_instance(ec_get_y(output_value_commit));
# The serial of the new coin is derived from the old coin
output_serial = poseidon_hash(SERIAL_PREFIX, input_secret_key, input_serial);
# The secret key of the new coin is derived from old coin
output_secret_key = poseidon_hash(SECRET_PREFIX, input_secret_key);
output_pub = ec_mul_base(output_secret_key, NULLIFIER_K);
output_pub_x = ec_get_x(output_pub);
output_pub_y = ec_get_y(output_pub);
# Poseidon hash of the new coin
# In here we set the new epoch as ZERO, thus removing a
# potentially existing timelock.
output_coin = poseidon_hash(
output_pub_x,
output_pub_y,
output_value,
ZERO,
output_serial,
);
constrain_instance(output_coin);
# ============================
# Constrain lottery parameters
# ============================
# Coin y, constructed with the old serial for seeding:
seed = poseidon_hash(SEED_PREFIX, input_serial);
y = poseidon_hash(seed, mu_y);
constrain_instance(mu_y);
constrain_instance(y);
# Coin rho (seed):
rho = poseidon_hash(seed, mu_rho);
constrain_instance(mu_rho);
constrain_instance(rho);
# Calculate lottery target
term_1 = base_mul(sigma1, input_value);
term_2 = base_mul(sigma2, input_value);
shifted_term_2 = base_mul(term_2, input_value);
target = base_add(term_1, shifted_term_2);
shifted_target = base_add(target, headstart);
constrain_instance(sigma1);
constrain_instance(sigma2);
constrain_instance(headstart);
# Play lottery
less_than_strict(y, shifted_target);
# At this point we've enforced all of our public inputs.
}