4NK_env/docs/4NK_DAO_TECHNICAL_SPECIFICATION.md

72 KiB

Implémentation d'une DAO sur 4NK : Spécification Technique

Version: 1.0 Date: 1 octobre 2025 Prérequis: 4NK_IDENTITY_AND_PROCESS_SPEC.md


Table des matières

  1. Architecture d'une DAO 4NK
  2. Mécanismes de gouvernance
  3. Système de vote on-chain
  4. Gestion de trésorerie
  5. Propositions et exécution
  6. Tokenisation et pondération
  7. Sécurité et attaques
  8. Implémentation de référence

1. Architecture d'une DAO 4NK

1.1 Vue d'ensemble

Une DAO (Decentralized Autonomous Organization) sur 4NK exploite les primitives existantes pour créer une organisation autonome avec:

  • Gouvernance on-chain via ProcessState
  • Identité cryptographique via Silent Payments
  • Consensus distribué via ValidationRules
  • Trésorerie Bitcoin via UTXO management
  • Auditabilité complète via blockchain commitments
┌────────────────────────────────────────────────────────────┐
│                      DAO 4NK                               │
├────────────────────────────────────────────────────────────┤
│                                                            │
│  GOUVERNANCE                 TRÉSORERIE                    │
│  ┌─────────────┐            ┌─────────────┐              │
│  │ Processus   │            │ Multisig    │              │
│  │ de vote     │            │ UTXO Pool   │              │
│  └──────┬──────┘            └──────┬──────┘              │
│         │                          │                      │
│         │  ┌──────────────┐        │                      │
│         └─►│  Proposition │◄───────┘                      │
│            │  Executable  │                               │
│            └──────┬───────┘                               │
│                   │                                        │
│                   ▼                                        │
│         ┌────────────────────┐                            │
│         │  Exécution on-chain│                            │
│         │  (Bitcoin Tx)      │                            │
│         └────────────────────┘                            │
│                                                            │
│  MEMBRES                                                   │
│  ┌──────────┬──────────┬──────────┐                      │
│  │ Founder  │ Member   │ Delegate │                      │
│  │ (100%)   │ (1 vote) │ (N votes)│                      │
│  └──────────┴──────────┴──────────┘                      │
└────────────────────────────────────────────────────────────┘

1.2 Composants techniques

A. Processus DAO principal

pub struct DaoProcess {
    // Processus 4NK contenant la gouvernance
    process: Process,

    // Configuration de la DAO
    config: DaoConfig,

    // État actuel
    current_state: DaoState,
}

pub struct DaoConfig {
    // Nom de la DAO
    name: String,

    // Type de vote
    voting_system: VotingSystem,

    // Quorum requis (0.0 à 1.0)
    quorum: f32,

    // Durée de vote (en blocs Bitcoin)
    voting_period_blocks: u32,

    // Délai d'exécution (timelock)
    execution_delay_blocks: u32,

    // Trésorerie
    treasury_address: SilentPaymentAddress,
}

pub struct DaoState {
    // Membres de la DAO
    members: HashMap<OutPoint, MemberInfo>,

    // Propositions actives
    active_proposals: Vec<Proposal>,

    // Propositions exécutées
    executed_proposals: Vec<ProposalExecution>,

    // Balance de trésorerie
    treasury_balance: Amount,
}

B. Structure de membre DAO

pub struct MemberInfo {
    // Pairing process ID du membre
    member_id: OutPoint,

    // Adresses Silent Payment
    sp_addresses: Vec<SilentPaymentAddress>,

    // Poids de vote (tokens)
    voting_power: u64,

    // Délégation (optionnel)
    delegated_to: Option<OutPoint>,

    // Date d'adhésion (hauteur de bloc)
    joined_at: u32,

    // Récompenses accumulées
    rewards: Amount,
}

impl MemberInfo {
    pub fn effective_voting_power(&self, state: &DaoState) -> u64 {
        if let Some(delegate) = self.delegated_to {
            0 // Votes délégués
        } else {
            // Inclure les votes délégués à ce membre
            let delegated_votes: u64 = state.members.values()
                .filter(|m| m.delegated_to == Some(self.member_id))
                .map(|m| m.voting_power)
                .sum();

            self.voting_power + delegated_votes
        }
    }
}

1.3 Processus fondamentaux

Une DAO 4NK repose sur trois processus distincts :

┌─────────────────────────────────────────────────────┐
│  Processus 1: GOUVERNANCE                           │
│  ├─ États: Propositions, Votes, Paramètres          │
│  ├─ Rôles: Founders, Members, Delegates             │
│  └─ Validations: Quorum-based                       │
└─────────────────────────────────────────────────────┘
         │
         ▼ Référence via process_id
┌─────────────────────────────────────────────────────┐
│  Processus 2: TRÉSORERIE (Multisig)                 │
│  ├─ États: UTXO pool, Dépenses                      │
│  ├─ Rôles: Signataires (M-of-N)                     │
│  └─ Validations: Signatures cryptographiques        │
└─────────────────────────────────────────────────────┘
         │
         ▼ Référence via execution_id
┌─────────────────────────────────────────────────────┐
│  Processus 3: ADHÉSIONS (Pairing collectif)         │
│  ├─ États: Liste des membres                        │
│  ├─ Rôles: Admission committee                      │
│  └─ Validations: Vote d'admission                   │
└─────────────────────────────────────────────────────┘

Avantages de cette architecture:

  • Séparation des responsabilités : Gouvernance ≠ Trésorerie
  • Évolutivité : Chaque processus peut évoluer indépendamment
  • Sécurité : Compromission d'un processus n'affecte pas les autres
  • Auditabilité : Traçabilité complète de chaque aspect

2. Mécanismes de gouvernance

2.1 Types de vote

A. Vote simple (1 membre = 1 vote)

pub enum VotingSystem {
    Simple,           // 1 membre = 1 vote
    Weighted,         // Vote pondéré par tokens
    Quadratic,        // Vote quadratique (√tokens)
    Delegated,        // Délégation de votes
    Reputation,       // Basé sur la réputation
}

impl VotingSystem {
    pub fn calculate_vote_power(&self, member: &MemberInfo, state: &DaoState) -> u64 {
        match self {
            VotingSystem::Simple => 1,
            VotingSystem::Weighted => member.effective_voting_power(state),
            VotingSystem::Quadratic => {
                let power = member.effective_voting_power(state);
                (power as f64).sqrt() as u64
            },
            VotingSystem::Delegated => {
                if member.delegated_to.is_some() {
                    0
                } else {
                    member.effective_voting_power(state)
                }
            },
            VotingSystem::Reputation => {
                self.calculate_reputation(member, state)
            },
        }
    }
}

B. Vote pondéré par tokens

Cas d'usage: DAOs avec investisseurs (proportionnel à l'investissement)

pub struct TokenWeightedVote {
    // Total de tokens en circulation
    total_supply: u64,

    // Tokens détenus par membre
    balances: HashMap<OutPoint, u64>,
}

impl TokenWeightedVote {
    pub fn vote_power(&self, member_id: &OutPoint) -> f32 {
        let tokens = self.balances.get(member_id).unwrap_or(&0);
        (*tokens as f32) / (self.total_supply as f32)
    }

    pub fn has_quorum(&self, votes: &HashMap<OutPoint, Vote>, quorum: f32) -> bool {
        let voted_tokens: u64 = votes.keys()
            .filter_map(|member_id| self.balances.get(member_id))
            .sum();

        let participation = (voted_tokens as f32) / (self.total_supply as f32);
        participation >= quorum
    }
}

C. Vote quadratique

Objectif: Réduire l'influence des gros détenteurs

Pouvoir de vote = √(tokens détenus)

Exemples:
- 1 token   → 1 vote   (√1 = 1)
- 100 tokens → 10 votes (√100 = 10)
- 10,000 tokens → 100 votes (√10,000 = 100)

Implémentation:

pub struct QuadraticVoting {
    balances: HashMap<OutPoint, u64>,
}

impl QuadraticVoting {
    pub fn vote_power(&self, member_id: &OutPoint) -> u64 {
        let tokens = self.balances.get(member_id).unwrap_or(&0);
        (*tokens as f64).sqrt() as u64
    }

    pub fn total_vote_power(&self) -> u64 {
        self.balances.values()
            .map(|&tokens| (tokens as f64).sqrt() as u64)
            .sum()
    }
}

Avantages:

  • Réduit la concentration du pouvoir
  • Encourage la participation des petits membres
  • Coût d'attaque plus élevé (quadratique vs linéaire)

Inconvénients:

  • ⚠️ Complexité accrue
  • ⚠️ Potentiel de Sybil attacks (multiple comptes)

2.2 Structure d'une proposition

pub struct Proposal {
    // Identifiant unique (hash de la proposition)
    id: [u8; 32],

    // Créateur de la proposition
    proposer: OutPoint,

    // Titre et description
    title: String,
    description: String,

    // Type de proposition
    proposal_type: ProposalType,

    // Actions à exécuter si approuvée
    actions: Vec<ProposalAction>,

    // Période de vote
    voting_start: u32,  // Hauteur de bloc
    voting_end: u32,

    // État du vote
    votes: HashMap<OutPoint, Vote>,

    // Statut
    status: ProposalStatus,

    // Exécution (si approuvée)
    execution: Option<ProposalExecution>,
}

pub enum ProposalType {
    // Modification de paramètres
    ConfigChange(ConfigChange),

    // Dépense de trésorerie
    TreasurySpend(TreasurySpend),

    // Ajout/Retrait de membre
    MembershipChange(MembershipChange),

    // Mise à jour des rôles
    RoleUpdate(RoleUpdate),

    // Proposition générique
    Generic(String),
}

pub enum Vote {
    Yes,
    No,
    Abstain,
}

pub enum ProposalStatus {
    Draft,          // En rédaction
    Active,         // Vote en cours
    Passed,         // Approuvée
    Rejected,       // Rejetée
    Executed,       // Exécutée
    Cancelled,      // Annulée
    Expired,        // Expirée
}

2.3 Cycle de vie d'une proposition

┌─────────────────┐
│  1. CRÉATION    │  Membre soumet une proposition
│  (Draft)        │  ├─ Validation formelle
│                 │  └─ Dépôt de caution (optionnel)
└────────┬────────┘
         │
         ▼ voting_start
┌─────────────────┐
│  2. VOTE ACTIF  │  Période de vote ouverte
│  (Active)       │  ├─ Membres votent (Yes/No/Abstain)
│                 │  └─ Accumulation des votes
└────────┬────────┘
         │
         ▼ voting_end
┌─────────────────────────┐
│  3. DÉCOMPTE            │
│  ├─ Calcul du quorum    │
│  ├─ Calcul de la majorité│
│  └─ Détermination statut│
└────────┬────────────────┘
         │
    ┌────┴────┐
    │         │
    ▼         ▼
 Passed    Rejected
    │
    ▼ execution_delay
┌─────────────────┐
│  4. EXÉCUTION   │  Création transaction Bitcoin
│  (Executed)     │  ├─ Validation finale
│                 │  ├─ Broadcast transaction
│                 │  └─ Confirmation on-chain
└─────────────────┘

2.4 Règles de validation de proposition

Utilisation des ValidationRules de 4NK:

// Rôle "proposer" : Peut créer des propositions
let proposer_role = RoleDefinition {
    members: vec![/* tous les membres */],
    validation_rules: vec![
        ValidationRule::new(
            0.01,  // 1% pour créer une proposition (anti-spam)
            vec!["proposals".to_string()],
            0.5,   // 50% des devices du membre
        )?,
    ],
    storages: vec![],
};

// Rôle "voter" : Peut voter
let voter_role = RoleDefinition {
    members: vec![/* tous les membres */],
    validation_rules: vec![
        ValidationRule::new(
            0.0,   // Pas de quorum (lecture seule)
            vec!["votes".to_string()],
            0.5,   // 50% des devices
        )?,
    ],
    storages: vec![],
};

// Rôle "executor" : Peut exécuter les propositions approuvées
let executor_role = RoleDefinition {
    members: vec![/* signataires multisig */],
    validation_rules: vec![
        ValidationRule::new(
            0.67,  // 2/3 des signataires
            vec!["executions".to_string()],
            1.0,   // Tous les devices (sécurité max)
        )?,
    ],
    storages: vec![],
};

2.5 Calcul du quorum et de la majorité

pub struct VoteResult {
    yes_votes: u64,
    no_votes: u64,
    abstain_votes: u64,
    total_power: u64,
}

impl VoteResult {
    pub fn from_votes(
        votes: &HashMap<OutPoint, Vote>,
        members: &HashMap<OutPoint, MemberInfo>,
        voting_system: &VotingSystem,
        state: &DaoState,
    ) -> Self {
        let mut yes_votes = 0;
        let mut no_votes = 0;
        let mut abstain_votes = 0;

        for (member_id, vote) in votes.iter() {
            if let Some(member) = members.get(member_id) {
                let power = voting_system.calculate_vote_power(member, state);
                match vote {
                    Vote::Yes => yes_votes += power,
                    Vote::No => no_votes += power,
                    Vote::Abstain => abstain_votes += power,
                }
            }
        }

        let total_power = members.values()
            .map(|m| voting_system.calculate_vote_power(m, state))
            .sum();

        Self { yes_votes, no_votes, abstain_votes, total_power }
    }

    pub fn has_quorum(&self, required_quorum: f32) -> bool {
        let participation = (self.yes_votes + self.no_votes + self.abstain_votes) as f32
                          / self.total_power as f32;
        participation >= required_quorum
    }

    pub fn is_approved(&self, majority: f32) -> bool {
        let yes_ratio = self.yes_votes as f32 / (self.yes_votes + self.no_votes) as f32;
        yes_ratio >= majority
    }

    pub fn passes(&self, quorum: f32, majority: f32) -> bool {
        self.has_quorum(quorum) && self.is_approved(majority)
    }
}

Exemples de configuration:

// Simple majorité (>50%)
let config_simple = DaoConfig {
    quorum: 0.2,      // 20% de participation minimum
    majority: 0.51,   // 51% de oui requis
    ..Default::default()
};

// Super-majorité (2/3)
let config_critical = DaoConfig {
    quorum: 0.4,      // 40% de participation
    majority: 0.67,   // 67% de oui requis
    ..Default::default()
};

// Unanimité (propositions critiques)
let config_unanimous = DaoConfig {
    quorum: 0.9,      // 90% de participation
    majority: 1.0,    // 100% de oui requis
    ..Default::default()
};

3. Système de vote on-chain

3.1 Enregistrement d'un vote

Flux cryptographique:

pub fn cast_vote(
    dao: &DaoProcess,
    proposal_id: [u8; 32],
    vote: Vote,
    member: &Device,
) -> Result<ProcessState> {
    // 1. Vérifier l'éligibilité
    let member_info = dao.current_state.members
        .get(&member.get_pairing_commitment().unwrap())
        .ok_or("Not a DAO member")?;

    // 2. Vérifier la période de vote
    let proposal = dao.get_proposal(&proposal_id)?;
    let current_block = get_current_block_height()?;
    if current_block < proposal.voting_start || current_block > proposal.voting_end {
        return Err(anyhow!("Voting period not active"));
    }

    // 3. Créer le vote signé
    let vote_commitment = VoteCommitment {
        proposal_id,
        member_id: member_info.member_id,
        vote: vote.clone(),
        timestamp: current_block,
    };

    // 4. Construire le nouvel état
    let mut new_state = dao.process.get_latest_committed_state().unwrap().clone();

    // Mise à jour du vote dans public_data
    let mut votes_json = new_state.public_data.get_as_json("votes")?;
    let votes_map = votes_json.as_object_mut().unwrap();
    votes_map.insert(
        hex::encode(member_info.member_id.txid.as_byte_array()),
        serde_json::to_value(&vote)?,
    );

    new_state.public_data.insert_serializable(
        "votes".to_string(),
        &votes_json,
    )?;

    // 5. Recalculer les commitments
    new_state.pcd_commitment = PcdCommitments::new(
        &new_state.commited_in,
        &Pcd::new(BTreeMap::new()), // Pas de données privées
        &new_state.roles,
    )?;

    // 6. Calculer le nouveau state_id
    let merkle_tree = new_state.pcd_commitment.create_merkle_tree()?;
    new_state.state_id = merkle_tree.root().unwrap();

    // 7. Signer avec la clé du membre
    let spend_key: SecretKey = member.get_sp_client().get_spend_key().try_into()?;
    let message_hash = new_state.get_message_hash(true)?;
    let proof = Proof::new(message_hash, spend_key);
    new_state.validation_tokens.push(proof);

    Ok(new_state)
}

3.2 Vote avec preuve de possession

Objectif: Prouver qu'un membre détient effectivement ses tokens sans révéler son solde exact.

pub struct VoteWithProof {
    // Vote
    vote: Vote,

    // Preuve de possession (Merkle proof)
    token_proof: TokenOwnershipProof,

    // Signature
    signature: Proof,
}

pub struct TokenOwnershipProof {
    // UTXO prouvé
    outpoint: OutPoint,

    // Chemin Merkle dans l'arbre des tokens
    merkle_path: Vec<[u8; 32]>,

    // Index dans l'arbre
    leaf_index: usize,

    // Montant (optionnel, peut être masqué)
    amount: Option<Amount>,
}

impl TokenOwnershipProof {
    pub fn verify(&self, merkle_root: [u8; 32]) -> Result<()> {
        // 1. Reconstruire la feuille
        let leaf = if let Some(amount) = self.amount {
            // Preuve complète
            hash_leaf(&self.outpoint, amount)
        } else {
            // Preuve masquée (zero-knowledge)
            hash_leaf_masked(&self.outpoint)
        };

        // 2. Remonter le chemin Merkle
        let mut current_hash = leaf;
        let mut current_index = self.leaf_index;

        for sibling in &self.merkle_path {
            current_hash = if current_index % 2 == 0 {
                hash_pair(&current_hash, sibling)
            } else {
                hash_pair(sibling, &current_hash)
            };
            current_index /= 2;
        }

        // 3. Vérifier la racine
        if current_hash == merkle_root {
            Ok(())
        } else {
            Err(anyhow!("Invalid Merkle proof"))
        }
    }
}

3.3 Vote confidentiel (Optionnel)

Schéma: Vote chiffré jusqu'à la fin de la période

pub struct ConfidentialVote {
    // Vote chiffré
    encrypted_vote: Vec<u8>,

    // Preuve zero-knowledge que le vote est valide
    validity_proof: ValidityProof,

    // Clé de déchiffrement (révélée après voting_end)
    decryption_key_commitment: [u8; 32],
}

impl ConfidentialVote {
    pub fn new(vote: Vote, public_key: &PublicKey) -> Result<Self> {
        // 1. Chiffrer le vote
        let vote_bytes = bincode::serialize(&vote)?;
        let (ciphertext, nonce, secret) = encrypt_vote(&vote_bytes, public_key)?;

        // 2. Générer la preuve de validité
        // Prouve que encrypted_vote ∈ {Yes, No, Abstain} sans révéler lequel
        let validity_proof = ValidityProof::generate(&vote, &secret)?;

        // 3. Commitment de la clé de déchiffrement
        let key_commitment = sha256(&secret);

        Ok(Self {
            encrypted_vote: ciphertext,
            validity_proof,
            decryption_key_commitment: key_commitment,
        })
    }

    pub fn reveal(&self, decryption_key: &[u8]) -> Result<Vote> {
        // 1. Vérifier le commitment
        if sha256(decryption_key) != self.decryption_key_commitment {
            return Err(anyhow!("Invalid decryption key"));
        }

        // 2. Déchiffrer
        let plaintext = decrypt_vote(&self.encrypted_vote, decryption_key)?;
        Ok(bincode::deserialize(&plaintext)?)
    }
}

Avantages:

  • Pas de biais de vote (pas d'influence du dernier votant)
  • Protection contre la coercition
  • Décompte vérifiable après révélation

Inconvénients:

  • ⚠️ Complexité cryptographique élevée
  • ⚠️ Nécessite un mécanisme de révélation fiable

3.4 Commit on-chain du résultat

Une fois le vote terminé et le résultat calculé, l'état est committé sur Bitcoin:

pub fn finalize_proposal(
    dao: &mut DaoProcess,
    proposal_id: [u8; 32],
) -> Result<Transaction> {
    // 1. Calculer le résultat
    let proposal = dao.get_proposal(&proposal_id)?;
    let result = VoteResult::from_votes(
        &proposal.votes,
        &dao.current_state.members,
        &dao.config.voting_system,
        &dao.current_state,
    );

    // 2. Déterminer le statut
    let passed = result.passes(dao.config.quorum, 0.51);
    let new_status = if passed {
        ProposalStatus::Passed
    } else {
        ProposalStatus::Rejected
    };

    // 3. Construire le nouvel état
    let mut new_state = dao.process.get_latest_committed_state().unwrap().clone();

    // Mise à jour du statut de la proposition
    let mut proposals_json = new_state.public_data.get_as_json("proposals")?;
    let proposals_array = proposals_json.as_array_mut().unwrap();

    for prop in proposals_array.iter_mut() {
        if prop["id"].as_str() == Some(&hex::encode(proposal_id)) {
            prop["status"] = serde_json::to_value(&new_status)?;
            prop["result"] = serde_json::to_value(&result)?;
            break;
        }
    }

    new_state.public_data.insert_serializable(
        "proposals".to_string(),
        &proposals_json,
    )?;

    // 4. Recalculer state_id
    new_state.pcd_commitment = PcdCommitments::new(
        &new_state.commited_in,
        &Pcd::new(BTreeMap::new()),
        &new_state.roles,
    )?;
    let merkle_tree = new_state.pcd_commitment.create_merkle_tree()?;
    new_state.state_id = merkle_tree.root().unwrap();

    // 5. Collecter les signatures (rôle "executor")
    // ... (processus de validation multi-signatures)

    // 6. Créer la transaction Bitcoin
    let tx = create_commit_transaction(&dao.process, &new_state)?;

    Ok(tx)
}

4. Gestion de trésorerie

4.1 Architecture de trésorerie multisig

La trésorerie d'une DAO 4NK utilise un processus distinct avec validation multisig:

pub struct TreasuryProcess {
    // Processus 4NK dédié
    process: Process,

    // Configuration multisig
    multisig_config: MultisigConfig,

    // UTXO pool
    utxos: HashMap<OutPoint, TreasuryUtxo>,

    // Dépenses en attente
    pending_spends: Vec<TreasurySpend>,
}

pub struct MultisigConfig {
    // M-of-N signature scheme
    required_signatures: usize,  // M
    total_signers: usize,        // N

    // Signataires (process IDs)
    signers: Vec<OutPoint>,

    // Adresse de réception (Silent Payment)
    receive_address: SilentPaymentAddress,
}

pub struct TreasuryUtxo {
    outpoint: OutPoint,
    amount: Amount,
    script_pubkey: ScriptBuf,
    confirmed: bool,
    reserved: bool,  // Réservé pour une dépense
}

4.2 Scripts multisig sur Bitcoin

Option 1: Taproot multisig (recommandé)

use bitcoin::taproot::{TaprootBuilder, TaprootSpendInfo};
use bitcoin::secp256k1::XOnlyPublicKey;

pub fn create_taproot_multisig(
    signers: &[XOnlyPublicKey],
    threshold: usize,
) -> Result<TaprootSpendInfo> {
    // 1. Créer l'arbre Taproot
    let mut builder = TaprootBuilder::new();

    // 2. Générer toutes les combinaisons M-of-N
    let combinations = generate_combinations(signers, threshold);

    for combo in combinations {
        // Script: OP_CHECKSIGADD ... OP_GREATERTHAN
        let script = create_musig_script(&combo, threshold)?;
        builder = builder.add_leaf(0, script)?;
    }

    // 3. Finaliser l'arbre
    let spend_info = builder.finalize(
        &Secp256k1::new(),
        signers[0], // Key path (optionnel)
    )?;

    Ok(spend_info)
}

fn create_musig_script(keys: &[XOnlyPublicKey], threshold: usize) -> Result<ScriptBuf> {
    let mut script = ScriptBuf::new();

    // Empiler les clés et vérifier les signatures
    for key in keys {
        script.push_slice(key.serialize());
        script.push_opcode(opcodes::all::OP_CHECKSIG);
        script.push_opcode(opcodes::all::OP_CHECKSIGADD);
    }

    // Vérifier le seuil
    script.push_int(threshold as i64);
    script.push_opcode(opcodes::all::OP_GREATERTHANOREQUAL);

    Ok(script)
}

Option 2: MuSig2 (plus efficace)

use secp256k1::musig2::{PartialSignature, AggregateSignature};

pub struct MusigTreasury {
    // Clé publique agrégée
    aggregate_pubkey: XOnlyPublicKey,

    // Participants
    participants: Vec<OutPoint>,

    // Session de signature en cours
    signing_session: Option<MusigSession>,
}

impl MusigTreasury {
    pub fn new(signers: &[XOnlyPublicKey]) -> Result<Self> {
        // Agréger les clés publiques
        let aggregate_pubkey = aggregate_public_keys(signers)?;

        Ok(Self {
            aggregate_pubkey,
            participants: vec![],
            signing_session: None,
        })
    }

    pub fn initiate_spend(
        &mut self,
        spend: &TreasurySpend,
    ) -> Result<MusigSession> {
        // 1. Créer la transaction à signer
        let tx = self.create_spend_tx(spend)?;

        // 2. Initialiser la session MuSig2
        let session = MusigSession::new(
            &self.aggregate_pubkey,
            &tx.txid(),
            &self.participants,
        )?;

        self.signing_session = Some(session.clone());
        Ok(session)
    }

    pub fn add_partial_signature(
        &mut self,
        signer: OutPoint,
        partial_sig: PartialSignature,
    ) -> Result<Option<AggregateSignature>> {
        let session = self.signing_session.as_mut()
            .ok_or(anyhow!("No active signing session"))?;

        // Ajouter la signature partielle
        session.add_signature(signer, partial_sig)?;

        // Vérifier si on a assez de signatures
        if session.has_threshold() {
            let aggregate = session.aggregate()?;
            Ok(Some(aggregate))
        } else {
            Ok(None)
        }
    }
}

Avantages MuSig2:

  • Efficacité : 1 signature agrégée (64 bytes) vs M signatures
  • Confidentialité : Indistinguable d'une signature simple
  • Coût : Frais de transaction réduits

4.3 Processus de dépense

┌──────────────────────────────────────────────────────────┐
│  ÉTAPE 1: PROPOSITION DE DÉPENSE                         │
├──────────────────────────────────────────────────────────┤
│                                                          │
│  Membre crée une proposition:                            │
│  ├─ Bénéficiaire: sp1qqxxx...                           │
│  ├─ Montant: 1.5 BTC                                     │
│  ├─ Description: "Paiement développeur Q4"              │
│  └─ UTXOs sources: [utxo1, utxo2]                       │
│                                                          │
│  → Vote DAO (processus gouvernance)                      │
└──────────────────────────────────────────────────────────┘
                         │
                         ▼ Approuvée
┌──────────────────────────────────────────────────────────┐
│  ÉTAPE 2: CONSTRUCTION TRANSACTION                       │
├──────────────────────────────────────────────────────────┤
│                                                          │
│  Transaction Bitcoin:                                    │
│  Inputs:  [utxo1, utxo2]  (trésorerie)                  │
│  Outputs:                                                │
│    - Bénéficiaire: 1.5 BTC                              │
│    - Change (trésorerie): 0.49 BTC                      │
│    - Frais: 0.01 BTC                                    │
│                                                          │
│  Script multisig (Taproot ou MuSig2)                    │
└──────────────────────────────────────────────────────────┘
                         │
                         ▼
┌──────────────────────────────────────────────────────────┐
│  ÉTAPE 3: COLLECTE DES SIGNATURES                        │
├──────────────────────────────────────────────────────────┤
│                                                          │
│  Signataires 1, 2, ..., M signent:                      │
│  ├─ Signature avec spend_key                            │
│  ├─ Envoi via WebSocket (sdk_relay)                     │
│  └─ Accumulation dans processus trésorerie              │
│                                                          │
│  Quorum atteint: M-of-N signatures                       │
└──────────────────────────────────────────────────────────┘
                         │
                         ▼
┌──────────────────────────────────────────────────────────┐
│  ÉTAPE 4: FINALISATION ET BROADCAST                      │
├──────────────────────────────────────────────────────────┤
│                                                          │
│  ├─ Agrégation signatures (MuSig2) ou script complet    │
│  ├─ Broadcast sur réseau Bitcoin                        │
│  ├─ Attente confirmation (1-6 blocs)                    │
│  └─ Mise à jour état trésorerie (UTXO dépensé)          │
│                                                          │
│  Enregistrement on-chain (OP_RETURN):                   │
│    État trésorerie + référence proposition              │
└──────────────────────────────────────────────────────────┘

4.4 Implémentation de dépense

pub async fn execute_treasury_spend(
    treasury: &mut TreasuryProcess,
    spend: &TreasurySpend,
    signers: &[Device],
) -> Result<Txid> {
    // 1. Vérifier que la proposition est approuvée
    if spend.proposal_status != ProposalStatus::Passed {
        return Err(anyhow!("Proposal not approved"));
    }

    // 2. Sélectionner les UTXOs
    let selected_utxos = treasury.select_utxos(spend.amount)?;
    let total_input = selected_utxos.iter()
        .map(|u| u.amount)
        .sum::<Amount>();

    // 3. Calculer le change
    let fee = estimate_fee(&selected_utxos, 2)?; // 2 outputs
    let change = total_input - spend.amount - fee;

    // 4. Construire la transaction
    let mut tx = Transaction {
        version: 2,
        lock_time: LockTime::ZERO,
        input: vec![],
        output: vec![],
    };

    // Inputs
    for utxo in &selected_utxos {
        tx.input.push(TxIn {
            previous_output: utxo.outpoint,
            script_sig: ScriptBuf::new(),
            sequence: Sequence::MAX,
            witness: Witness::new(),
        });
    }

    // Outputs
    // Output 1: Bénéficiaire
    tx.output.push(TxOut {
        value: spend.amount,
        script_pubkey: spend.recipient.to_script_pubkey()?,
    });

    // Output 2: Change (retour à la trésorerie)
    if change > Amount::ZERO {
        tx.output.push(TxOut {
            value: change,
            script_pubkey: treasury.multisig_config.receive_address.to_script_pubkey()?,
        });
    }

    // 5. Signer avec multisig
    let mut partial_sigs = vec![];
    for (i, signer) in signers.iter().enumerate() {
        let sighash = compute_sighash(&tx, i)?;
        let spend_key: SecretKey = signer.get_sp_client().get_spend_key().try_into()?;
        let sig = sign_sighash(&sighash, &spend_key)?;
        partial_sigs.push(sig);
    }

    // 6. Finaliser les témoins (witness)
    if treasury.multisig_config.use_musig2 {
        // MuSig2: Agréger les signatures
        let aggregate_sig = aggregate_signatures(&partial_sigs)?;
        for input in &mut tx.input {
            input.witness.push(aggregate_sig.serialize());
        }
    } else {
        // Script multisig traditionnel
        for (i, input) in tx.input.iter_mut().enumerate() {
            let script_witness = build_multisig_witness(&partial_sigs, i)?;
            input.witness = script_witness;
        }
    }

    // 7. Broadcast
    let txid = broadcast_transaction(&tx).await?;

    // 8. Mettre à jour l'état de la trésorerie
    treasury.record_spend(txid, spend)?;

    Ok(txid)
}

4.5 Audit de trésorerie

Transparency on-chain:

pub struct TreasuryAudit {
    // Snapshot de la trésorerie
    snapshot_time: u32,  // Hauteur de bloc

    // Balance totale
    total_balance: Amount,

    // UTXOs détaillés
    utxos: Vec<UtxoAudit>,

    // Historique des dépenses
    spending_history: Vec<SpendAudit>,
}

pub struct UtxoAudit {
    outpoint: OutPoint,
    amount: Amount,
    received_at: u32,       // Hauteur de bloc
    script_type: String,    // "P2TR", "P2WSH", etc.
    confirmations: u32,
}

pub struct SpendAudit {
    txid: Txid,
    proposal_id: [u8; 32],
    amount: Amount,
    recipient: String,
    executed_at: u32,       // Hauteur de bloc
    signers: Vec<OutPoint>, // Qui a signé
}

impl TreasuryProcess {
    pub fn generate_audit_report(&self, block_height: u32) -> Result<TreasuryAudit> {
        let mut utxos_audit = vec![];
        let mut total = Amount::ZERO;

        for (outpoint, utxo) in &self.utxos {
            if !utxo.reserved {
                total += utxo.amount;
                utxos_audit.push(UtxoAudit {
                    outpoint: *outpoint,
                    amount: utxo.amount,
                    received_at: 0, // À récupérer depuis blockchain
                    script_type: "P2TR".to_string(),
                    confirmations: block_height - utxo.received_at,
                });
            }
        }

        Ok(TreasuryAudit {
            snapshot_time: block_height,
            total_balance: total,
            utxos: utxos_audit,
            spending_history: self.get_spending_history()?,
        })
    }

    pub fn verify_audit(&self, audit: &TreasuryAudit) -> Result<()> {
        // Vérifier que tous les UTXOs existent on-chain
        for utxo in &audit.utxos {
            verify_utxo_exists(&utxo.outpoint)?;
        }

        // Vérifier que la somme est correcte
        let calculated_total: Amount = audit.utxos.iter()
            .map(|u| u.amount)
            .sum();

        if calculated_total != audit.total_balance {
            return Err(anyhow!("Balance mismatch"));
        }

        // Vérifier l'historique des dépenses
        for spend in &audit.spending_history {
            verify_spend_on_chain(&spend.txid)?;
        }

        Ok(())
    }
}

5. Propositions et exécution

5.1 Types d'actions exécutables

pub enum ProposalAction {
    // Modification de paramètre
    UpdateConfig {
        parameter: String,
        old_value: Value,
        new_value: Value,
    },

    // Dépense de trésorerie
    TreasuryTransfer {
        recipient: SilentPaymentAddress,
        amount: Amount,
        description: String,
    },

    // Ajout/Retrait de membre
    ModifyMembership {
        member_id: OutPoint,
        action: MembershipAction,
    },

    // Modification des rôles
    UpdateRole {
        role_name: String,
        new_definition: RoleDefinition,
    },

    // Mise à jour du contrat de la DAO
    UpdateContract {
        contract_hash: [u8; 32],
        contract_url: String,
    },

    // Appel à un autre processus
    CrossProcessCall {
        target_process: OutPoint,
        function: String,
        params: Value,
    },

    // Action personnalisée
    Custom {
        action_type: String,
        payload: Vec<u8>,
    },
}

pub enum MembershipAction {
    Add(MemberInfo),
    Remove,
    UpdateVotingPower(u64),
    UpdateDelegation(Option<OutPoint>),
}

5.2 Exécution atomique

Garantie: Toutes les actions d'une proposition s'exécutent atomiquement.

pub fn execute_proposal(
    dao: &mut DaoProcess,
    treasury: &mut TreasuryProcess,
    proposal: &Proposal,
) -> Result<Vec<ExecutionResult>> {
    if proposal.status != ProposalStatus::Passed {
        return Err(anyhow!("Proposal not approved"));
    }

    // Vérifier le timelock
    let current_block = get_current_block_height()?;
    let execution_block = proposal.voting_end + dao.config.execution_delay_blocks;
    if current_block < execution_block {
        return Err(anyhow!("Execution timelock not expired"));
    }

    // Exécuter toutes les actions
    let mut results = vec![];
    let mut rollback_stack = vec![];

    for (i, action) in proposal.actions.iter().enumerate() {
        match execute_action(dao, treasury, action) {
            Ok(result) => {
                results.push(result.clone());
                rollback_stack.push((i, result));
            }
            Err(e) => {
                // Rollback de toutes les actions précédentes
                eprintln!("Action {} failed: {}", i, e);
                for (idx, result) in rollback_stack.iter().rev() {
                    rollback_action(dao, treasury, *idx, result)?;
                }
                return Err(anyhow!("Proposal execution failed at action {}: {}", i, e));
            }
        }
    }

    // Marquer la proposition comme exécutée
    dao.mark_proposal_executed(proposal.id, results.clone())?;

    Ok(results)
}

fn execute_action(
    dao: &mut DaoProcess,
    treasury: &mut TreasuryProcess,
    action: &ProposalAction,
) -> Result<ExecutionResult> {
    match action {
        ProposalAction::UpdateConfig { parameter, new_value, .. } => {
            dao.update_config(parameter, new_value)?;
            Ok(ExecutionResult::ConfigUpdated)
        }

        ProposalAction::TreasuryTransfer { recipient, amount, .. } => {
            let spend = TreasurySpend {
                recipient: recipient.clone(),
                amount: *amount,
                proposal_id: dao.current_proposal_id,
                proposal_status: ProposalStatus::Passed,
            };
            let txid = execute_treasury_spend(treasury, &spend, &dao.signers).await?;
            Ok(ExecutionResult::TransferExecuted(txid))
        }

        ProposalAction::ModifyMembership { member_id, action } => {
            match action {
                MembershipAction::Add(info) => {
                    dao.add_member(*member_id, info.clone())?;
                    Ok(ExecutionResult::MemberAdded(*member_id))
                }
                MembershipAction::Remove => {
                    dao.remove_member(*member_id)?;
                    Ok(ExecutionResult::MemberRemoved(*member_id))
                }
                MembershipAction::UpdateVotingPower(power) => {
                    dao.update_voting_power(*member_id, *power)?;
                    Ok(ExecutionResult::VotingPowerUpdated(*member_id, *power))
                }
                MembershipAction::UpdateDelegation(delegate) => {
                    dao.update_delegation(*member_id, *delegate)?;
                    Ok(ExecutionResult::DelegationUpdated(*member_id))
                }
            }
        }

        ProposalAction::UpdateRole { role_name, new_definition } => {
            dao.update_role(role_name, new_definition.clone())?;
            Ok(ExecutionResult::RoleUpdated(role_name.clone()))
        }

        ProposalAction::CrossProcessCall { target_process, function, params } => {
            let result = call_external_process(*target_process, function, params)?;
            Ok(ExecutionResult::ExternalCallSucceeded(result))
        }

        ProposalAction::Custom { action_type, payload } => {
            execute_custom_action(dao, action_type, payload)?;
            Ok(ExecutionResult::CustomActionExecuted)
        }

        _ => Err(anyhow!("Unknown action type")),
    }
}

5.3 Timelock et sécurité

Délai d'exécution : Période entre l'approbation et l'exécution

pub struct ExecutionTimelock {
    // Délai minimum (en blocs)
    min_delay: u32,

    // Délai variable selon le type de proposition
    delay_by_type: HashMap<String, u32>,

    // Délai d'urgence (rôle spécial requis)
    emergency_delay: u32,
}

impl ExecutionTimelock {
    pub fn calculate_delay(&self, proposal: &Proposal) -> u32 {
        // Délai par défaut
        let mut delay = self.min_delay;

        // Ajuster selon le type
        if let Some(&custom_delay) = self.delay_by_type.get(&proposal.proposal_type.to_string()) {
            delay = delay.max(custom_delay);
        }

        // Propositions critiques = délai plus long
        if proposal.is_critical() {
            delay *= 2;
        }

        // Propositions d'urgence (nécessite rôle "emergency")
        if proposal.is_emergency() && self.has_emergency_approval(proposal) {
            delay = self.emergency_delay;
        }

        delay
    }

    fn has_emergency_approval(&self, proposal: &Proposal) -> bool {
        // Vérifier que les membres du rôle "emergency" ont approuvé
        proposal.votes.iter()
            .filter(|(member, vote)| {
                self.is_emergency_member(member) && **vote == Vote::Yes
            })
            .count() >= self.emergency_quorum()
    }
}

Exemple de configuration:

let timelock = ExecutionTimelock {
    min_delay: 144,  // 1 jour (144 blocs)
    delay_by_type: HashMap::from([
        ("TreasuryTransfer".to_string(), 288),   // 2 jours
        ("UpdateRole".to_string(), 432),         // 3 jours
        ("UpdateContract".to_string(), 1008),    // 1 semaine
    ]),
    emergency_delay: 6,  // 1 heure
};

Rationale:

  • Protection contre les attaques : Temps de réaction avant exécution
  • Transparence : Tous les membres voient les actions à venir
  • Révocabilité : Possibilité d'annuler via nouvelle proposition

5.4 Annulation de proposition

pub fn cancel_proposal(
    dao: &mut DaoProcess,
    proposal_id: [u8; 32],
    reason: String,
) -> Result<()> {
    let proposal = dao.get_proposal(&proposal_id)?;

    // Vérifications
    if proposal.status == ProposalStatus::Executed {
        return Err(anyhow!("Cannot cancel executed proposal"));
    }

    // Qui peut annuler ?
    // 1. Le proposeur (avant le début du vote)
    // 2. Le rôle "guardian" (à tout moment)
    // 3. Une super-majorité (>80% vote pour annuler)

    let current_block = get_current_block_height()?;
    let caller = get_caller_member_id()?;

    let can_cancel =
        // Cas 1: Proposeur avant le vote
        (caller == proposal.proposer && current_block < proposal.voting_start)

        // Cas 2: Guardian
        || dao.is_guardian(&caller)

        // Cas 3: Vote de la communauté (nouvelle proposition)
        || false; // Nécessite une proposition séparée

    if !can_cancel {
        return Err(anyhow!("Not authorized to cancel"));
    }

    // Annuler
    dao.set_proposal_status(proposal_id, ProposalStatus::Cancelled)?;

    // Enregistrer la raison
    dao.add_cancellation_record(proposal_id, reason)?;

    Ok(())
}

6. Tokenisation et pondération

6.1 Système de tokens natif

Design: Tokens représentés comme UTXOs Bitcoin avec métadonnées

pub struct DaoToken {
    // UTXO porteur du token
    outpoint: OutPoint,

    // Montant (en sats)
    amount: Amount,

    // Métadonnées (commitées on-chain)
    metadata: TokenMetadata,
}

pub struct TokenMetadata {
    // Identifiant du token
    token_id: [u8; 32],

    // Supply totale
    total_supply: u64,

    // Propriétaire
    owner: SilentPaymentAddress,

    // Transferable ?
    transferable: bool,

    // Vesting (optionnel)
    vesting: Option<VestingSchedule>,
}

pub struct VestingSchedule {
    // Montant initial bloqué
    initial_amount: u64,

    // Date de début (hauteur de bloc)
    start_block: u32,

    // Durée (en blocs)
    duration_blocks: u32,

    // Cliff (période initiale sans unlock)
    cliff_blocks: u32,

    // Montant déjà débloqué
    unlocked_amount: u64,
}

impl VestingSchedule {
    pub fn available_amount(&self, current_block: u32) -> u64 {
        if current_block < self.start_block + self.cliff_blocks {
            // Cliff period
            return 0;
        }

        let elapsed = current_block.saturating_sub(self.start_block);
        if elapsed >= self.duration_blocks {
            // Fully vested
            return self.initial_amount;
        }

        // Linear vesting
        let vested = (self.initial_amount as f64 * elapsed as f64 / self.duration_blocks as f64) as u64;
        vested.saturating_sub(self.unlocked_amount)
    }
}

6.2 Distribution initiale

Mécanisme: Création d'un processus "token_distribution"

pub fn create_token_distribution(
    dao: &DaoProcess,
    distribution: &TokenDistribution,
) -> Result<Process> {
    // 1. Créer le processus de distribution
    let mut process = Process::new(OutPoint::null());

    // 2. Définir la supply totale
    let total_supply = distribution.allocations.values().sum::<u64>();

    // 3. Créer l'état initial avec les allocations
    let mut allocations_map = BTreeMap::new();
    for (member_id, amount) in &distribution.allocations {
        allocations_map.insert(
            hex::encode(member_id.txid.as_byte_array()),
            json!({
                "amount": amount,
                "vesting": distribution.vesting.clone(),
            }),
        );
    }

    let public_data = json!({
        "token_id": distribution.token_id,
        "total_supply": total_supply,
        "allocations": allocations_map,
        "metadata": {
            "name": distribution.name,
            "symbol": distribution.symbol,
            "decimals": distribution.decimals,
        }
    });

    let initial_state = ProcessState::new(
        process.get_process_tip()?,
        Pcd::new(BTreeMap::new()),  // Pas de données privées
        public_data.try_into()?,
        distribution.roles.clone(),
    )?;

    // 4. Signer avec le rôle "founder"
    let founder_signatures = collect_founder_signatures(&initial_state, dao)?;
    initial_state.validation_tokens = founder_signatures;

    // 5. Insérer l'état
    process.insert_concurrent_state(initial_state)?;

    Ok(process)
}

pub struct TokenDistribution {
    token_id: [u8; 32],
    name: String,
    symbol: String,
    decimals: u8,

    // Allocations initiales
    allocations: HashMap<OutPoint, u64>,

    // Vesting (optionnel)
    vesting: Option<VestingSchedule>,

    // Rôles de gouvernance du token
    roles: Roles,
}

Exemple de distribution:

let distribution = TokenDistribution {
    token_id: sha256(b"MyDAO_TOKEN"),
    name: "MyDAO Governance Token".to_string(),
    symbol: "MDAO".to_string(),
    decimals: 18,

    allocations: HashMap::from([
        // Founders (30%)
        (founder1_id, 300_000 * 10u64.pow(18)),
        (founder2_id, 300_000 * 10u64.pow(18)),

        // Early investors (20%)
        (investor1_id, 200_000 * 10u64.pow(18)),

        // Community treasury (40%)
        (dao_treasury_id, 400_000 * 10u64.pow(18)),

        // Team (10%, vested)
        (team_pool_id, 100_000 * 10u64.pow(18)),
    ]),

    vesting: Some(VestingSchedule {
        initial_amount: 100_000 * 10u64.pow(18),
        start_block: current_block,
        duration_blocks: 52560,  // 1 an (6 blocs/h * 24h * 365j)
        cliff_blocks: 4380,      // 1 mois
        unlocked_amount: 0,
    }),

    roles: create_token_governance_roles(),
};

6.3 Transfert de tokens

pub fn transfer_tokens(
    from: &Device,
    to: &SilentPaymentAddress,
    amount: u64,
    token_process: &mut Process,
) -> Result<ProcessState> {
    // 1. Vérifier le solde
    let current_state = token_process.get_latest_committed_state().unwrap();
    let from_id = from.get_pairing_commitment().unwrap();

    let balances: HashMap<String, u64> = current_state
        .public_data
        .get_as_json("balances")?
        .as_object()
        .unwrap()
        .iter()
        .map(|(k, v)| (k.clone(), v.as_u64().unwrap()))
        .collect();

    let from_balance = balances.get(&hex::encode(from_id.txid.as_byte_array()))
        .copied()
        .unwrap_or(0);

    if from_balance < amount {
        return Err(anyhow!("Insufficient balance"));
    }

    // 2. Créer le nouvel état
    let mut new_balances = balances;
    *new_balances.get_mut(&hex::encode(from_id.txid.as_byte_array())).unwrap() -= amount;

    let to_key = hex::encode(to.get_scan_public_key().serialize());
    *new_balances.entry(to_key).or_insert(0) += amount;

    let mut new_state = current_state.clone();
    new_state.public_data.insert_serializable(
        "balances".to_string(),
        &json!(new_balances),
    )?;

    // 3. Recalculer state_id
    new_state.pcd_commitment = PcdCommitments::new(
        &new_state.commited_in,
        &Pcd::new(BTreeMap::new()),
        &new_state.roles,
    )?;
    let merkle_tree = new_state.pcd_commitment.create_merkle_tree()?;
    new_state.state_id = merkle_tree.root().unwrap();

    // 4. Signer
    let spend_key: SecretKey = from.get_sp_client().get_spend_key().try_into()?;
    let message_hash = new_state.get_message_hash(true)?;
    let proof = Proof::new(message_hash, spend_key);
    new_state.validation_tokens.push(proof);

    Ok(new_state)
}

6.4 Mécanismes anti-Sybil

Problème: Vote quadratique vulnérable aux attaques Sybil (création de multiples identités)

Solutions:

A. Preuve d'humanité

pub struct HumanityProof {
    // Preuve cryptographique d'humanité
    proof_type: HumanityProofType,

    // Données de la preuve
    proof_data: Vec<u8>,

    // Validité
    valid_until: u32,  // Hauteur de bloc
}

pub enum HumanityProofType {
    // Preuve biométrique (empreinte faciale)
    Biometric(BiometricProof),

    // Preuve sociale (attestations)
    SocialVouching(Vec<Attestation>),

    // Preuve financière (dépôt important)
    StakeProof(Amount),

    // Intégration Worldcoin/autres
    ExternalOracle(String),
}

pub struct Attestation {
    // Attestateur
    attester: OutPoint,

    // Sujet attesté
    subject: OutPoint,

    // Signature
    signature: Proof,

    // Validité
    expires_at: u32,
}

B. Coût progressif de création de compte

pub struct MembershipFee {
    // Frais de base
    base_fee: Amount,

    // Multiplicateur par compte existant
    multiplier: f32,

    // Maximum
    max_fee: Amount,
}

impl MembershipFee {
    pub fn calculate_fee(&self, existing_accounts: usize) -> Amount {
        let fee = self.base_fee.to_sat() as f32 * self.multiplier.powi(existing_accounts as i32);
        Amount::from_sat((fee as u64).min(self.max_fee.to_sat()))
    }
}

// Exemple: Frais exponentiels
let fee_structure = MembershipFee {
    base_fee: Amount::from_sat(10_000),  // 10k sats
    multiplier: 1.5,                      // +50% par compte
    max_fee: Amount::from_sat(1_000_000), // 1M sats max
};

// Coûts:
// Compte 1: 10,000 sats
// Compte 2: 15,000 sats
// Compte 3: 22,500 sats
// Compte 10: 576,650 sats

C. Délégation restreinte

pub struct DelegationPolicy {
    // Limite de délégations reçues par membre
    max_delegations_received: usize,

    // Pénalité de vote pour sur-délégation
    over_delegation_penalty: f32,
}

impl DelegationPolicy {
    pub fn effective_voting_power(
        &self,
        member: &MemberInfo,
        delegations_received: usize,
    ) -> u64 {
        let base_power = member.voting_power;

        if delegations_received <= self.max_delegations_received {
            base_power
        } else {
            // Pénalité sur les votes délégués en excès
            let excess = delegations_received - self.max_delegations_received;
            let penalty = 1.0 - (excess as f32 * self.over_delegation_penalty);
            (base_power as f32 * penalty.max(0.0)) as u64
        }
    }
}

7. Sécurité et attaques

7.1 Vecteurs d'attaque

Attaque Description Mitigation
51% Attack Contrôle majoritaire des tokens Vote quadratique, quorum élevé, timelock
Flash Loan Attack Emprunt temporaire de tokens Snapshot voting (bloc fixe)
Sybil Attack Création de multiples identités Preuve d'humanité, frais progressifs
Bribe Attack Corruption de votants Vote confidentiel, slashing
Censorship Relay bloque les messages Multi-relays, P2P gossip
Front-running Observation et devancement de votes Vote chiffré avec révélation
Governance Capture Centralisation progressive du pouvoir Limites de délégation, quorum dynamique

7.2 Flash loan attack mitigation

Problème: Emprunter massivement des tokens juste pour voter

pub struct SnapshotVoting {
    // Bloc de snapshot (avant le début du vote)
    snapshot_block: u32,

    // Balances figées à ce bloc
    snapshot_balances: HashMap<OutPoint, u64>,
}

impl SnapshotVoting {
    pub fn create_snapshot(
        proposal: &Proposal,
        lookback_blocks: u32,
    ) -> Result<Self> {
        // Snapshot N blocs avant le début du vote
        let snapshot_block = proposal.voting_start.saturating_sub(lookback_blocks);

        // Récupérer les balances à ce bloc
        let snapshot_balances = get_balances_at_block(snapshot_block)?;

        Ok(Self {
            snapshot_block,
            snapshot_balances,
        })
    }

    pub fn get_voting_power(&self, member: &OutPoint) -> u64 {
        self.snapshot_balances.get(member).copied().unwrap_or(0)
    }
}

// Utilisation
let snapshot = SnapshotVoting::create_snapshot(proposal, 144)?; // 1 jour avant
let voting_power = snapshot.get_voting_power(&member_id);

Avantages:

  • Impossible d'emprunter des tokens juste pour voter
  • Prévisibilité (pouvoir de vote connu à l'avance)
  • Résistance aux manipulations de marché

7.3 Slashing pour mauvais comportement

pub struct SlashingPolicy {
    // Types de fautes
    offenses: HashMap<OffenseType, SlashingRule>,
}

pub enum OffenseType {
    // Vote malveillant (deux fois différemment)
    DoubleVoting,

    // Proposition spam
    SpamProposal,

    // Exécution malveillante
    MaliciousExecution,

    // Violation de règles
    RuleViolation,
}

pub struct SlashingRule {
    // Pénalité (% des tokens)
    slash_percentage: f32,

    // Montant minimum slashé
    min_slash: Amount,

    // Destination des tokens slashés
    destination: SlashDestination,
}

pub enum SlashDestination {
    Burn,                    // Détruire
    Treasury,                // Trésorerie DAO
    Reporter(OutPoint),      // Celui qui a signalé
    Redistribute,            // Redistribuer aux autres membres
}

impl SlashingPolicy {
    pub fn slash_member(
        &self,
        dao: &mut DaoProcess,
        member_id: &OutPoint,
        offense: OffenseType,
        evidence: &Evidence,
    ) -> Result<Amount> {
        // 1. Vérifier l'évidence
        if !self.verify_evidence(offense, evidence)? {
            return Err(anyhow!("Invalid evidence"));
        }

        // 2. Calculer le montant à slasher
        let rule = self.offenses.get(&offense)
            .ok_or(anyhow!("Unknown offense"))?;

        let member_balance = dao.get_member_balance(member_id)?;
        let slash_amount = (member_balance.to_sat() as f32 * rule.slash_percentage) as u64;
        let slash_amount = slash_amount.max(rule.min_slash.to_sat());
        let slash_amount = Amount::from_sat(slash_amount);

        // 3. Exécuter le slashing
        dao.reduce_member_balance(member_id, slash_amount)?;

        // 4. Distribuer selon la règle
        match &rule.destination {
            SlashDestination::Burn => {
                dao.burn_tokens(slash_amount)?;
            }
            SlashDestination::Treasury => {
                dao.transfer_to_treasury(slash_amount)?;
            }
            SlashDestination::Reporter(reporter) => {
                dao.transfer_to_member(reporter, slash_amount)?;
            }
            SlashDestination::Redistribute => {
                dao.redistribute_to_all(slash_amount)?;
            }
        }

        // 5. Enregistrer l'événement
        dao.record_slashing_event(member_id, offense, slash_amount)?;

        Ok(slash_amount)
    }
}

7.4 Quorum dynamique

Objectif: Adapter le quorum selon l'activité de la DAO

pub struct DynamicQuorum {
    // Quorum de base
    base_quorum: f32,

    // Historique de participation
    participation_history: Vec<f32>,

    // Fenêtre d'historique
    history_window: usize,
}

impl DynamicQuorum {
    pub fn calculate_quorum(&self) -> f32 {
        if self.participation_history.is_empty() {
            return self.base_quorum;
        }

        // Moyenne de participation récente
        let avg_participation: f32 = self.participation_history.iter().sum::<f32>()
                                    / self.participation_history.len() as f32;

        // Quorum = 50% de la participation moyenne (avec minimum)
        let dynamic_quorum = avg_participation * 0.5;
        dynamic_quorum.max(self.base_quorum)
    }

    pub fn update_with_vote(&mut self, participation: f32) {
        self.participation_history.push(participation);

        // Garder seulement les N derniers votes
        if self.participation_history.len() > self.history_window {
            self.participation_history.remove(0);
        }
    }
}

// Exemple
let mut quorum = DynamicQuorum {
    base_quorum: 0.1,       // 10% minimum
    participation_history: vec![],
    history_window: 10,     // 10 derniers votes
};

// Après plusieurs votes:
// Participations: [0.3, 0.4, 0.35, 0.5, 0.45, 0.4, 0.38, 0.42, 0.36, 0.41]
// Moyenne: 0.397
// Quorum dynamique: 0.397 * 0.5 = 0.1985 ≈ 20%

Avantages:

  • S'adapte à l'activité réelle
  • Évite les blocages (quorum trop élevé)
  • Maintient la légitimité (quorum basé sur participation)

7.5 Rate limiting

pub struct RateLimiter {
    // Limite de propositions par membre
    max_proposals_per_period: usize,

    // Période (en blocs)
    period_blocks: u32,

    // Historique
    proposal_history: HashMap<OutPoint, Vec<u32>>,
}

impl RateLimiter {
    pub fn can_propose(&mut self, member_id: &OutPoint, current_block: u32) -> bool {
        let history = self.proposal_history
            .entry(*member_id)
            .or_insert_with(Vec::new);

        // Nettoyer l'historique (garder seulement la période récente)
        history.retain(|&block| block > current_block.saturating_sub(self.period_blocks));

        // Vérifier la limite
        if history.len() >= self.max_proposals_per_period {
            return false;
        }

        // Ajouter cette proposition
        history.push(current_block);
        true
    }
}

// Exemple: Max 3 propositions par semaine
let limiter = RateLimiter {
    max_proposals_per_period: 3,
    period_blocks: 1008,  // ~1 semaine
    proposal_history: HashMap::new(),
};

8. Implémentation de référence

8.1 Structure complète d'une DAO

pub struct CompleteDAO {
    // Processus principal (gouvernance)
    governance: DaoProcess,

    // Processus de trésorerie
    treasury: TreasuryProcess,

    // Processus de distribution de tokens
    token_distribution: Process,

    // Processus d'adhésion
    membership: Process,

    // Configuration
    config: DaoConfiguration,

    // Politiques de sécurité
    security: SecurityPolicies,
}

pub struct DaoConfiguration {
    // Identité
    name: String,
    description: String,
    website: String,

    // Gouvernance
    voting_system: VotingSystem,
    quorum: DynamicQuorum,
    majority: f32,
    voting_period_blocks: u32,
    execution_delay_blocks: u32,

    // Tokens
    token_name: String,
    token_symbol: String,
    total_supply: u64,

    // Membres
    min_stake: Amount,
    membership_fee: MembershipFee,

    // Trésorerie
    treasury_multisig: MultisigConfig,

    // Sécurité
    slashing: SlashingPolicy,
    rate_limiting: RateLimiter,
}

pub struct SecurityPolicies {
    // Anti-Sybil
    humanity_proof_required: bool,

    // Flash loan protection
    snapshot_voting: bool,
    snapshot_lookback_blocks: u32,

    // Slashing
    slashing_enabled: bool,

    // Timelock
    timelocks: ExecutionTimelock,

    // Rate limiting
    rate_limiting_enabled: bool,
}

8.2 Initialisation d'une DAO

pub async fn initialize_dao(
    founders: Vec<Device>,
    config: DaoConfiguration,
) -> Result<CompleteDAO> {
    // 1. Créer le processus de gouvernance
    let governance_process = create_governance_process(&founders, &config)?;

    // 2. Créer le processus de trésorerie
    let treasury_process = create_treasury_process(&founders, &config.treasury_multisig)?;

    // 3. Créer la distribution de tokens
    let token_process = create_token_distribution(&config)?;

    // 4. Créer le processus d'adhésion
    let membership_process = create_membership_process(&founders, &config)?;

    // 5. Lier les processus
    link_processes(&governance_process, &treasury_process)?;
    link_processes(&governance_process, &token_process)?;
    link_processes(&governance_process, &membership_process)?;

    // 6. Commit initial on-chain
    let governance_tx = commit_initial_state(&governance_process).await?;
    let treasury_tx = commit_initial_state(&treasury_process).await?;
    let token_tx = commit_initial_state(&token_process).await?;
    let membership_tx = commit_initial_state(&membership_process).await?;

    // 7. Attendre les confirmations
    wait_for_confirmations(&[governance_tx, treasury_tx, token_tx, membership_tx], 6).await?;

    // 8. Construire la DAO complète
    let dao = CompleteDAO {
        governance: DaoProcess {
            process: governance_process,
            config: config.clone(),
            current_state: DaoState::default(),
        },
        treasury: treasury_process,
        token_distribution: token_process,
        membership: membership_process,
        config,
        security: SecurityPolicies::default(),
    };

    Ok(dao)
}

fn create_governance_process(
    founders: &[Device],
    config: &DaoConfiguration,
) -> Result<Process> {
    let mut process = Process::new(OutPoint::null());

    // Rôles initiaux
    let founder_ids: Vec<OutPoint> = founders.iter()
        .map(|f| f.get_pairing_commitment().unwrap())
        .collect();

    let roles = Roles::new(BTreeMap::from([
        // Founders: Contrôle total initial
        ("founder".to_string(), RoleDefinition {
            members: founder_ids.clone(),
            validation_rules: vec![
                ValidationRule::new(0.67, vec!["proposals", "config", "roles"], 1.0)?,
            ],
            storages: vec![],
        }),

        // Members: Peuvent voter et proposer
        ("member".to_string(), RoleDefinition {
            members: vec![],  // Sera rempli au fur et à mesure
            validation_rules: vec![
                ValidationRule::new(0.01, vec!["proposals"], 0.5)?,
                ValidationRule::new(0.0, vec!["votes"], 0.5)?,
            ],
            storages: vec![],
        }),

        // Guardians: Peuvent annuler des propositions
        ("guardian".to_string(), RoleDefinition {
            members: founder_ids.clone(),
            validation_rules: vec![
                ValidationRule::new(0.51, vec!["cancellations"], 1.0)?,
            ],
            storages: vec![],
        }),

        // Emergency: Exécution rapide
        ("emergency".to_string(), RoleDefinition {
            members: founder_ids.clone(),
            validation_rules: vec![
                ValidationRule::new(0.9, vec!["emergency_actions"], 1.0)?,
            ],
            storages: vec![],
        }),
    ]));

    // État initial
    let initial_data = json!({
        "name": config.name,
        "description": config.description,
        "proposals": [],
        "members": founder_ids.iter().map(|id| {
            json!({
                "id": hex::encode(id.txid.as_byte_array()),
                "voting_power": 1,
                "joined_at": 0,
            })
        }).collect::<Vec<_>>(),
        "config": config,
    });

    let initial_state = ProcessState::new(
        process.get_process_tip()?,
        Pcd::new(BTreeMap::new()),
        initial_data.try_into()?,
        roles,
    )?;

    // Signatures des founders
    let mut validation_tokens = vec![];
    for founder in founders {
        let spend_key: SecretKey = founder.get_sp_client().get_spend_key().try_into()?;
        let message_hash = initial_state.get_message_hash(true)?;
        validation_tokens.push(Proof::new(message_hash, spend_key));
    }
    initial_state.validation_tokens = validation_tokens;

    process.insert_concurrent_state(initial_state)?;

    Ok(process)
}

8.3 Cycle de vie complet

┌─────────────────────────────────────────────────────────┐
│  PHASE 1: INITIALISATION                                │
│  ├─ Création des processus (gouvernance, trésorerie)    │
│  ├─ Distribution initiale de tokens                     │
│  ├─ Définition des rôles et permissions                 │
│  └─ Commit initial on-chain                             │
└─────────────────┬───────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────┐
│  PHASE 2: CROISSANCE                                    │
│  ├─ Admission de nouveaux membres                       │
│  ├─ Propositions et votes actifs                        │
│  ├─ Gestion de trésorerie                               │
│  └─ Évolution de la gouvernance                         │
└─────────────────┬───────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────┐
│  PHASE 3: MATURITÉ                                      │
│  ├─ Décentralisation progressive                        │
│  ├─ Réduction du pouvoir des founders                   │
│  ├─ Délégation de responsabilités                       │
│  └─ Optimisation des processus                          │
└─────────────────┬───────────────────────────────────────┘
                  │
                  ▼
┌─────────────────────────────────────────────────────────┐
│  PHASE 4: AUTONOMIE                                     │
│  ├─ Gouvernance entièrement décentralisée               │
│  ├─ Exécution automatique de propositions               │
│  ├─ Intégration avec d'autres protocoles                │
│  └─ Évolution continue                                  │
└─────────────────────────────────────────────────────────┘

Conclusion

Points forts d'une DAO sur 4NK

Identité cryptographique : Silent Payments Bitcoin (souveraineté) Gouvernance on-chain : Immuabilité et traçabilité complète Flexibilité des rôles : ValidationRules adaptables Trésorerie native : Gestion Bitcoin multisig Sécurité cryptographique : Signatures Schnorr, Merkle commitments Multi-device : Pairing pour résilience Auditabilité : Tous les états sur blockchain Pas de smart contracts : Pas de vulnérabilités Solidity

Défis techniques

⚠️ Complexité : Architecture multi-processus ⚠️ Coûts : Frais Bitcoin par état ⚠️ Latence : Confirmations blockchain (10 min) ⚠️ Scalabilité : Limité par débit Bitcoin ⚠️ UX : Courbe d'apprentissage

Recommandations

  1. Démarrage : Utiliser un petit groupe de founders de confiance
  2. Décentralisation progressive : Réduire le pouvoir des founders sur 6-12 mois
  3. Tests extensifs : Déployer d'abord sur testnet/signet
  4. Auditabilité : Outils de monitoring et dashboards
  5. Sécurité : Audits réguliers, bug bounties

Prochaines étapes

  • 🔄 Lightning Network : Votes off-chain avec règlement on-chain
  • 🔄 ZK-SNARKs : Vote confidentiel vérifiable
  • 🔄 Cross-chain bridges : Interopérabilité avec autres blockchains
  • 🔄 Optimisations : Batch voting, état compressé

Document complet: 165 pages Code de référence: https://git.4nkweb.com/4nk/dao-reference Contact: dao@4nkweb.com