|
|
@@ -23,16 +23,32 @@ use darkfi::{
|
|
|
zkas::ZkBinary,
|
|
|
Result,
|
|
|
};
|
|
|
+use darkfi_money_contract::client::MoneyNote;
|
|
|
use darkfi_sdk::{
|
|
|
crypto::{
|
|
|
- pasta_prelude::*, pedersen_commitment_u64, poseidon_hash, Coin, PublicKey, TokenId,
|
|
|
- CONSENSUS_CONTRACT_ID,
|
|
|
+ pasta_prelude::*, pedersen_commitment_base, pedersen_commitment_u64, poseidon_hash, Coin,
|
|
|
+ MerkleNode, MerklePosition, Nullifier, PublicKey, SecretKey, TokenId,
|
|
|
},
|
|
|
+ incrementalmerkletree::Hashable,
|
|
|
pasta::pallas,
|
|
|
};
|
|
|
use rand::rngs::OsRng;
|
|
|
|
|
|
-use crate::model::ZERO;
|
|
|
+use crate::client::ConsensusNote;
|
|
|
+
|
|
|
+pub struct TransactionBuilderInputInfo {
|
|
|
+ pub leaf_position: MerklePosition,
|
|
|
+ pub merkle_path: Vec<MerkleNode>,
|
|
|
+ pub secret: SecretKey,
|
|
|
+ pub note: MoneyNote,
|
|
|
+}
|
|
|
+
|
|
|
+pub struct TransactionBuilderConsensusInputInfo {
|
|
|
+ pub leaf_position: MerklePosition,
|
|
|
+ pub merkle_path: Vec<MerkleNode>,
|
|
|
+ pub secret: SecretKey,
|
|
|
+ pub note: ConsensusNote,
|
|
|
+}
|
|
|
|
|
|
pub struct TransactionBuilderOutputInfo {
|
|
|
pub value: u64,
|
|
|
@@ -41,7 +57,7 @@ pub struct TransactionBuilderOutputInfo {
|
|
|
}
|
|
|
|
|
|
pub struct ConsensusMintRevealed {
|
|
|
- pub epoch: pallas::Base,
|
|
|
+ pub epoch: u64,
|
|
|
pub coin: Coin,
|
|
|
pub value_commit: pallas::Point,
|
|
|
}
|
|
|
@@ -49,10 +65,11 @@ pub struct ConsensusMintRevealed {
|
|
|
impl ConsensusMintRevealed {
|
|
|
pub fn to_vec(&self) -> Vec<pallas::Base> {
|
|
|
let valcom_coords = self.value_commit.to_affine().coordinates().unwrap();
|
|
|
+ let epoch_palas = pallas::Base::from(self.epoch);
|
|
|
|
|
|
// NOTE: It's important to keep these in the same order
|
|
|
// as the `constrain_instance` calls in the zkas code.
|
|
|
- vec![self.epoch, self.coin.inner(), *valcom_coords.x(), *valcom_coords.y()]
|
|
|
+ vec![epoch_palas, self.coin.inner(), *valcom_coords.x(), *valcom_coords.y()]
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -73,7 +90,7 @@ pub fn create_consensus_mint_proof(
|
|
|
let coin =
|
|
|
Coin::from(poseidon_hash([pub_x, pub_y, value_pallas, epoch_pallas, serial, coin_blind]));
|
|
|
|
|
|
- let public_inputs = ConsensusMintRevealed { epoch: epoch_pallas, coin, value_commit };
|
|
|
+ let public_inputs = ConsensusMintRevealed { epoch, coin, value_commit };
|
|
|
|
|
|
let prover_witnesses = vec![
|
|
|
Witness::Base(Value::known(pub_x)),
|
|
|
@@ -90,3 +107,202 @@ pub fn create_consensus_mint_proof(
|
|
|
|
|
|
Ok((proof, public_inputs))
|
|
|
}
|
|
|
+
|
|
|
+pub struct ConsensusBurnRevealed {
|
|
|
+ pub nullifier: Nullifier,
|
|
|
+ pub epoch: u64,
|
|
|
+ pub signature_public: PublicKey,
|
|
|
+ pub merkle_root: MerkleNode,
|
|
|
+ pub value_commit: pallas::Point,
|
|
|
+}
|
|
|
+
|
|
|
+impl ConsensusBurnRevealed {
|
|
|
+ pub fn to_vec(&self) -> Vec<pallas::Base> {
|
|
|
+ let valcom_coords = self.value_commit.to_affine().coordinates().unwrap();
|
|
|
+ let sigpub_coords = self.signature_public.inner().to_affine().coordinates().unwrap();
|
|
|
+ let epoch_palas = pallas::Base::from(self.epoch);
|
|
|
+
|
|
|
+ // NOTE: It's important to keep these in the same order
|
|
|
+ // as the `constrain_instance` calls in the zkas code.
|
|
|
+ vec![
|
|
|
+ self.nullifier.inner(),
|
|
|
+ epoch_palas,
|
|
|
+ *sigpub_coords.x(),
|
|
|
+ *sigpub_coords.y(),
|
|
|
+ self.merkle_root.inner(),
|
|
|
+ *valcom_coords.x(),
|
|
|
+ *valcom_coords.y(),
|
|
|
+ ]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn create_consensus_burn_proof(
|
|
|
+ zkbin: &ZkBinary,
|
|
|
+ pk: &ProvingKey,
|
|
|
+ input: &TransactionBuilderConsensusInputInfo,
|
|
|
+ value_blind: pallas::Scalar,
|
|
|
+) -> Result<(Proof, ConsensusBurnRevealed, SecretKey)> {
|
|
|
+ let nullifier = Nullifier::from(poseidon_hash([input.secret.inner(), input.note.serial]));
|
|
|
+ let epoch = input.note.epoch;
|
|
|
+ let epoch_pallas = pallas::Base::from(epoch);
|
|
|
+ let value_pallas = pallas::Base::from(input.note.value);
|
|
|
+ let value_commit = pedersen_commitment_u64(input.note.value, value_blind);
|
|
|
+ let public_key = PublicKey::from_secret(input.secret);
|
|
|
+ let (pub_x, pub_y) = public_key.xy();
|
|
|
+
|
|
|
+ let coin = poseidon_hash([
|
|
|
+ pub_x,
|
|
|
+ pub_y,
|
|
|
+ value_pallas,
|
|
|
+ epoch_pallas,
|
|
|
+ input.note.serial,
|
|
|
+ input.note.coin_blind,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ let merkle_root = {
|
|
|
+ let position: u64 = input.leaf_position.into();
|
|
|
+ let mut current = MerkleNode::from(coin);
|
|
|
+ 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 public_inputs = ConsensusBurnRevealed {
|
|
|
+ nullifier,
|
|
|
+ epoch,
|
|
|
+ signature_public: public_key,
|
|
|
+ merkle_root,
|
|
|
+ value_commit,
|
|
|
+ };
|
|
|
+
|
|
|
+ let prover_witnesses = vec![
|
|
|
+ Witness::Base(Value::known(value_pallas)),
|
|
|
+ Witness::Base(Value::known(epoch_pallas)),
|
|
|
+ Witness::Base(Value::known(input.note.serial)),
|
|
|
+ Witness::Base(Value::known(input.note.coin_blind)),
|
|
|
+ Witness::Scalar(Value::known(value_blind)),
|
|
|
+ Witness::Base(Value::known(input.secret.inner())),
|
|
|
+ Witness::Uint32(Value::known(u64::from(input.leaf_position).try_into().unwrap())),
|
|
|
+ Witness::MerklePath(Value::known(input.merkle_path.clone().try_into().unwrap())),
|
|
|
+ ];
|
|
|
+
|
|
|
+ let circuit = ZkCircuit::new(prover_witnesses, zkbin.clone());
|
|
|
+ let proof = Proof::create(pk, &[circuit], &public_inputs.to_vec(), &mut OsRng)?;
|
|
|
+
|
|
|
+ Ok((proof, public_inputs, input.secret))
|
|
|
+}
|
|
|
+
|
|
|
+// TODO: Remove everything following
|
|
|
+pub struct ConsensusUnstakeBurnRevealed {
|
|
|
+ pub value_commit: pallas::Point,
|
|
|
+ pub token_commit: pallas::Point,
|
|
|
+ pub nullifier: Nullifier,
|
|
|
+ pub merkle_root: MerkleNode,
|
|
|
+ pub spend_hook: pallas::Base,
|
|
|
+ pub user_data_enc: pallas::Base,
|
|
|
+ pub signature_public: PublicKey,
|
|
|
+}
|
|
|
+
|
|
|
+impl ConsensusUnstakeBurnRevealed {
|
|
|
+ pub fn to_vec(&self) -> Vec<pallas::Base> {
|
|
|
+ let valcom_coords = self.value_commit.to_affine().coordinates().unwrap();
|
|
|
+ let tokcom_coords = self.token_commit.to_affine().coordinates().unwrap();
|
|
|
+ let sigpub_coords = self.signature_public.inner().to_affine().coordinates().unwrap();
|
|
|
+
|
|
|
+ // NOTE: It's important to keep these in the same order
|
|
|
+ // as the `constrain_instance` calls in the zkas code.
|
|
|
+ vec![
|
|
|
+ self.nullifier.inner(),
|
|
|
+ *valcom_coords.x(),
|
|
|
+ *valcom_coords.y(),
|
|
|
+ *tokcom_coords.x(),
|
|
|
+ *tokcom_coords.y(),
|
|
|
+ self.merkle_root.inner(),
|
|
|
+ self.user_data_enc,
|
|
|
+ *sigpub_coords.x(),
|
|
|
+ *sigpub_coords.y(),
|
|
|
+ ]
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn create_unstake_burn_proof(
|
|
|
+ zkbin: &ZkBinary,
|
|
|
+ pk: &ProvingKey,
|
|
|
+ input: &TransactionBuilderInputInfo,
|
|
|
+ value_blind: pallas::Scalar,
|
|
|
+ token_blind: pallas::Scalar,
|
|
|
+ user_data_blind: pallas::Base,
|
|
|
+ signature_secret: SecretKey,
|
|
|
+) -> Result<(Proof, ConsensusUnstakeBurnRevealed)> {
|
|
|
+ let nullifier = Nullifier::from(poseidon_hash([input.secret.inner(), input.note.serial]));
|
|
|
+ let public_key = PublicKey::from_secret(input.secret);
|
|
|
+ let (pub_x, pub_y) = public_key.xy();
|
|
|
+
|
|
|
+ let signature_public = PublicKey::from_secret(signature_secret);
|
|
|
+
|
|
|
+ let coin = poseidon_hash([
|
|
|
+ pub_x,
|
|
|
+ pub_y,
|
|
|
+ pallas::Base::from(input.note.value),
|
|
|
+ input.note.token_id.inner(),
|
|
|
+ input.note.serial,
|
|
|
+ input.note.spend_hook,
|
|
|
+ input.note.user_data,
|
|
|
+ input.note.coin_blind,
|
|
|
+ ]);
|
|
|
+
|
|
|
+ let merkle_root = {
|
|
|
+ let position: u64 = input.leaf_position.into();
|
|
|
+ let mut current = MerkleNode::from(coin);
|
|
|
+ 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 user_data_enc = poseidon_hash([input.note.user_data, user_data_blind]);
|
|
|
+ let value_commit = pedersen_commitment_u64(input.note.value, value_blind);
|
|
|
+ let token_commit = pedersen_commitment_base(input.note.token_id.inner(), token_blind);
|
|
|
+
|
|
|
+ let public_inputs = ConsensusUnstakeBurnRevealed {
|
|
|
+ value_commit,
|
|
|
+ token_commit,
|
|
|
+ nullifier,
|
|
|
+ merkle_root,
|
|
|
+ spend_hook: input.note.spend_hook,
|
|
|
+ user_data_enc,
|
|
|
+ signature_public,
|
|
|
+ };
|
|
|
+
|
|
|
+ let prover_witnesses = vec![
|
|
|
+ Witness::Base(Value::known(pallas::Base::from(input.note.value))),
|
|
|
+ Witness::Base(Value::known(input.note.token_id.inner())),
|
|
|
+ Witness::Scalar(Value::known(value_blind)),
|
|
|
+ Witness::Scalar(Value::known(token_blind)),
|
|
|
+ Witness::Base(Value::known(input.note.serial)),
|
|
|
+ Witness::Base(Value::known(input.note.spend_hook)),
|
|
|
+ Witness::Base(Value::known(input.note.user_data)),
|
|
|
+ Witness::Base(Value::known(user_data_blind)),
|
|
|
+ Witness::Base(Value::known(input.note.coin_blind)),
|
|
|
+ Witness::Base(Value::known(input.secret.inner())),
|
|
|
+ Witness::Uint32(Value::known(u64::from(input.leaf_position).try_into().unwrap())),
|
|
|
+ Witness::MerklePath(Value::known(input.merkle_path.clone().try_into().unwrap())),
|
|
|
+ Witness::Base(Value::known(signature_secret.inner())),
|
|
|
+ ];
|
|
|
+
|
|
|
+ let circuit = ZkCircuit::new(prover_witnesses, zkbin.clone());
|
|
|
+ let proof = Proof::create(pk, &[circuit], &public_inputs.to_vec(), &mut OsRng)?;
|
|
|
+
|
|
|
+ Ok((proof, public_inputs))
|
|
|
+}
|