narodnik 4 лет назад
Родитель
Сommit
89b0b8cadd
5 измененных файлов с 465 добавлено и 0 удалено
  1. 2 0
      bin/daod/src/demo.rs
  2. 1 0
      bin/daod/src/main.rs
  3. 188 0
      bin/daod/src/money/builder.rs
  4. 243 0
      bin/daod/src/money/mod.rs
  5. 31 0
      bin/daod/src/money/partial.rs

+ 2 - 0
bin/daod/src/demo.rs

@@ -16,6 +16,8 @@ use std::{
     time::Instant,
 };
 
+use crate::money;
+
 use darkfi::{
     crypto::{
         constants::MERKLE_DEPTH,

+ 1 - 0
bin/daod/src/main.rs

@@ -15,6 +15,7 @@ use darkfi::{
 };
 
 mod demo;
+mod money;
 use crate::demo::demo;
 
 async fn _start() -> Result<()> {

+ 188 - 0
bin/daod/src/money/builder.rs

@@ -0,0 +1,188 @@
+use pasta_curves::group::ff::Field;
+use rand::rngs::OsRng;
+
+use super::{
+    partial::{PartialTransaction, PartialTransactionClearInput, PartialTransactionInput},
+    Transaction, TransactionClearInput, TransactionInput, TransactionOutput,
+};
+use darkfi::{
+    crypto::{
+        burn_proof::create_burn_proof,
+        keypair::{PublicKey, SecretKey},
+        merkle_node::MerkleNode,
+        mint_proof::create_mint_proof,
+        note::Note,
+        proof::ProvingKey,
+        schnorr::SchnorrSecret,
+        types::{DrkCoinBlind, DrkSerial, DrkTokenId, DrkValueBlind},
+    },
+    util::serial::Encodable,
+    Result,
+};
+
+pub struct TransactionBuilder {
+    pub clear_inputs: Vec<TransactionBuilderClearInputInfo>,
+    pub inputs: Vec<TransactionBuilderInputInfo>,
+    pub outputs: Vec<TransactionBuilderOutputInfo>,
+}
+
+pub struct TransactionBuilderClearInputInfo {
+    pub value: u64,
+    pub token_id: DrkTokenId,
+    pub signature_secret: SecretKey,
+}
+
+pub struct TransactionBuilderInputInfo {
+    pub leaf_position: incrementalmerkletree::Position,
+    pub merkle_path: Vec<MerkleNode>,
+    pub secret: SecretKey,
+    pub note: Note,
+}
+
+pub struct TransactionBuilderOutputInfo {
+    pub value: u64,
+    pub token_id: DrkTokenId,
+    pub public: PublicKey,
+}
+
+impl TransactionBuilder {
+    fn compute_remainder_blind(
+        clear_inputs: &[PartialTransactionClearInput],
+        input_blinds: &[DrkValueBlind],
+        output_blinds: &[DrkValueBlind],
+    ) -> DrkValueBlind {
+        let mut total = DrkValueBlind::zero();
+
+        for input in clear_inputs {
+            total += input.value_blind;
+        }
+
+        for input_blind in input_blinds {
+            total += input_blind;
+        }
+
+        for output_blind in output_blinds {
+            total -= output_blind;
+        }
+
+        total
+    }
+
+    pub fn build(self, mint_pk: &ProvingKey, burn_pk: &ProvingKey) -> Result<Transaction> {
+        assert!(self.clear_inputs.len() + self.inputs.len() > 0);
+
+        let mut clear_inputs = vec![];
+        let token_blind = DrkValueBlind::random(&mut OsRng);
+        for input in &self.clear_inputs {
+            let signature_public = PublicKey::from_secret(input.signature_secret);
+            let value_blind = DrkValueBlind::random(&mut OsRng);
+
+            let clear_input = PartialTransactionClearInput {
+                value: input.value,
+                token_id: input.token_id,
+                value_blind,
+                token_blind,
+                signature_public,
+            };
+            clear_inputs.push(clear_input);
+        }
+
+        let mut inputs = vec![];
+        let mut input_blinds = vec![];
+        let mut signature_secrets = vec![];
+        for input in self.inputs {
+            let value_blind = DrkValueBlind::random(&mut OsRng);
+            input_blinds.push(value_blind);
+
+            let signature_secret = SecretKey::random(&mut OsRng);
+
+            let (proof, revealed) = create_burn_proof(
+                burn_pk,
+                input.note.value,
+                input.note.token_id,
+                value_blind,
+                token_blind,
+                input.note.serial,
+                input.note.coin_blind,
+                input.secret,
+                input.leaf_position,
+                input.merkle_path,
+                signature_secret,
+            )?;
+
+            // First we make the tx then sign after
+            signature_secrets.push(signature_secret);
+
+            let input = PartialTransactionInput { burn_proof: proof, revealed };
+            inputs.push(input);
+        }
+
+        let mut outputs = vec![];
+        let mut output_blinds = vec![];
+        // This value_blind calc assumes there will always be at least a single output
+        assert!(self.outputs.len() > 0);
+
+        for (i, output) in self.outputs.iter().enumerate() {
+            let value_blind = if i == self.outputs.len() - 1 {
+                Self::compute_remainder_blind(&clear_inputs, &input_blinds, &output_blinds)
+            } else {
+                DrkValueBlind::random(&mut OsRng)
+            };
+            output_blinds.push(value_blind);
+
+            let serial = DrkSerial::random(&mut OsRng);
+            let coin_blind = DrkCoinBlind::random(&mut OsRng);
+
+            let (mint_proof, revealed) = create_mint_proof(
+                mint_pk,
+                output.value,
+                output.token_id,
+                value_blind,
+                token_blind,
+                serial,
+                coin_blind,
+                output.public,
+            )?;
+
+            // Encrypted note
+            let note = Note {
+                serial,
+                value: output.value,
+                token_id: output.token_id,
+                coin_blind,
+                value_blind,
+                token_blind,
+                memo: vec![],
+            };
+
+            let encrypted_note = note.encrypt(&output.public)?;
+
+            let output = TransactionOutput { mint_proof, revealed, enc_note: encrypted_note };
+            outputs.push(output);
+        }
+
+        let partial_tx = PartialTransaction { clear_inputs, inputs, outputs };
+
+        let mut unsigned_tx_data = vec![];
+        partial_tx.encode(&mut unsigned_tx_data)?;
+
+        let mut clear_inputs = vec![];
+        for (input, info) in partial_tx.clear_inputs.into_iter().zip(self.clear_inputs) {
+            let secret = info.signature_secret;
+            let signature = secret.sign(&unsigned_tx_data[..]);
+            let input = TransactionClearInput::from_partial(input, signature);
+            clear_inputs.push(input);
+        }
+
+        let mut inputs = vec![];
+        for (input, signature_secret) in
+            partial_tx.inputs.into_iter().zip(signature_secrets.into_iter())
+        {
+            let signature = signature_secret.sign(&unsigned_tx_data[..]);
+            let input = TransactionInput::from_partial(input, signature);
+            inputs.push(input);
+        }
+
+        Ok(Transaction { clear_inputs, inputs, outputs: partial_tx.outputs })
+    }
+}

+ 243 - 0
bin/daod/src/money/mod.rs

@@ -0,0 +1,243 @@
+use std::io;
+
+use log::error;
+use pasta_curves::group::Group;
+
+use darkfi::{
+    crypto::{
+        burn_proof::verify_burn_proof,
+        keypair::PublicKey,
+        mint_proof::verify_mint_proof,
+        note::EncryptedNote,
+        proof::VerifyingKey,
+        schnorr,
+        schnorr::SchnorrPublic,
+        types::{DrkTokenId, DrkValueBlind, DrkValueCommit},
+        util::{pedersen_commitment_base, pedersen_commitment_u64},
+        BurnRevealedValues, MintRevealedValues, Proof,
+    },
+    util::serial::{Encodable, SerialDecodable, SerialEncodable, VarInt},
+    Result, VerifyFailed, VerifyResult,
+};
+
+pub mod builder;
+pub mod partial;
+
+/// A DarkFi transaction
+#[derive(Debug, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)]
+pub struct Transaction {
+    /// Clear inputs
+    pub clear_inputs: Vec<TransactionClearInput>,
+    /// Anonymous inputs
+    pub inputs: Vec<TransactionInput>,
+    /// Anonymous outputs
+    pub outputs: Vec<TransactionOutput>,
+}
+
+/// A transaction's clear input
+#[derive(Debug, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)]
+pub struct TransactionClearInput {
+    /// Input's value (amount)
+    pub value: u64,
+    /// Input's token ID
+    pub token_id: DrkTokenId,
+    /// Blinding factor for `value`
+    pub value_blind: DrkValueBlind,
+    /// Blinding factor for `token_id`
+    pub token_blind: DrkValueBlind,
+    /// Public key for the signature
+    pub signature_public: PublicKey,
+    /// Transaction signature
+    pub signature: schnorr::Signature,
+}
+
+/// A transaction's anonymous input
+#[derive(Debug, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)]
+pub struct TransactionInput {
+    /// Zero-knowledge proof for the input
+    pub burn_proof: Proof,
+    /// Public inputs for the zero-knowledge proof
+    pub revealed: BurnRevealedValues,
+    /// Input's signature
+    pub signature: schnorr::Signature,
+}
+
+/// A transaction's anonymous output
+#[derive(Debug, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)]
+pub struct TransactionOutput {
+    /// Zero-knowledge proof for the output
+    pub mint_proof: Proof,
+    /// Public inputs for the zero-knowledge proof
+    pub revealed: MintRevealedValues,
+    /// The encrypted note
+    pub enc_note: EncryptedNote,
+}
+
+impl Transaction {
+    /// Verify the transaction
+    pub fn verify(&self, mint_vk: &VerifyingKey, burn_vk: &VerifyingKey) -> VerifyResult<()> {
+        // Transaction must have minimum 1 clear or anon input, and 1 output
+        if self.clear_inputs.len() + self.inputs.len() == 0 {
+            error!("tx::verify(): Missing inputs");
+            return Err(VerifyFailed::LackingInputs)
+        }
+        if self.outputs.len() == 0 {
+            error!("tx::verify(): Missing outputs");
+            return Err(VerifyFailed::LackingOutputs)
+        }
+
+        // Accumulator for the value commitments
+        let mut valcom_total = DrkValueCommit::identity();
+
+        // Add values from the clear inputs
+        for input in &self.clear_inputs {
+            valcom_total += pedersen_commitment_u64(input.value, input.value_blind);
+        }
+
+        // Add values from the inputs
+        for (i, input) in self.inputs.iter().enumerate() {
+            match verify_burn_proof(burn_vk, &input.burn_proof, &input.revealed) {
+                Ok(()) => valcom_total += &input.revealed.value_commit,
+                Err(e) => {
+                    error!("tx::verify(): Failed to verify burn proof {}: {}", i, e);
+                    return Err(VerifyFailed::BurnProof(i))
+                }
+            }
+        }
+
+        // Subtract values from the outputs
+        for (i, output) in self.outputs.iter().enumerate() {
+            match verify_mint_proof(mint_vk, &output.mint_proof, &output.revealed) {
+                Ok(()) => valcom_total -= &output.revealed.value_commit,
+                Err(e) => {
+                    error!("tx::verify(): Failed to verify mint proof {}: {}", i, e);
+                    return Err(VerifyFailed::MintProof(i))
+                }
+            }
+        }
+
+        // If the accumulator is not back in its initial state,
+        // there's a value mismatch.
+        if valcom_total != DrkValueCommit::identity() {
+            error!("tx::verify(): Missing funds");
+            return Err(VerifyFailed::MissingFunds)
+        }
+
+        // Verify that the token commitments match
+        if !self.verify_token_commitments() {
+            error!("tx::verify(): Token ID mismatch");
+            return Err(VerifyFailed::TokenMismatch)
+        }
+
+        // Verify the available signatures
+        let mut unsigned_tx_data = vec![];
+        self.encode_without_signature(&mut unsigned_tx_data)?;
+
+        for (i, input) in self.clear_inputs.iter().enumerate() {
+            let public = &input.signature_public;
+            if !public.verify(&unsigned_tx_data[..], &input.signature) {
+                error!("tx::verify(): Failed to verify Clear Input signature {}", i);
+                return Err(VerifyFailed::ClearInputSignature(i))
+            }
+        }
+
+        for (i, input) in self.inputs.iter().enumerate() {
+            let public = &input.revealed.signature_public;
+            if !public.verify(&unsigned_tx_data[..], &input.signature) {
+                error!("tx::verify(): Failed to verify Input signature {}", i);
+                return Err(VerifyFailed::InputSignature(i))
+            }
+        }
+
+        Ok(())
+    }
+
+    pub fn encode_without_signature<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        let mut len = 0;
+        len += self.clear_inputs.encode_without_signature(&mut s)?;
+        len += self.inputs.encode_without_signature(&mut s)?;
+        len += self.outputs.encode(s)?;
+        Ok(len)
+    }
+
+    fn verify_token_commitments(&self) -> bool {
+        assert_ne!(self.outputs.len(), 0);
+        let token_commit_value = self.outputs[0].revealed.token_commit;
+
+        let mut failed =
+            self.inputs.iter().any(|input| input.revealed.token_commit != token_commit_value);
+
+        failed = failed ||
+            self.outputs.iter().any(|output| output.revealed.token_commit != token_commit_value);
+
+        failed = failed ||
+            self.clear_inputs.iter().any(|input| {
+                pedersen_commitment_base(input.token_id, input.token_blind) != token_commit_value
+            });
+        !failed
+    }
+}
+
+impl TransactionClearInput {
+    fn from_partial(
+        partial: partial::PartialTransactionClearInput,
+        signature: schnorr::Signature,
+    ) -> Self {
+        Self {
+            value: partial.value,
+            token_id: partial.token_id,
+            value_blind: partial.value_blind,
+            token_blind: partial.token_blind,
+            signature_public: partial.signature_public,
+            signature,
+        }
+    }
+
+    fn encode_without_signature<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        let mut len = 0;
+        len += self.value.encode(&mut s)?;
+        len += self.token_id.encode(&mut s)?;
+        len += self.value_blind.encode(&mut s)?;
+        len += self.token_blind.encode(&mut s)?;
+        len += self.signature_public.encode(s)?;
+        Ok(len)
+    }
+}
+
+impl TransactionInput {
+    pub fn from_partial(
+        partial: partial::PartialTransactionInput,
+        signature: schnorr::Signature,
+    ) -> Self {
+        Self { burn_proof: partial.burn_proof, revealed: partial.revealed, signature }
+    }
+
+    fn encode_without_signature<S: io::Write>(&self, mut s: S) -> Result<usize> {
+        let mut len = 0;
+        len += self.burn_proof.encode(&mut s)?;
+        len += self.revealed.encode(&mut s)?;
+        Ok(len)
+    }
+}
+
+trait EncodableWithoutSignature {
+    fn encode_without_signature<S: io::Write>(&self, s: S) -> Result<usize>;
+}
+
+macro_rules! impl_vec_without_signature {
+    ($type: ty) => {
+        impl EncodableWithoutSignature for Vec<$type> {
+            #[inline]
+            fn encode_without_signature<S: io::Write>(&self, mut s: S) -> Result<usize> {
+                let mut len = 0;
+                len += VarInt(self.len() as u64).encode(&mut s)?;
+                for c in self.iter() {
+                    len += c.encode_without_signature(&mut s)?;
+                }
+                Ok(len)
+            }
+        }
+    };
+}
+impl_vec_without_signature!(TransactionClearInput);
+impl_vec_without_signature!(TransactionInput);

+ 31 - 0
bin/daod/src/money/partial.rs

@@ -0,0 +1,31 @@
+use super::TransactionOutput;
+use darkfi::{
+    crypto::{
+        keypair::PublicKey,
+        types::{DrkTokenId, DrkValueBlind},
+        BurnRevealedValues, Proof,
+    },
+    util::serial::{SerialDecodable, SerialEncodable},
+};
+
+#[derive(Clone, SerialEncodable, SerialDecodable)]
+pub struct PartialTransaction {
+    pub clear_inputs: Vec<PartialTransactionClearInput>,
+    pub inputs: Vec<PartialTransactionInput>,
+    pub outputs: Vec<TransactionOutput>,
+}
+
+#[derive(Clone, SerialEncodable, SerialDecodable)]
+pub struct PartialTransactionClearInput {
+    pub value: u64,
+    pub token_id: DrkTokenId,
+    pub value_blind: DrkValueBlind,
+    pub token_blind: DrkValueBlind,
+    pub signature_public: PublicKey,
+}
+
+#[derive(Clone, SerialEncodable, SerialDecodable)]
+pub struct PartialTransactionInput {
+    pub burn_proof: Proof,
+    pub revealed: BurnRevealedValues,
+}