| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- use std::io;
- use darkfi_serial::{Encodable, SerialDecodable, SerialEncodable, VarInt};
- use log::error;
- use pasta_curves::group::Group;
- use crate::{
- 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,
- },
- 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.is_empty() {
- 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);
|