diff --git a/src/contract/dao/Cargo.toml b/src/contract/dao/Cargo.toml index 996230e98..99f22ab22 100644 --- a/src/contract/dao/Cargo.toml +++ b/src/contract/dao/Cargo.toml @@ -25,7 +25,7 @@ rand = { version = "0.8.5", optional = true } [dev-dependencies] async-std = {version = "1.12.0", features = ["attributes"]} darkfi = {path = "../../../", features = ["tx", "blockchain"]} -darkfi-money-contract = { path = "../money", features = ["client"] } +darkfi-money-contract = { path = "../money", features = ["client", "no-entrypoint"] } simplelog = "0.12.0" sled = "0.34.7" sqlx = {version = "0.6.2", features = ["runtime-async-std-native-tls", "sqlite"]} diff --git a/src/contract/dao/src/client.rs b/src/contract/dao/src/dao_client.rs similarity index 100% rename from src/contract/dao/src/client.rs rename to src/contract/dao/src/dao_client.rs diff --git a/src/contract/dao/src/lib.rs b/src/contract/dao/src/lib.rs index be6037a8a..94b7a82cf 100644 --- a/src/contract/dao/src/lib.rs +++ b/src/contract/dao/src/lib.rs @@ -27,8 +27,12 @@ pub mod state; pub mod note; #[cfg(feature = "client")] -/// Transaction building API for clients interacting with this contract -pub mod client; +/// Transaction building API for clients interacting with DAO contract +pub mod dao_client; + +#[cfg(feature = "client")] +/// Transaction building API for clients interacting with money contract +pub mod money_client; // These are the zkas circuit namespaces pub const DAO_CONTRACT_ZKAS_DAO_MINT_NS: &str = "DaoMint"; diff --git a/src/contract/dao/src/money_client.rs b/src/contract/dao/src/money_client.rs new file mode 100644 index 000000000..5e6c17954 --- /dev/null +++ b/src/contract/dao/src/money_client.rs @@ -0,0 +1,272 @@ +/* This file is part of DarkFi (https://dark.fi) + * + * Copyright (C) 2020-2022 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 . + */ + +use chacha20poly1305::{AeadInPlace, ChaCha20Poly1305, KeyInit}; +use darkfi::{ + consensus::leadcoin::LeadCoin, + zk::{ + proof::{Proof, ProvingKey}, + vm::ZkCircuit, + vm_stack::Witness, + }, + zkas::ZkBinary, + ClientFailed, Error, Result, +}; +use darkfi_sdk::{ + crypto::{ + constants::MERKLE_DEPTH, + diffie_hellman::{kdf_sapling, sapling_ka_agree}, + pedersen::{pedersen_commitment_base, pedersen_commitment_u64, ValueBlind, ValueCommit}, + poseidon_hash, Keypair, MerkleNode, Nullifier, PublicKey, SecretKey, TokenId, + }, + incrementalmerkletree, + incrementalmerkletree::{bridgetree::BridgeTree, Hashable, Tree}, + pasta::{ + arithmetic::CurveAffine, + group::{ + ff::{Field, PrimeField}, + Curve, + }, + pallas, + }, +}; +use darkfi_serial::{serialize, Decodable, Encodable, SerialDecodable, SerialEncodable}; +use halo2_proofs::circuit::Value; +use log::{debug, error, info}; +use rand::rngs::OsRng; + +use darkfi_money_contract::{ + client::{create_transfer_burn_proof, create_transfer_mint_proof, Note}, + state::{ClearInput, Input, MoneyTransferParams, Output}, +}; + +/* +use darkfi::{ + crypto::{ + burn_proof::create_burn_proof, + mint_proof::create_mint_proof, + types::{ + DrkCoinBlind, DrkSerial, DrkSpendHook, DrkUserData, DrkUserDataBlind, DrkValueBlind, + }, + }, + Result, +}; + +use crate::{ + contract::money::{ + transfer::validate::{CallData, ClearInput, Input, Output}, + CONTRACT_ID, + }, + note, + util::{FuncCall, ZkContractInfo, ZkContractTable}, +}; +*/ + +/* +#[derive(Clone, SerialEncodable, SerialDecodable)] +pub struct Note { + pub serial: DrkSerial, + pub value: u64, + pub token_id: TokenId, + pub spend_hook: DrkSpendHook, + pub user_data: DrkUserData, + pub coin_blind: DrkCoinBlind, + pub value_blind: DrkValueBlind, + pub token_blind: DrkValueBlind, +} +*/ + +pub struct Builder { + pub clear_inputs: Vec, + pub inputs: Vec, + pub outputs: Vec, +} + +pub struct BuilderClearInputInfo { + pub value: u64, + pub token_id: TokenId, + pub signature_secret: SecretKey, +} + +pub struct BuilderInputInfo { + pub leaf_position: incrementalmerkletree::Position, + pub merkle_path: Vec, + pub secret: SecretKey, + pub note: Note, + pub user_data_blind: pallas::Base, + pub value_blind: ValueBlind, + pub signature_secret: SecretKey, +} + +pub struct BuilderOutputInfo { + pub value: u64, + pub token_id: TokenId, + pub public: PublicKey, + pub serial: pallas::Base, + pub coin_blind: pallas::Base, + pub spend_hook: pallas::Base, + pub user_data: pallas::Base, +} + +impl Builder { + fn compute_remainder_blind( + clear_inputs: &[ClearInput], + input_blinds: &[ValueBlind], + output_blinds: &[ValueBlind], + ) -> ValueBlind { + let mut total = ValueBlind::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_zkbin: &ZkBinary, + mint_pk: &ProvingKey, + burn_zkbin: &ZkBinary, + burn_pk: &ProvingKey, + ) -> Result<(MoneyTransferParams, Vec)> { + assert!(self.clear_inputs.len() + self.inputs.len() > 0); + + let mut clear_inputs = vec![]; + let token_blind = ValueBlind::random(&mut OsRng); + for input in &self.clear_inputs { + let signature_public = PublicKey::from_secret(input.signature_secret); + let value_blind = ValueBlind::random(&mut OsRng); + + let clear_input = ClearInput { + value: input.value, + token_id: input.token_id, + value_blind, + token_blind, + signature_public, + }; + clear_inputs.push(clear_input); + } + + let mut proofs = vec![]; + let mut inputs = vec![]; + let mut input_blinds = vec![]; + + for input in self.inputs { + let value_blind = input.value_blind; + input_blinds.push(value_blind); + + // Note from the previous output + let note = input.note.clone(); + + let (proof, revealed) = create_transfer_burn_proof( + burn_zkbin, + burn_pk, + note.value, + note.token_id, + value_blind, + token_blind, + note.serial, + note.spend_hook, + note.user_data, + input.user_data_blind, + note.coin_blind, + input.secret, + input.leaf_position, + input.merkle_path.clone(), + input.signature_secret, + )?; + + proofs.push(proof); + + let input = Input { + value_commit: revealed.value_commit, + token_commit: revealed.token_commit, + nullifier: revealed.nullifier, + merkle_root: revealed.merkle_root, + spend_hook: revealed.spend_hook, + user_data_enc: revealed.user_data_enc, + signature_public: revealed.signature_public, + }; + 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.is_empty()); + + 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 { + ValueBlind::random(&mut OsRng) + }; + output_blinds.push(value_blind); + + let serial = output.serial; + let coin_blind = output.coin_blind; + + let (proof, revealed) = create_transfer_mint_proof( + mint_zkbin, + mint_pk, + output.value, + output.token_id, + value_blind, + token_blind, + serial, + output.spend_hook, + output.user_data, + coin_blind, + output.public, + )?; + + proofs.push(proof); + + let note = Note { + serial, + value: output.value, + token_id: output.token_id, + spend_hook: output.spend_hook, + user_data: output.user_data, + coin_blind, + value_blind, + token_blind, + memo: Vec::new(), + }; + + //let encrypted_note = note.encrypt(&output.public)?; + /* + let encrypted_note = note::encrypt(¬e, &output.public)?; + + let output = Output { revealed, enc_note: encrypted_note }; + outputs.push(output); + */ + } + + Ok((MoneyTransferParams { clear_inputs, inputs, outputs }, proofs)) + } +} diff --git a/src/contract/dao/tests/integration.rs b/src/contract/dao/tests/integration.rs index 8f93ca162..7a8a7846f 100644 --- a/src/contract/dao/tests/integration.rs +++ b/src/contract/dao/tests/integration.rs @@ -28,7 +28,7 @@ use log::{debug, info}; use rand::rngs::OsRng; use darkfi_dao_contract::{ - client::{build_dao_mint_tx, MerkleTree}, + dao_client::{build_dao_mint_tx, MerkleTree}, DaoFunction, }; @@ -60,11 +60,11 @@ async fn integration_test() -> Result<()> { let mut th = DaoTestHarness::new().await?; // Money parameters - //let xdrk_supply = 1_000_000; - //let xrdk_token_id = TokenId::from(pallas::Base::random(&mut OsRng)); + let xdrk_supply = 1_000_000; + let xrdk_token_id = TokenId::from(pallas::Base::random(&mut OsRng)); // Governance token parameters - //let gdrk_supply = 1_000_000; + let gdrk_supply = 1_000_000; let gdrk_token_id = TokenId::from(pallas::Base::random(&mut OsRng)); // DAO parameters @@ -131,7 +131,11 @@ async fn integration_test() -> Result<()> { // ======================================================= debug!(target: "demo", "Stage 2. Minting treasury token"); + // We use this to receive coins + //let mut cache = WalletCache::new(); + let mut th = MoneyTestHarness::new().await?; + //let (params, proofs) = builder.build(&zk_bins)?; Ok(()) } diff --git a/src/contract/money/src/client.rs b/src/contract/money/src/client.rs index 56bb48e44..adca60b62 100644 --- a/src/contract/money/src/client.rs +++ b/src/contract/money/src/client.rs @@ -47,12 +47,15 @@ use darkfi_sdk::{ incrementalmerkletree::{bridgetree::BridgeTree, Hashable, Tree}, pasta::{ arithmetic::CurveAffine, - group::{ff::PrimeField, Curve}, + group::{ + ff::{Field, PrimeField}, + Curve, + }, pallas, }, }; use darkfi_serial::{serialize, Decodable, Encodable, SerialDecodable, SerialEncodable}; -use halo2_proofs::{arithmetic::Field, circuit::Value}; +use halo2_proofs::circuit::Value; use log::{debug, error, info}; use rand::rngs::OsRng; @@ -143,6 +146,11 @@ pub struct Note { pub value: u64, /// Token ID of the coin pub token_id: TokenId, + /// Spend hook used for protocol owned liquidity. + /// Specifies which contract owns this coin. + pub spend_hook: pallas::Base, + /// User data used by protocol when spend hook is enabled. + pub user_data: pallas::Base, /// Blinding factor for the coin bulla pub coin_blind: pallas::Base, /// Blinding factor for the value pedersen commitment @@ -207,6 +215,9 @@ impl EncryptedNote { } } +// TODO: we can put all these in an internal module like: +// money_transfer::builder::ClearInputInfo + struct TransactionBuilderClearInputInfo { pub value: u64, pub token_id: TokenId, @@ -226,7 +237,7 @@ struct TransactionBuilderOutputInfo { pub public_key: PublicKey, } -struct TransferBurnRevealed { +pub struct TransferBurnRevealed { pub value_commit: ValueCommit, pub token_commit: ValueCommit, pub nullifier: Nullifier, @@ -321,7 +332,7 @@ impl TransferBurnRevealed { } } -struct TransferMintRevealed { +pub struct TransferMintRevealed { pub coin: Coin, pub value_commit: ValueCommit, pub token_commit: ValueCommit, @@ -376,7 +387,7 @@ impl TransferMintRevealed { } #[allow(clippy::too_many_arguments)] -fn create_transfer_mint_proof( +pub fn create_transfer_mint_proof( zkbin: &ZkBinary, pk: &ProvingKey, value: u64, @@ -424,7 +435,7 @@ fn create_transfer_mint_proof( } #[allow(clippy::too_many_arguments)] -fn create_transfer_burn_proof( +pub fn create_transfer_burn_proof( zkbin: &ZkBinary, pk: &ProvingKey, value: u64, @@ -799,6 +810,8 @@ pub fn build_half_swap_tx( serial, value: output.value, token_id: output.token_id, + spend_hook: pallas::Base::zero(), + user_data: pallas::Base::zero(), coin_blind, value_blind: value_recv_blind, token_blind: token_recv_blind, @@ -1025,6 +1038,8 @@ pub fn build_transfer_tx( serial, value: output.value, token_id: output.token_id, + spend_hook: pallas::Base::zero(), + user_data: pallas::Base::zero(), coin_blind, value_blind, token_blind, @@ -1236,6 +1251,8 @@ pub fn build_unstake_tx( serial, value: coin.value, token_id: token_id_recv, + spend_hook: pallas::Base::zero(), + user_data: pallas::Base::zero(), coin_blind, value_blind, token_blind: token_recv_blind, @@ -1290,6 +1307,8 @@ mod tests { serial: pallas::Base::random(&mut OsRng), value: 100, token_id: TokenId::from(pallas::Base::random(&mut OsRng)), + spend_hook: pallas::Base::zero(), + user_data: pallas::Base::zero(), coin_blind: pallas::Base::random(&mut OsRng), value_blind: pallas::Scalar::random(&mut OsRng), token_blind: pallas::Scalar::random(&mut OsRng),