4NK_env/docs/4NK_DAO_TECHNICAL_SPECIFICATION.md

2403 lines
72 KiB
Markdown

# 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](./4NK_IDENTITY_AND_PROCESS_SPEC.md)
---
## Table des matières
1. [Architecture d'une DAO 4NK](#1-architecture-dune-dao-4nk)
2. [Mécanismes de gouvernance](#2-mécanismes-de-gouvernance)
3. [Système de vote on-chain](#3-système-de-vote-on-chain)
4. [Gestion de trésorerie](#4-gestion-de-trésorerie)
5. [Propositions et exécution](#5-propositions-et-exécution)
6. [Tokenisation et pondération](#6-tokenisation-et-pondération)
7. [Sécurité et attaques](#7-sécurité-et-attaques)
8. [Implémentation de référence](#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
```rust
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
```rust
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)
```rust
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)
```rust
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:**
```rust
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
```rust
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:**
```rust
// 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é
```rust
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:**
```rust
// 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:**
```rust
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.
```rust
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
```rust
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:
```rust
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:
```rust
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é)**
```rust
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)**
```rust
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
```rust
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:**
```rust
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
```rust
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.
```rust
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
```rust
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:**
```rust
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
```rust
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
```rust
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"
```rust
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:**
```rust
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
```rust
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é
```rust
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
```rust
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
```rust
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
```rust
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
```rust
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
```rust
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
```rust
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
```rust
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
```rust
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