use darkfi::{
zk::{halo2::Value, Proof, ProvingKey, Witness, ZkCircuit},
zkas::ZkBinary,
Result,
};
use darkfi_sdk::{
bridgetree::Hashable,
crypto::{
pasta_prelude::{Curve, CurveAffine},
pedersen_commitment_u64, poseidon_hash, BaseBlind, FuncId, MerkleNode, PublicKey,
ScalarBlind, SecretKey,
},
pasta::pallas,
};
use rand::rngs::OsRng;
use crate::{
client::{Coin, MoneyNote, OwnCoin},
model::{CoinAttributes, Nullifier},
};
pub const FEE_CALL_GAS: u64 = 41_000_000;
pub struct FeeCallSecrets {
pub proof: Proof,
pub signature_secret: SecretKey,
pub note: MoneyNote,
pub input_value_blind: ScalarBlind,
pub output_value_blind: ScalarBlind,
}
pub struct FeeRevealed {
pub nullifier: Nullifier,
pub input_value_commit: pallas::Point,
pub token_commit: pallas::Base,
pub merkle_root: MerkleNode,
pub input_user_data_enc: pallas::Base,
pub signature_public: PublicKey,
pub output_coin: Coin,
pub output_value_commit: pallas::Point,
}
impl FeeRevealed {
pub fn to_vec(&self) -> Vec<pallas::Base> {
let input_vc_coords = self.input_value_commit.to_affine().coordinates().unwrap();
let output_vc_coords = self.output_value_commit.to_affine().coordinates().unwrap();
let sigpub_coords = self.signature_public.inner().to_affine().coordinates().unwrap();
vec![
self.nullifier.inner(),
*input_vc_coords.x(),
*input_vc_coords.y(),
self.token_commit,
self.merkle_root.inner(),
self.input_user_data_enc,
*sigpub_coords.x(),
*sigpub_coords.y(),
self.output_coin.inner(),
*output_vc_coords.x(),
*output_vc_coords.y(),
]
}
}
pub struct FeeCallInput {
pub coin: OwnCoin,
pub merkle_path: Vec<MerkleNode>,
pub user_data_blind: BaseBlind,
}
pub type FeeCallOutput = CoinAttributes;
#[allow(clippy::too_many_arguments)]
pub fn create_fee_proof(
zkbin: &ZkBinary,
pk: &ProvingKey,
input: &FeeCallInput,
input_value_blind: ScalarBlind,
output: &FeeCallOutput,
output_value_blind: ScalarBlind,
output_spend_hook: FuncId,
output_user_data: pallas::Base,
output_coin_blind: BaseBlind,
token_blind: BaseBlind,
signature_secret: SecretKey,
) -> Result<(Proof, FeeRevealed)> {
let public_key = PublicKey::from_secret(input.coin.secret);
let signature_public = PublicKey::from_secret(signature_secret);
let input_coin = CoinAttributes {
public_key,
value: input.coin.note.value,
token_id: input.coin.note.token_id,
spend_hook: input.coin.note.spend_hook,
user_data: input.coin.note.user_data,
blind: input.coin.note.coin_blind,
}
.to_coin();
let merkle_root = {
let position: u64 = input.coin.leaf_position.into();
let mut current = MerkleNode::from(input_coin.inner());
for (level, sibling) in input.merkle_path.iter().enumerate() {
let level = level as u8;
current = if position & (1 << level) == 0 {
MerkleNode::combine(level.into(), ¤t, sibling)
} else {
MerkleNode::combine(level.into(), sibling, ¤t)
};
}
current
};
let input_user_data_enc =
poseidon_hash([input.coin.note.user_data, input.user_data_blind.inner()]);
let input_value_commit = pedersen_commitment_u64(input.coin.note.value, input_value_blind);
let output_value_commit = pedersen_commitment_u64(output.value, output_value_blind);
let token_commit = poseidon_hash([input.coin.note.token_id.inner(), token_blind.inner()]);
let output_coin = CoinAttributes {
public_key: output.public_key,
value: output.value,
token_id: output.token_id,
spend_hook: output_spend_hook,
user_data: output_user_data,
blind: output_coin_blind,
}
.to_coin();
let public_inputs = FeeRevealed {
nullifier: input.coin.nullifier(),
input_value_commit,
token_commit,
merkle_root,
input_user_data_enc,
signature_public,
output_coin,
output_value_commit,
};
let prover_witnesses = vec![
Witness::Base(Value::known(input.coin.secret.inner())),
Witness::Uint32(Value::known(u64::from(input.coin.leaf_position).try_into().unwrap())),
Witness::MerklePath(Value::known(input.merkle_path.clone().try_into().unwrap())),
Witness::Base(Value::known(signature_secret.inner())),
Witness::Base(Value::known(pallas::Base::from(input.coin.note.value))),
Witness::Scalar(Value::known(input_value_blind.inner())),
Witness::Base(Value::known(input.coin.note.spend_hook.inner())),
Witness::Base(Value::known(input.coin.note.user_data)),
Witness::Base(Value::known(input.coin.note.coin_blind.inner())),
Witness::Base(Value::known(input.user_data_blind.inner())),
Witness::Base(Value::known(pallas::Base::from(output.value))),
Witness::Base(Value::known(output_spend_hook.inner())),
Witness::Base(Value::known(output_user_data)),
Witness::Scalar(Value::known(output_value_blind.inner())),
Witness::Base(Value::known(output_coin_blind.inner())),
Witness::Base(Value::known(input.coin.note.token_id.inner())),
Witness::Base(Value::known(token_blind.inner())),
];
let circuit = ZkCircuit::new(prover_witnesses, zkbin);
let proof = Proof::create(pk, &[circuit], &public_inputs.to_vec(), &mut OsRng)?;
Ok((proof, public_inputs))
}