dao: begin migrating dao code over to new infra

This commit is contained in:
x
2022-12-23 11:47:59 +01:00
parent 0a961cc822
commit 525e57e97a
8 changed files with 236 additions and 13 deletions

1
Cargo.lock generated
View File

@@ -1172,6 +1172,7 @@ name = "darkfi-dao-contract"
version = "0.3.0"
dependencies = [
"async-std",
"chacha20poly1305",
"darkfi",
"darkfi-money-contract",
"darkfi-sdk",

View File

@@ -22,8 +22,8 @@ use async_std::sync::{Arc, RwLock};
use darkfi_sdk::{
crypto::{
constants::MERKLE_DEPTH,
contract_id::MONEY_CONTRACT_ID,
//contract_id::{DAO_CONTRACT_ID, MONEY_CONTRACT_ID},
//contract_id::MONEY_CONTRACT_ID,
contract_id::{DAO_CONTRACT_ID, MONEY_CONTRACT_ID},
schnorr::{SchnorrPublic, SchnorrSecret},
MerkleNode,
PublicKey,
@@ -146,7 +146,7 @@ impl ValidatorState {
// The faucet pubkeys are pubkeys which are allowed to create clear inputs
// in the money contract.
let money_contract_deploy_payload = serialize(&faucet_pubkeys);
//let dao_contract_deploy_payload = vec![];
let dao_contract_deploy_payload = vec![];
// In this hashmap, we keep references to ZK proof verifying keys needed
// for the circuits our native contracts provide.
@@ -159,14 +159,12 @@ impl ValidatorState {
include_bytes!("../contract/money/money_contract.wasm").to_vec(),
money_contract_deploy_payload,
),
/*
(
"DAO Contract",
*DAO_CONTRACT_ID,
include_bytes!("../contract/dao/dao_contract.wasm").to_vec(),
dao_contract_deploy_payload,
),
*/
];
info!("Deploying native wasm contracts");

View File

@@ -15,6 +15,7 @@ darkfi-money-contract = { path = "../money", features = ["no-entrypoint"] }
# The following dependencies are used for the client API and
# probably shouldn't be in WASM
chacha20poly1305 = { version = "0.10.1", optional = true }
darkfi = { path = "../../../", features = ["zk", "rpc"], optional = true }
halo2_proofs = { version = "0.2.0", optional = true }
log = { version = "0.4.17", optional = true }
@@ -39,6 +40,7 @@ no-entrypoint = []
client = [
"darkfi",
"rand",
"chacha20poly1305",
"log",
"halo2_proofs",
]

View File

@@ -22,14 +22,83 @@ use darkfi::{
Result,
};
use darkfi_sdk::{
crypto::{poseidon_hash, PublicKey, SecretKey, TokenId},
crypto::{
coin::Coin, constants::MERKLE_DEPTH, poseidon_hash, MerkleNode, PublicKey, SecretKey,
TokenId,
},
incrementalmerkletree::{bridgetree::BridgeTree, Tree},
pasta::pallas,
};
use halo2_proofs::circuit::Value;
use log::debug;
use rand::rngs::OsRng;
use crate::state::{DaoBulla, DaoMintParams};
use crate::{
note::EncryptedNote2,
state::{DaoBulla, DaoMintParams},
};
pub type MerkleTree = BridgeTree<MerkleNode, { MERKLE_DEPTH }>;
/*
pub struct OwnCoin {
pub coin: Coin,
pub note: money::transfer::wallet::Note,
pub leaf_position: incrementalmerkletree::Position,
}
pub struct WalletCache {
// Normally this would be a HashMap, but SecretKey is not Hash-able
// TODO: This can be HashableBase
cache: Vec<(SecretKey, Vec<OwnCoin>)>,
/// The entire Merkle tree state
tree: MerkleTree,
}
impl Default for WalletCache {
fn default() -> Self {
Self { cache: Vec::new(), tree: MerkleTree::new(100) }
}
}
impl WalletCache {
pub fn new() -> Self {
Self { cache: Vec::new(), tree: MerkleTree::new(100) }
}
/// Must be called at the start to begin tracking received coins for this secret.
pub fn track(&mut self, secret: SecretKey) {
self.cache.push((secret, Vec::new()));
}
/// Get all coins received by this secret key
/// track() must be called on this secret before calling this or the function will panic.
pub fn get_received(&mut self, secret: &SecretKey) -> Vec<OwnCoin> {
for (other_secret, own_coins) in self.cache.iter_mut() {
if *secret == *other_secret {
// clear own_coins vec, and return current contents
return std::mem::take(own_coins)
}
}
panic!("you forget to track() this secret!");
}
pub fn try_decrypt_note(&mut self, coin: Coin, ciphertext: &EncryptedNote2) {
// Add the new coins to the Merkle tree
let node = MerkleNode::from(coin.inner());
self.tree.append(&node);
// Loop through all our secret keys...
for (secret, own_coins) in self.cache.iter_mut() {
// .. attempt to decrypt the note ...
if let Ok(note) = ciphertext.decrypt(secret) {
let leaf_position = self.tree.witness().expect("coin should be in tree");
own_coins.push(OwnCoin { coin, note, leaf_position });
}
}
}
}
*/
struct DaoMintRevealed {
pub bulla: DaoBulla,

View File

@@ -23,6 +23,9 @@ pub mod entrypoint;
pub mod state;
#[cfg(feature = "client")]
pub mod note;
#[cfg(feature = "client")]
/// Transaction building API for clients interacting with this contract
pub mod client;

View File

@@ -0,0 +1,119 @@
/* 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 <https://www.gnu.org/licenses/>.
*/
use chacha20poly1305::{AeadInPlace, ChaCha20Poly1305, KeyInit};
use darkfi_sdk::crypto::{
diffie_hellman::{kdf_sapling, sapling_ka_agree},
PublicKey, SecretKey,
};
use darkfi_serial::{Decodable, Encodable, SerialDecodable, SerialEncodable};
use rand::rngs::OsRng;
use darkfi::{Error, Result};
pub const AEAD_TAG_SIZE: usize = 16;
pub fn encrypt<T: Encodable>(note: &T, public: &PublicKey) -> Result<EncryptedNote2> {
let ephem_secret = SecretKey::random(&mut OsRng);
let ephem_public = PublicKey::from_secret(ephem_secret);
let shared_secret = sapling_ka_agree(&ephem_secret, public);
let key = kdf_sapling(&shared_secret, &ephem_public);
let mut input = Vec::new();
note.encode(&mut input)?;
let input_len = input.len();
let mut ciphertext = vec![0_u8; input_len + AEAD_TAG_SIZE];
ciphertext[..input_len].copy_from_slice(&input);
ChaCha20Poly1305::new(key.as_ref().into())
.encrypt_in_place([0u8; 12][..].into(), &[], &mut ciphertext)
.unwrap();
Ok(EncryptedNote2 { ciphertext, ephem_public })
}
#[derive(Debug, Clone, PartialEq, Eq, SerialEncodable, SerialDecodable)]
pub struct EncryptedNote2 {
ciphertext: Vec<u8>,
ephem_public: PublicKey,
}
impl EncryptedNote2 {
pub fn decrypt<T: Decodable>(&self, secret: &SecretKey) -> Result<T> {
let shared_secret = sapling_ka_agree(secret, &self.ephem_public);
let key = kdf_sapling(&shared_secret, &self.ephem_public);
let ciphertext_len = self.ciphertext.len();
let mut plaintext = vec![0_u8; ciphertext_len];
plaintext.copy_from_slice(&self.ciphertext);
match ChaCha20Poly1305::new(key.as_ref().into()).decrypt_in_place(
[0u8; 12][..].into(),
&[],
&mut plaintext,
) {
Ok(()) => {
Ok(T::decode(&plaintext[..ciphertext_len - AEAD_TAG_SIZE]).map_err(Error::from)?)
}
Err(e) => Err(Error::NoteDecryptionFailed(e.to_string())),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use darkfi::crypto::types::{DrkCoinBlind, DrkSerial, DrkValueBlind};
use darkfi_sdk::{
crypto::{Keypair, TokenId},
pasta::{group::ff::Field, pallas},
};
#[test]
fn test_note_encdec() {
#[derive(SerialEncodable, SerialDecodable)]
struct MyNote {
serial: DrkSerial,
value: u64,
token_id: TokenId,
coin_blind: DrkCoinBlind,
value_blind: DrkValueBlind,
token_blind: DrkValueBlind,
memo: Vec<u8>,
}
let note = MyNote {
serial: DrkSerial::random(&mut OsRng),
value: 110,
token_id: TokenId::from(pallas::Base::random(&mut OsRng)),
coin_blind: DrkCoinBlind::random(&mut OsRng),
value_blind: DrkValueBlind::random(&mut OsRng),
token_blind: DrkValueBlind::random(&mut OsRng),
memo: vec![32, 223, 231, 3, 1, 1],
};
let keypair = Keypair::random(&mut OsRng);
let encrypted_note = encrypt(&note, &keypair.public).unwrap();
let note2: MyNote = encrypted_note.decrypt(&keypair.secret).unwrap();
assert_eq!(note.value, note2.value);
assert_eq!(note.token_id, note2.token_id);
assert_eq!(note.token_blind, note2.token_blind);
assert_eq!(note.memo, note2.memo);
}
}

View File

@@ -18,19 +18,31 @@
use darkfi::{tx::Transaction, Result};
use darkfi_sdk::{
crypto::TokenId,
crypto::{constants::MERKLE_DEPTH, MerkleNode, TokenId},
incrementalmerkletree::{bridgetree::BridgeTree, Tree},
pasta::{group::ff::Field, pallas},
tx::ContractCall,
};
use darkfi_serial::Encodable;
use log::info;
use log::{debug, info};
use rand::rngs::OsRng;
use darkfi_dao_contract::{client::build_dao_mint_tx, DaoFunction};
use darkfi_dao_contract::{
client::{build_dao_mint_tx, MerkleTree},
DaoFunction,
};
mod harness;
use harness::{init_logger, DaoTestHarness};
// TODO: Anonymity leaks in this proof of concept:
//
// * Vote updates are linked to the proposal_bulla
// * Nullifier of vote will link vote with the coin when it's spent
// TODO: strategize and cleanup Result/Error usage
// TODO: fix up code doc
#[async_std::test]
async fn integration_test() -> Result<()> {
init_logger()?;
@@ -51,9 +63,12 @@ async fn integration_test() -> Result<()> {
let dao_approval_ratio_quot = 1;
let dao_approval_ratio_base = 2;
// =================
// DaoFunction::Mint
// =================
// =======================================================
// Dao::Mint
//
// Create the DAO bulla
// =======================================================
debug!(target: "demo", "Stage 1. Creating DAO bulla");
let dao_bulla_blind = pallas::Base::random(&mut OsRng);
@@ -90,5 +105,21 @@ async fn integration_test() -> Result<()> {
th.alice_state.read().await.verify_transactions(&[tx.clone()], true).await?;
// TODO: Witness and add to wallet merkle tree?
let mut dao_tree = MerkleTree::new(100);
let dao_leaf_position = {
let node = MerkleNode::from(params.dao_bulla.inner());
dao_tree.append(&node);
dao_tree.witness().unwrap()
};
debug!(target: "demo", "Created DAO bulla: {:?}", params.dao_bulla.inner());
// =======================================================
// Money::Transfer
//
// Mint the initial supply of treasury token
// and send it all to the DAO directly
// =======================================================
debug!(target: "demo", "Stage 2. Minting treasury token");
Ok(())
}