Просмотр исходного кода

contract/money/client: Reworked Swap API

parazyd 3 лет назад
Родитель
Сommit
86a66dae86

+ 3 - 0
src/contract/money/src/client/mod.rs

@@ -35,6 +35,9 @@ use darkfi_serial::{SerialDecodable, SerialEncodable};
 /// `Money::TransferV1` API
 pub mod transfer_v1;
 
+/// `Money::OtcSwapV1` API
+pub mod swap_v1;
+
 // Wallet SQL table constant names. These have to represent the `wallet.sql`
 // SQL schema.
 // TODO: They should also be prefixed with the contract ID to avoid collisions.

+ 205 - 0
src/contract/money/src/client/swap_v1.rs

@@ -0,0 +1,205 @@
+/* This file is part of DarkFi (https://dark.fi)
+ *
+ * Copyright (C) 2020-2023 Dyne.org foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+
+//! This API is crufty. Please rework it into something nice to read and nice to use.
+
+use darkfi::{
+    zk::{Proof, ProvingKey},
+    zkas::ZkBinary,
+    ClientFailed, Result,
+};
+use darkfi_sdk::{
+    crypto::{
+        note::AeadEncryptedNote, pasta_prelude::*, MerkleTree, PublicKey, SecretKey, TokenId,
+    },
+    incrementalmerkletree::Tree,
+    pasta::pallas,
+};
+use darkfi_serial::serialize;
+use log::{debug, info};
+use rand::rngs::OsRng;
+
+use crate::{
+    client::{
+        transfer_v1::{
+            create_transfer_burn_proof, create_transfer_mint_proof, TransactionBuilderInputInfo,
+            TransactionBuilderOutputInfo,
+        },
+        MoneyNote, OwnCoin,
+    },
+    model::{Input, MoneyTransferParamsV1, Output},
+};
+
+pub struct SwapCallDebris {
+    pub params: MoneyTransferParamsV1,
+    pub proofs: Vec<Proof>,
+}
+
+/// Struct holding necessary information to build a `Money::OtcSwapV1` contract call.
+/// This is used to build half of the swap transaction, so both parties have to build
+/// their halves and combine them.
+pub struct SwapCallBuilder {
+    /// Party's public key for receiving the output
+    pub pubkey: PublicKey,
+    /// The value of the party's input to swap (send)
+    pub value_send: u64,
+    /// The token ID of the party's input to swap (send)
+    pub token_id_send: TokenId,
+    /// The value of the party's output to receive
+    pub value_recv: u64,
+    /// The token ID of the party's output to receive
+    pub token_id_recv: TokenId,
+    /// User data blind for the party's input
+    pub user_data_blind_send: pallas::Base,
+    /// Spend hook for the party's output
+    pub spend_hook_recv: pallas::Base,
+    /// User data for the party's output
+    pub user_data_recv: pallas::Base,
+    /// The blinds to be used for value pedersen commitments
+    /// `[0]` is used for input 0 and output 1, and `[1]` is
+    /// used for input 1 and output 0. The same applies to
+    /// `token_blinds`.
+    pub value_blinds: [pallas::Scalar; 2],
+    /// The blinds to be used for token ID pedersen commitments
+    pub token_blinds: [pallas::Scalar; 2],
+    /// The coin to be used as the input to the swap
+    pub coin: OwnCoin,
+    /// Merkle tree of coins used to create inclusion proofs
+    pub tree: MerkleTree,
+    /// `Mint_V1` zkas circuit ZkBinary
+    pub mint_zkbin: ZkBinary,
+    /// Proving key for the `Mint_V1` zk circuit
+    pub mint_pk: ProvingKey,
+    /// `Burn_V1` zkas circuit ZkBinary
+    pub burn_zkbin: ZkBinary,
+    /// Proving key for the `Burn_V1` zk circuit
+    pub burn_pk: ProvingKey,
+}
+
+impl SwapCallBuilder {
+    pub fn build(&self) -> Result<SwapCallDebris> {
+        debug!("Building half of Money::OtcSwapV1 contract call");
+        assert!(self.value_send != 0);
+        assert!(self.value_recv != 0);
+        assert!(self.token_id_send.inner() != pallas::Base::zero());
+        assert!(self.token_id_recv.inner() != pallas::Base::zero());
+
+        if self.coin.note.value != self.value_send {
+            return Err(ClientFailed::InvalidAmount(self.coin.note.value).into())
+        }
+
+        if self.coin.note.token_id != self.token_id_send {
+            return Err(ClientFailed::InvalidTokenId.into())
+        }
+
+        let leaf_position = self.coin.leaf_position;
+        let root = self.tree.root(0).unwrap();
+        let merkle_path = self.tree.authentication_path(leaf_position, &root).unwrap();
+
+        let input = TransactionBuilderInputInfo {
+            leaf_position,
+            merkle_path,
+            secret: self.coin.secret,
+            note: self.coin.note.clone(),
+        };
+
+        let output = TransactionBuilderOutputInfo {
+            value: self.value_recv,
+            token_id: self.token_id_recv,
+            public_key: self.pubkey,
+        };
+
+        // Now we fill this with necessary stuff
+        let mut params =
+            MoneyTransferParamsV1 { clear_inputs: vec![], inputs: vec![], outputs: vec![] };
+
+        // Create a new ephemeral secret key
+        let signature_secret = SecretKey::random(&mut OsRng);
+
+        let mut proofs = vec![];
+        info!("Creating burn proof for input");
+        let (proof, public_inputs) = create_transfer_burn_proof(
+            &self.burn_zkbin,
+            &self.burn_pk,
+            &input,
+            self.value_blinds[0],
+            self.token_blinds[0],
+            self.user_data_blind_send,
+            signature_secret,
+        )?;
+
+        params.inputs.push(Input {
+            value_commit: public_inputs.value_commit,
+            token_commit: public_inputs.token_commit,
+            nullifier: public_inputs.nullifier,
+            merkle_root: public_inputs.merkle_root,
+            spend_hook: public_inputs.spend_hook,
+            user_data_enc: public_inputs.user_data_enc,
+            signature_public: public_inputs.signature_public,
+        });
+
+        proofs.push(proof);
+
+        // For the output, we create a new serial and coin blind
+        let serial = pallas::Base::random(&mut OsRng);
+        let coin_blind = pallas::Base::random(&mut OsRng);
+
+        info!("Creating mint proof for output");
+        let (proof, public_inputs) = create_transfer_mint_proof(
+            &self.mint_zkbin,
+            &self.mint_pk,
+            &output,
+            self.value_blinds[1],
+            self.token_blinds[1],
+            serial,
+            self.spend_hook_recv,
+            self.user_data_recv,
+            coin_blind,
+        )?;
+
+        proofs.push(proof);
+
+        // Encrypted note
+        let note = MoneyNote {
+            serial,
+            value: output.value,
+            token_id: output.token_id,
+            spend_hook: self.spend_hook_recv,
+            user_data: self.user_data_recv,
+            coin_blind,
+            value_blind: self.value_blinds[1],
+            token_blind: self.token_blinds[1],
+            // Here we store our secret key we use for signing
+            memo: serialize(&signature_secret),
+        };
+
+        let encrypted_note = AeadEncryptedNote::encrypt(&note, &self.pubkey, &mut OsRng)?;
+
+        params.outputs.push(Output {
+            value_commit: public_inputs.value_commit,
+            token_commit: public_inputs.token_commit,
+            coin: public_inputs.coin,
+            note: encrypted_note,
+        });
+
+        // Now we should have all the params, zk proofs, and signature secrets.
+        // We return it all and let the caller deal with it.
+        let debris = SwapCallDebris { params, proofs };
+        Ok(debris)
+    }
+}

+ 2 - 2
src/contract/money/src/client/transfer_v1.rs

@@ -363,7 +363,7 @@ impl TransferCallBuilder {
     }
 }
 
-fn create_transfer_burn_proof(
+pub(crate) fn create_transfer_burn_proof(
     zkbin: &ZkBinary,
     pk: &ProvingKey,
     input: &TransactionBuilderInputInfo,
@@ -439,7 +439,7 @@ fn create_transfer_burn_proof(
     Ok((proof, public_inputs))
 }
 
-fn create_transfer_mint_proof(
+pub(crate) fn create_transfer_mint_proof(
     zkbin: &ZkBinary,
     pk: &ProvingKey,
     output: &TransactionBuilderOutputInfo,