4NK_env/docs/4NK_IDENTITY_AND_PROCESS_SPEC.md

1511 lines
47 KiB
Markdown

# Spécification du Système d'Identité et de Processus 4NK
**Version:** 1.0
**Date:** 1 octobre 2025
**Auteur:** Analyse complète des composants sdk_client, sdk_common, sdk_relay, sdk_storage, ihm_client, rust-silentPayments
## Table des matières
1. [Vue d'ensemble](#vue-densemble)
2. [Architecture d'identité](#architecture-didentité)
3. [Système de processus](#système-de-processus)
4. [Validation et consensus](#validation-et-consensus)
5. [Communication réseau](#communication-réseau)
6. [Stockage et persistance](#stockage-et-persistance)
7. [Flux de données](#flux-de-données)
8. [Sécurité](#sécurité)
---
## 1. Vue d'ensemble
### 1.1 Philosophie du système
4NK est un système décentralisé de gestion de processus collaboratifs basé sur la blockchain Bitcoin, utilisant les **Silent Payments (BIP352)** pour l'identité et le chiffrement. Le système permet à plusieurs parties de collaborer sur des processus avec des états vérifiables, des rôles définis et des règles de validation cryptographiques.
### 1.2 Principes fondamentaux
- **Identité décentralisée** : Basée sur des adresses Silent Payment Bitcoin
- **Device-centric** : Chaque appareil possède sa propre clé cryptographique
- **Multi-device** : Un utilisateur peut gérer plusieurs appareils via le pairing
- **Processus à états** : Évolution contrôlée par des commitments Bitcoin
- **Validation distribuée** : Signatures cryptographiques multiples requises
- **Communication P2P** : Relais WebSocket pour synchronisation temps réel
### 1.3 Composants principaux
| Composant | Rôle | Technologie |
|-----------|------|-------------|
| `sdk_common` | Types et structures partagés | Rust (WASM-ready) |
| `sdk_client` | Bibliothèque cliente WASM | Rust → WebAssembly |
| `sdk_relay` | Relais de messages et synchronisation | Rust + WebSocket |
| `sdk_storage` | Stockage clé-valeur distribué | Rust + HTTP |
| `ihm_client` | Interface utilisateur web | TypeScript + Web Components |
| `rust-silentPayments` | Implémentation Silent Payments | Rust (dépendance) |
---
## 2. Architecture d'identité
### 2.1 Hiérarchie d'identité
```
Device
├─ SpClient (Silent Payment Client)
│ ├─ Scan Key (privée)
│ ├─ Spend Key (privée)
│ └─ Silent Payment Address (publique)
├─ SpWallet (outputs UTXO)
├─ Pairing Process (OutPoint)
└─ Member (liste d'adresses pairées)
```
### 2.2 Structure `Device`
**Fichier source:** `sdk_common/src/device.rs`
```rust
pub struct Device {
sp_wallet: SpWallet, // Portefeuille Silent Payments
pairing_process_commitment: Option<OutPoint>, // ID du processus de pairing
paired_member: Member, // Groupe d'adresses pairées
}
```
**Opérations principales:**
1. **Création** : `Device::new(sp_client)`
- Génère une adresse Silent Payment locale
- Initialise un `Member` avec cette adresse seule
2. **Pairing** : `device.pair(commitment_outpoint, member)`
- Associe le device à un processus de pairing
- Lie plusieurs adresses Silent Payment ensemble
3. **Unpairing** : `device.unpair()`
- Réinitialise à l'état local unique
### 2.3 Structure `Member`
**Fichier source:** `sdk_common/src/pcd.rs` (lignes 140-223)
```rust
pub struct Member {
sp_addresses: Vec<String>, // Liste d'adresses Silent Payment
}
```
**Caractéristiques:**
- Représente un ensemble d'appareils appartenant à une même entité
- Utilisé pour les validations multi-signatures
- Comparaison indépendante de l'ordre (HashSet interne)
- Sérialisation déterministe (tri automatique)
**Méthodes clés:**
```rust
pub fn new(sp_addresses: Vec<SilentPaymentAddress>) -> Self
pub fn get_addresses(&self) -> Vec<String>
pub fn key_is_part_of_member(&self, key: &PublicKey) -> bool
pub fn get_address_for_key(&self, key: &PublicKey) -> Option<String>
```
### 2.4 Processus de pairing
Le **pairing** est le mécanisme permettant d'associer plusieurs appareils (devices) à une seule identité logique (member).
#### Étapes du pairing:
1. **Création du processus de pairing**
- Un device crée un nouveau `Process` avec un rôle spécial `"pairing"`
- Le `ProcessState` contient un champ public `"pairedAddresses"` avec la liste des adresses
2. **Validation du pairing**
- Fichier: `sdk_common/src/process.rs` (lignes 161-200)
- Règle: `members` doit être vide (pas de membres préexistants)
- Une seule règle de validation pour le champ `"pairedAddresses"`
- Signatures requises:
- **Création** : Signature de la nouvelle adresse seule
- **Ajout** : Signature des adresses déjà pairées
- **Retrait** : Signatures de tous les devices (consensus)
3. **État après pairing**
```rust
device.pairing_process_commitment = Some(process_id);
device.paired_member = Member::new(all_paired_addresses);
```
**Code de validation (extrait):**
```rust
fn handle_pairing(
&self,
pairing_role: RoleDefinition,
previous_addresses: Vec<SilentPaymentAddress>,
) -> anyhow::Result<()> {
// members must be empty
if !pairing_role.members.is_empty() {
return Err(anyhow::Error::msg("Invalid pairing role"));
}
let updated_addresses_json = self.public_data.get_as_json(PAIREDADDRESSES)?;
let updated_member = Member::new(updated_addresses_json);
let previous_member = Member::new(previous_addresses);
let members = if previous_member.get_addresses().is_empty() {
vec![&updated_member] // Création
} else {
vec![&previous_member] // Modification
};
// Validation des signatures
pairing_rule.is_satisfied(PAIREDADDRESSES, state_id, validation_tokens, members)
}
```
### 2.5 Identité dans le réseau
Chaque participant au réseau 4NK est identifié par:
1. **Adresse Silent Payment** : Identité publique cryptographique
2. **Process ID (OutPoint)** : Identifiant du processus de pairing (si multi-device)
3. **Member** : Groupe d'adresses associées
**Carte d'identité réseau:**
```rust
// Fichier: sdk_common/src/serialization.rs
pub struct OutPointMemberMap(pub HashMap<OutPoint, Member>);
```
Cette structure permet au réseau de:
- Résoudre un `process_id` vers un groupe d'adresses
- Vérifier qu'une signature provient d'un membre légitime
- Gérer les membres multi-appareils de façon transparente
---
## 3. Système de processus
### 3.1 Modèle de processus
Un **Process** est une machine à états distribuée, commitée sur la blockchain Bitcoin.
```
Process
└─ states: Vec<ProcessState>
├─ State 0 (initial empty)
├─ State 1 (premier commit)
├─ State 2 (update)
├─ ...
└─ State n (dernier = toujours empty)
```
**Règle fondamentale:** Le dernier état est toujours vide, représentant le prochain UTXO à dépenser.
### 3.2 Structure `ProcessState`
**Fichier source:** `sdk_common/src/process.rs` (lignes 19-35)
```rust
pub struct ProcessState {
pub commited_in: OutPoint, // UTXO Bitcoin portant cet état
pub pcd_commitment: PcdCommitments, // Map field -> hash(value)
pub state_id: [u8; 32], // Merkle root (identifiant unique)
pub keys: BTreeMap<String, [u8; 32]>, // Clés de déchiffrement (optionnel)
pub validation_tokens: Vec<Proof>, // Signatures de validation
pub public_data: Pcd, // Données publiques (non hashées)
pub roles: Roles, // Définition des rôles
}
```
#### Éléments clés:
1. **`commited_in`** : UTXO Bitcoin qui "porte" cet état
- Permet de lier l'état à la blockchain
- Forme l'identifiant du processus (premier `commited_in`)
2. **`pcd_commitment`** : Commitments Merkle des données privées
- Map `field_name -> hash(compressed_value)`
- Hash taggé: `AnkPcdHash::from_pcd_value(data, field, outpoint)`
3. **`state_id`** : Racine Merkle de tous les commitments + rôles
- Identifiant unique de l'état
- Utilisé pour les signatures de validation
4. **`validation_tokens`** : Signatures cryptographiques
- Prouvent l'approbation des membres
- Type: `Proof` (signature Schnorr sur `state_id`)
5. **`public_data`** : Données non chiffrées
- Lisibles par tous les participants
- Exemple: `"pairedAddresses"` pour le pairing
6. **`roles`** : Définition des rôles et permissions
- Map `role_name -> RoleDefinition`
- Contrôle qui peut modifier quels champs
### 3.3 Cycle de vie d'un processus
```
┌─────────────────┐
│ 1. Création │
│ Process::new() │
│ État initial │
│ vide │
└────────┬────────┘
v
┌──────────────────────┐
│ 2. Proposition │
│ insert_concurrent │
│ _state() │
│ + validation_tokens │
└────────┬─────────────┘
v
┌─────────────────────────┐
│ 3. Validation │
│ ProcessState::is_valid()│
│ Vérif. signatures │
└────────┬────────────────┘
v
┌──────────────────────────┐
│ 4. Commit blockchain │
│ Transaction avec │
│ OP_RETURN (state_id) │
└────────┬─────────────────┘
v
┌──────────────────────────┐
│ 5. Confirmation │
│ update_states_tip() │
│ Nouvel UTXO │
└────────┬─────────────────┘
v
(retour étape 2)
```
### 3.4 États concurrents
Le système gère la **concurrence** via des états multiples pour un même `commited_in`:
```
Process {
states: [
State(outpoint_0, state_id_A), // Commité
State(outpoint_1, state_id_B), // Concurrent 1
State(outpoint_1, state_id_C), // Concurrent 2
State(outpoint_1, [0u8; 32]), // Empty (tip)
]
}
```
- Plusieurs propositions peuvent coexister
- Une seule sera commitée on-chain
- Les autres sont prunées après confirmation
**Méthodes de gestion:**
```rust
fn get_latest_concurrent_states(&self) -> Vec<&ProcessState>
fn remove_all_concurrent_states(&mut self) -> Vec<ProcessState>
```
### 3.5 Types de processus spéciaux
#### 3.5.1 Processus de pairing
- **Rôle:** `"pairing"`
- **Champ public:** `"pairedAddresses"`
- **Règle:** Membres vides, validation par adresses existantes
#### 3.5.2 Oblitération (Termination)
- **État:** `state_id = [0u8; 32]`
- **Rôle:** `"apophis"` (défini dans l'état précédent)
- **Effet:** Termine définitivement le processus
- **Fichier:** `sdk_common/src/process.rs` (lignes 133-159)
```rust
fn handle_obliteration(&self, apophis: &RoleDefinition, members_list: &OutPointMemberMap) -> Result<()> {
// Vérifie que le rôle "apophis" approuve l'oblitération
let empty_field = "";
apophis.is_satisfied(
vec![empty_field.to_owned()],
[0u8; 32],
&self.validation_tokens,
members_list,
)
}
```
#### 3.5.3 Rôle "Demiurge"
- **Contexte:** État initial uniquement
- **Pouvoir:** Créer tous les champs initiaux
- **Règle auto-générée:**
```rust
ValidationRule::new(1.0, all_keys, 1.0)
```
- **Fichier:** `sdk_common/src/process.rs` (lignes 103-130)
---
## 4. Validation et consensus
### 4.1 Architecture de validation
La validation dans 4NK repose sur un système de **rôles**, **règles** et **preuves cryptographiques**.
```
Validation Flow:
ProcessState
└─> roles: Roles
└─> RoleDefinition
├─> members: Vec<OutPoint>
├─> validation_rules: Vec<ValidationRule>
└─> storages: Vec<String>
```
### 4.2 Structure `RoleDefinition`
**Fichier source:** `sdk_common/src/pcd.rs` (lignes 564-610)
```rust
pub struct RoleDefinition {
pub members: Vec<OutPoint>, // Process IDs des membres
pub validation_rules: Vec<ValidationRule>, // Règles de modification
pub storages: Vec<String>, // Storages autorisés
}
```
**Caractéristiques:**
- **members** : Utilise des `OutPoint` (process IDs) plutôt que des adresses
- Permet d'ajouter des devices sans modifier les rôles
- Résolu via `OutPointMemberMap` au moment de la validation
- **validation_rules** : Définit quels champs peuvent être modifiés
- Quorum requis
- Signatures minimales par membre
- **storages** : Liste des storages où les données peuvent être déposées
### 4.3 Structure `ValidationRule`
**Fichier source:** `sdk_common/src/pcd.rs` (lignes 429-562)
```rust
pub struct ValidationRule {
quorum: f32, // 0.0 à 1.0 (proportion de membres requis)
fields: Vec<String>, // Champs concernés par cette règle
min_sig_member: f32, // 0.0 à 1.0 (proportion de devices par membre)
}
```
**Exemple concret:**
```rust
// Règle : 50% des membres, chacun avec au moins 1 device
ValidationRule {
quorum: 0.5,
fields: vec!["contract".to_string(), "amount".to_string()],
min_sig_member: 0.5,
}
```
**Interprétation:**
- 50% des membres du rôle doivent approuver
- Chaque membre approuvant doit fournir au moins 50% de ses devices
- S'applique aux modifications de `"contract"` et `"amount"`
### 4.4 Processus de validation
**Fichier source:** `sdk_common/src/process.rs` (lignes 202-321)
#### Étapes de validation:
```rust
pub fn is_valid(
&self,
previous_state: Option<&ProcessState>,
members_list: &OutPointMemberMap,
) -> anyhow::Result<()>
```
1. **Vérification de base**
- `validation_tokens` non vide
2. **Cas spéciaux** (prioritaires):
- **Oblitération** : `state_id == [0u8; 32]`
- **Pairing** : Rôle `"pairing"` présent
3. **Gestion du Demiurge** (état initial uniquement):
```rust
if previous_state.is_none() {
if let Some(demiurge) = roles.get("demiurge") {
// Génère une règle couvrant tous les champs
}
}
```
4. **Validation par champ**:
Pour chaque champ modifié:
- Trouver les rôles applicables
- Vérifier qu'au moins un rôle valide le champ
- Vérifier les signatures cryptographiques
**Algorithme de validation:**
```rust
let all_fields_validated: bool = self.pcd_commitment.keys().all(|field| {
let applicable_roles = roles.filter(|role| role.has_rule_for(field));
applicable_roles.into_iter().any(|(role_name, role_def)| {
role_def.validation_rules.iter().any(|rule| {
let members = role_def.members
.filter_map(|outpoint| members_list.get(outpoint))
.collect();
rule.is_satisfied(field, state_id, validation_tokens, members).is_ok()
})
})
});
```
### 4.5 Validation d'une règle
**Fichier source:** `sdk_common/src/pcd.rs` (lignes 466-562)
```rust
pub fn is_satisfied(
&self,
field: &str,
merkle_root: [u8; 32],
proofs: &[Proof],
members: &[&Member],
) -> Result<()>
```
#### Algorithme:
1. **Calcul du quorum**:
```rust
let required_members = (members.len() as f32 * self.quorum).ceil() as usize;
```
2. **Filtrage des membres validants**:
```rust
let validating_members = members.iter().filter(|member| {
let member_proofs = proofs.iter()
.filter(|p| member.key_is_part_of_member(&p.get_key()))
.collect();
self.satisfy_min_sig_member(member, merkle_root, member_proofs).is_ok()
}).count();
```
3. **Vérification du quorum**:
```rust
validating_members >= required_members
```
### 4.6 Signatures cryptographiques
#### Structure `Proof`
```rust
pub struct Proof {
signature: SchnorrSignature,
public_key: PublicKey,
message: [u8; 32],
}
```
**Types de messages signés:**
```rust
pub enum AnkHash {
ValidationYes(AnkValidationYesHash),
ValidationNo(AnkValidationNoHash),
}
```
- **ValidationYes** : Approbation du `state_id`
- **ValidationNo** : Rejet du `state_id`
**Création d'une signature:**
```rust
let message_hash = state.get_message_hash(true)?; // true = yes
let proof = Proof::new(message_hash, spend_key);
state.validation_tokens.push(proof);
```
**Vérification:**
```rust
fn satisfy_min_sig_member(&self, member: &Member, merkle_root: [u8; 32], proofs: &[&Proof]) -> Result<()> {
let required_sigs = (member.addresses().len() as f32 * self.min_sig_member).ceil();
let yes_votes = proofs.iter()
.filter(|p| p.verify().is_ok())
.filter(|p| p.get_message() == AnkValidationYesHash::from_merkle_root(merkle_root))
.count();
if yes_votes >= required_sigs {
Ok(())
} else {
Err("Not enough yes votes")
}
}
```
### 4.7 Scénarios de validation
#### Scénario 1: Création de processus (2 membres, quorum 100%)
```rust
// Membres
let alice_bob = Member::new(vec![alice_addr, bob_addr]);
let carol = Member::new(vec![carol_addr]);
// Rôle
let role = RoleDefinition {
members: vec![alice_bob_pairing_id, carol_pairing_id],
validation_rules: vec![
ValidationRule::new(1.0, vec!["field1"], 0.5) // quorum 100%, 50% devices
],
};
// Validation
// Alice signe seule (50% de alice_bob) -> ❌ Insuffisant (1/2 membres)
// Alice + Carol signent -> ✅ Valide (2/2 membres, quorum atteint)
```
#### Scénario 2: Update avec plusieurs rôles
```rust
// Rôle 1: Owner (peut modifier "roles")
let owner = RoleDefinition {
members: vec![alice_pairing_id],
validation_rules: vec![
ValidationRule::new(1.0, vec!["roles"], 1.0)
],
};
// Rôle 2: Validator (peut modifier "idCertified", "roles")
let validator = RoleDefinition {
members: vec![bob_pairing_id],
validation_rules: vec![
ValidationRule::new(0.5, vec!["idCertified", "roles"], 1.0)
],
};
// Modification de "roles" -> Peut être validé par owner OU validator
```
---
## 5. Communication réseau
### 5.1 Architecture réseau
```
┌─────────────┐ WebSocket ┌─────────────┐
│ ihm_client │◄────────────────────────────►│ sdk_relay │
│ (WASM) │ │ (Rust) │
└─────────────┘ └──────┬──────┘
┌───────┴──────┐
│ │
Bitcoin RPC ZMQ Events
Blindbit API (rawtx, hashblock)
```
### 5.2 Protocole WebSocket
**Fichier source:** `sdk_common/src/network.rs`
#### Types de messages (`AnkFlag`):
```rust
pub enum AnkFlag {
Handshake, // Synchronisation initiale
NewTx, // Nouvelle transaction détectée
Cipher, // Données chiffrées
Commit, // Demande de commit on-chain
Faucet, // Demande de fonds (testnet)
Sync, // Synchronisation d'état
}
```
#### Structure d'enveloppe:
```rust
pub struct Envelope {
pub flag: AnkFlag,
pub content: String, // JSON sérialisé
}
```
### 5.3 Message types
#### 5.3.1 HandshakeMessage
**Envoyé par:** sdk_relay → client (à la connexion)
```rust
pub struct HandshakeMessage {
pub sp_address: String, // Adresse du relay
pub peers_list: OutPointMemberMap, // Carte des membres
pub processes_list: OutPointProcessMap, // Processus connus
pub chain_tip: u32, // Hauteur de bloc
}
```
**Objectif:** Synchroniser l'état initial du réseau
#### 5.3.2 NewTxMessage
**Envoyé par:** sdk_relay → clients (transaction détectée)
```rust
pub struct NewTxMessage {
pub transaction: String, // Transaction hex
pub tweak_data: Option<String>, // Données de tweak Silent Payment
pub error: Option<AnkError>,
}
```
**Objectif:** Notifier une nouvelle transaction Bitcoin concernant les participants
#### 5.3.3 CommitMessage
**Envoyé par:** client → sdk_relay (demande de commit)
```rust
pub struct CommitMessage {
pub process_id: OutPoint,
pub pcd_commitment: PcdCommitments,
pub roles: Roles,
pub public_data: Pcd,
pub validation_tokens: Vec<Proof>,
pub error: Option<AnkError>,
}
```
**Flux de commit:**
1. **Client** : Envoie `CommitMessage` avec `validation_tokens = []`
- Annonce l'intention de commit
2. **Relay** : Diffuse aux autres clients
3. **Autres clients** : Signent et renvoient leurs `validation_tokens`
4. **Client initiateur** : Accumule les signatures, renvoie `CommitMessage` complet
5. **Relay** : Vérifie les signatures, crée la transaction Bitcoin, broadcast
### 5.4 Gestion des connexions
**Fichier source:** `sdk_relay/src/main.rs` (lignes 178-242)
```rust
async fn handle_connection(
raw_stream: TcpStream,
addr: SocketAddr,
our_sp_address: SilentPaymentAddress,
) {
let ws_stream = tokio_tungstenite::accept_async(raw_stream).await?;
// Insertion dans la peer map
let (tx, rx) = unbounded_channel();
PEERMAP.lock().insert(addr, tx);
// Envoi du handshake
let init_msg = HandshakeMessage::new(
our_sp_address,
members_list,
processes_list,
chain_tip,
);
broadcast_message(AnkFlag::Handshake, init_msg, BroadcastType::Sender(addr));
// Gestion des messages
let (outgoing, incoming) = ws_stream.split();
incoming.try_for_each(|msg| {
process_message(msg.to_text(), addr);
future::ok(())
});
}
```
**Types de broadcast:**
```rust
pub enum BroadcastType {
All, // Tous les peers
Sender(SocketAddr), // Peer spécifique
Exclude(SocketAddr), // Tous sauf un
}
```
### 5.5 Synchronisation blockchain
**Fichier source:** `sdk_relay/src/sync.rs`
Le relay écoute les événements Bitcoin via:
1. **ZMQ** :
- `rawtx` : Transactions dans le mempool
- `hashblock` : Nouveaux blocs minés
2. **Bitcoin RPC** :
- Récupération des blocs complets
- Vérification des confirmations
**Détection de transactions pertinentes:**
```rust
pub fn check_tx_for_process_updates(tx: &Transaction) -> Result<OutPoint> {
let processes = lock_processes()?;
for (process_id, process) in processes.iter() {
let process_tip = process.get_process_tip()?;
// Vérifie si la tx dépense le tip
if tx.input.iter().any(|input| input.previous_output == process_tip) {
// Extrait le state_id de l'OP_RETURN
let op_return = tx.output.iter()
.find(|o| o.script_pubkey.is_op_return())?;
let state_id = &op_return.script_pubkey.as_bytes()[2..34];
// Met à jour le processus
process.commit_state(state_id)?;
return Ok(*process_id);
}
}
Err("No matching process")
}
```
---
## 6. Stockage et persistance
### 6.1 Architecture de stockage
```
┌────────────────────────────────────────────────┐
│ Couche Application │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Process │ │ Member │ │ Device │ │
│ │ State │ │ Registry │ │ Wallet │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└────────────────────┬───────────────────────────┘
┌────────────────────▼───────────────────────────┐
│ Couche de Données │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ sdk_storage │ │ Blockchain │ │
│ │ (Key-Value) │ │ Bitcoin │ │
│ └─────────────┘ └─────────────┘ │
└────────────────────────────────────────────────┘
```
### 6.2 sdk_storage : Stockage clé-valeur
**Fichier source:** `sdk_storage/src/main.rs`
#### Architecture:
- **Type:** Serveur HTTP REST
- **Stockage:** Fichiers dans `~/.4nk/storage/`
- **TTL:** Optionnel (expiration automatique)
#### API:
```http
POST /store
{
"key": "hex64", # Clé 32 bytes en hex
"value": "hex", # Valeur en hex
"ttl": 3600 # Optional: secondes
}
GET /retrieve/:key
→ { "value": "hex" }
```
#### Utilisation dans 4NK:
```rust
// RoleDefinition indique les storages autorisés
pub struct RoleDefinition {
pub storages: Vec<String>, // URLs des sdk_storage
}
```
**Cas d'usage:**
1. **Données chiffrées volumineuses** : Documents, images
2. **Partage de clés** : Clés de déchiffrement AES
3. **Coordination** : États temporaires avant commit
### 6.3 Données sur blockchain
**Seules les données commitées on-chain:**
```rust
// Transaction Bitcoin
TxOut {
value: Amount::ZERO,
script_pubkey: ScriptBuf::new_op_return(&state_id), // 32 bytes
}
```
- **state_id** : Merkle root identifiant l'état
- **Pas de données brutes** sur la blockchain
- **Récupération** : Via sdk_storage ou pairs
### 6.4 Structure des données (PCD)
**Fichier source:** `sdk_common/src/pcd.rs` (lignes 225-333)
#### PCD (Private Collaborative Data)
```rust
pub struct Pcd(BTreeMap<String, Vec<u8>>);
```
**Format de sérialisation:**
```
┌─────────────────────────┐
│ Version (1 byte): 0x01 │
├─────────────────────────┤
│ DataType (1 byte): │
│ 0 = FileBlob │
│ 1 = JSON │
├─────────────────────────┤
│ Compressed data (zstd) │
└─────────────────────────┘
```
**Exemple d'utilisation:**
```rust
// Insertion
let mut pcd = Pcd::new(BTreeMap::new());
let value = json!({ "name": "Alice", "age": 30 });
pcd.insert_serializable("profile".to_string(), &value)?;
// Récupération
let profile: Value = pcd.get_as_json("profile")?;
```
#### PcdCommitments
```rust
pub struct PcdCommitments(BTreeMap<String, [u8; 32]>);
```
**Génération des commitments:**
```rust
pub fn new(commited_in: &OutPoint, attributes: &Pcd, roles: &Roles) -> Result<Self> {
let mut field2hash = BTreeMap::new();
for (field, value) in attributes.iter() {
let hash = AnkPcdHash::from_pcd_value(value, field.as_bytes(), commited_in);
field2hash.insert(field, hash.to_byte_array());
}
// Ajout du hash des rôles
let roles_hash = AnkPcdHash::from_pcd_value(roles.to_bytes()?, b"roles", commited_in);
field2hash.insert("roles".to_string(), roles_hash);
Ok(Self(field2hash))
}
```
**Propriétés:**
- **Déterministe** : BTreeMap garantit l'ordre
- **Merkle Tree** : Racine utilisée comme `state_id`
- **Prouvable** : Chemin Merkle pour prouver un champ
### 6.5 Chiffrement des données
**Algorithme:** AES-256-GCM
```rust
use sdk_common::crypto::{Aes256Gcm, KeyInit, AeadCore};
// Génération de clé
let key = Aes256Gcm::generate_key(&mut rng);
// Chiffrement
let cipher = Aes256Gcm::new(&key);
let nonce = Aes256Gcm::generate_nonce(&mut rng);
let ciphertext = cipher.encrypt(&nonce, plaintext.as_ref())?;
// Stockage de la clé dans ProcessState.keys
state.keys.insert(field_name, key.as_slice().try_into()?);
```
**Distribution des clés:**
1. **Option 1:** Via `sdk_storage` (URL dans `RoleDefinition.storages`)
2. **Option 2:** Chiffrement asymétrique avec clés publiques Silent Payment
3. **Option 3:** Partage hors-bande
---
## 7. Flux de données
### 7.1 Flux de création de processus
```
┌─────────────┐
│ Client A │
└──────┬──────┘
│ 1. Génère ProcessState initial
│ ├─ private_data (chiffré)
│ ├─ public_data
│ └─ roles
┌──────────────────────┐
│ Génération state_id │
│ (Merkle root) │
└──────┬───────────────┘
│ 2. Signature avec spend_key
│ proof = sign(state_id)
┌─────────────────────────┐
│ Envoi CommitMessage │
│ → sdk_relay (WebSocket) │
└──────┬──────────────────┘
│ 3. Broadcast aux autres clients
┌──────────────┐ ┌──────────────┐
│ Client B │ │ Client C │
└──────┬───────┘ └──────┬───────┘
│ 4. Validation │
│ is_valid()? │
│ 5. Signature │
│ proof = sign() │
▼ ▼
┌──────────────────────────────────┐
│ Accumulation des signatures │
│ dans CommitMessage │
└──────┬───────────────────────────┘
│ 6. Renvoi à sdk_relay (complet)
┌────────────────────────────────┐
│ sdk_relay vérifie quorum │
│ ├─ is_valid() │
│ └─ Toutes signatures valides │
└──────┬─────────────────────────┘
│ 7. Création transaction Bitcoin
│ ├─ Input: fonds du relay
│ ├─ Output 0: UTXO process (546 sats)
│ └─ Output 1: OP_RETURN (state_id)
┌──────────────────────┐
│ Broadcast Bitcoin │
└──────┬───────────────┘
│ 8. Confirmation
┌──────────────────────────┐
│ ZMQ event (rawtx) │
│ → Tous les clients │
│ → Mise à jour Process │
└──────────────────────────┘
```
### 7.2 Flux de mise à jour
```
┌─────────────┐
│ Client A │
└──────┬──────┘
│ 1. Récupère l'état actuel
│ current = process.get_latest_committed_state()
┌────────────────────────┐
│ Modification de champs │
│ ├─ update_value(key) │
│ └─ Nouveau state_id │
└──────┬─────────────────┘
│ 2. Vérification des permissions
│ can_modify = roles.check(member_id, field)
┌─────────────────────────┐
│ Proposition d'état │
│ insert_concurrent_state │
└──────┬──────────────────┘
│ 3. Collecte des signatures
│ (même flux que création)
┌──────────────────────────┐
│ Commit on-chain │
│ ├─ Dépense ancien UTXO │
│ └─ Crée nouveau UTXO │
└──────┬───────────────────┘
│ 4. Confirmation
┌──────────────────────────┐
│ Mise à jour réseau │
│ update_states_tip() │
│ Pruning états concurrents│
└──────────────────────────┘
```
### 7.3 Flux de pairing multi-device
```
Device 1 (Desktop) Device 2 (Mobile) sdk_relay
────────────────── ───────────────── ─────────
│ │ │
│ 1. Création pairing │ │
│ create_pairing_process() │ │
│────────────────────────────────────────────────────►│
│ │ │
│ 2. Broadcast HandshakeMsg │ │
│◄────────────────────────────────────────────────────│
│ │◄───────────────────────│
│ │ │
│ 3. Affichage QR Code │ │
│ (pairing_id + addresses) │ │
│ │ │
│ │ 4. Scan QR Code │
│ │ get_pairing_info() │
│ │ │
│ │ 5. Demande d'ajout │
│ │ pair_device(pairing_id)│
│ │───────────────────────►│
│ │ │
│ 6. Notification nouvelle │ │
│ adresse (CommitMessage) │ │
│◄────────────────────────────────────────────────────│
│ │ │
│ 7. Validation (signature) │ │
│ approve_pairing() │ │
│────────────────────────────────────────────────────►│
│ │ │
│ 8. Commit on-chain │ │
│ (nouveau state pairedAddr.) │ │
│◄────────────────────────────────────────────────────│
│ │◄───────────────────────│
│ │ │
│ 9. Confirmation │ 9. Confirmation │
│ device.pair() │ device.pair() │
│ │ │
```
### 7.4 Flux de synchronisation Silent Payments
```
┌──────────────┐
│ Bitcoin Node │
│ (ZMQ) │
└──────┬───────┘
│ rawtx / hashblock
┌─────────────────────┐
│ sdk_relay │
│ ┌─────────────────┐ │
│ │ Blindbit Filter │ │ ← Filtres BIP158 compacts
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Match Detection │ │
│ └────────┬────────┘ │
└──────────┼──────────┘
│ NewTxMessage (si match)
┌────────────────────────┐
│ Clients │
│ ┌────────────────────┐ │
│ │ Silent Payment │ │
│ │ Scanner │ │
│ └────────┬───────────┘ │
│ │ │
│ ┌────────▼───────────┐ │
│ │ ECDH + Derivation │ │
│ │ calculate_ecdh() │ │
│ └────────┬───────────┘ │
│ │ │
│ ┌────────▼───────────┐ │
│ │ Output Detection │ │
│ │ OwnedOutput │ │
│ └────────┬───────────┘ │
└──────────┼─────────────┘
┌──────────────┐
│ Device Wallet│
│ update_outputs│
└──────────────┘
```
**Détails du scanning:**
```rust
// Fichier: sdk_common/src/device.rs (lignes 65-151)
pub fn update_outputs_with_transaction(
&mut self,
tx: &Transaction,
blockheight: u32,
partial_tweak: PublicKey,
) -> Result<HashMap<OutPoint, OwnedOutput>> {
// 1. Calcul du shared secret
let shared_secret = calculate_ecdh_shared_secret(
&partial_tweak,
&self.sp_wallet.get_sp_client().get_scan_key(),
);
// 2. Extraction des clés publiques P2TR
let mut pubkeys_to_check: HashMap<XOnlyPublicKey, u32> = HashMap::new();
for (vout, output) in tx.output.iter().enumerate() {
if output.script_pubkey.is_p2tr() {
let xonly = XOnlyPublicKey::from_slice(&output.script_pubkey.as_bytes()[2..])?;
pubkeys_to_check.insert(xonly, vout as u32);
}
}
// 3. Scanning avec sp_receiver
let ours = self.sp_wallet.get_sp_client().sp_receiver
.scan_transaction(&shared_secret, pubkeys_to_check.keys().cloned().collect())?;
// 4. Création des OwnedOutput
let mut new_outputs = HashMap::new();
for (label, map) in ours.iter() {
for (key, scalar) in map {
let vout = pubkeys_to_check.get(&key).unwrap();
let outpoint = OutPoint::new(tx.txid(), *vout);
let owned = OwnedOutput {
blockheight: Height::from_consensus(blockheight)?,
tweak: scalar.to_be_bytes(),
amount: tx.output[*vout as usize].value,
script: tx.output[*vout as usize].script_pubkey.to_bytes().try_into()?,
label: label.clone(),
spend_status: OutputSpendStatus::Unspent,
};
new_outputs.insert(outpoint, owned);
}
}
// 5. Mise à jour du wallet
self.sp_wallet.get_mut_outputs().extend(new_outputs.clone());
// 6. Détection des dépenses
for input in tx.input.iter() {
if let Some(prevout) = self.sp_wallet.get_mut_outputs().get_mut(&input.previous_output) {
prevout.spend_status = OutputSpendStatus::Spent(*tx.txid().as_byte_array());
}
}
Ok(new_outputs)
}
```
---
## 8. Sécurité
### 8.1 Modèle de menaces
#### Menaces couvertes:
1. **Usurpation d'identité** : ✅ Signatures Schnorr obligatoires
2. **Modification non autorisée** : ✅ Validation multi-signatures
3. **Replay attacks** : ✅ state_id unique (Merkle root)
4. **Man-in-the-middle** : ✅ Messages signés cryptographiquement
5. **Observation réseau** : ✅ Données chiffrées (AES-256-GCM)
6. **Perte de clés** : ✅ Multi-device pairing
#### Menaces non couvertes:
1. **Compromission du device** : ❌ Clés stockées localement
2. **Collusion des membres** : ⚠️ Dépend du quorum configuré
3. **Attaque 51% Bitcoin** : ❌ Dépendance à la sécurité Bitcoin
4. **DoS sur sdk_relay** : ⚠️ Rate limiting à implémenter
### 8.2 Primitives cryptographiques
#### Courbes elliptiques:
- **secp256k1** : Courbe Bitcoin standard
- **Schnorr signatures** : BIP340
#### Hachage:
- **SHA-256** : Hash principal
- **Tagged hashes** : BIP340 style
```rust
AnkPcdHash::from_pcd_value(data, tag, outpoint)
```
#### Chiffrement symétrique:
- **AES-256-GCM** : AEAD (Authenticated Encryption with Associated Data)
- **Nonce** : 96 bits aléatoires
- **Key derivation** : Potentiellement HKDF (à vérifier)
#### Signatures:
```rust
pub struct Proof {
// Signature Schnorr (64 bytes)
signature: [u8; 64],
// Clé publique (33 bytes compressed)
public_key: PublicKey,
// Message (32 bytes)
message: [u8; 32],
}
```
**Vérification:**
```rust
impl Proof {
pub fn verify(&self) -> Result<()> {
let secp = Secp256k1::verification_only();
let message = Message::from_slice(&self.message)?;
secp.verify_schnorr(&self.signature, &message, &self.public_key.x_only_public_key().0)?;
Ok(())
}
}
```
### 8.3 Isolation des données
#### Niveaux de visibilité:
1. **Privé (PCD)** :
- Données chiffrées
- Seul le commitment on-chain
- Clés distribuées aux membres autorisés
2. **Public (public_data)** :
- Lisibles par tous les participants réseau
- Exemple: `"pairedAddresses"`
3. **On-chain** :
- Uniquement `state_id` (32 bytes)
- Aucune donnée sensible
#### Gestion des clés:
```rust
// Stockage dans ProcessState
pub struct ProcessState {
pub keys: BTreeMap<String, [u8; 32]>, // field_name -> AES key
}
```
**Distribution:**
- **Méthode 1:** Stockage chiffré asymétriquement sur `sdk_storage`
- **Méthode 2:** Envoi via `CipherMessage` (WebSocket)
- **Méthode 3:** Dérivation à partir d'un secret partagé (ECDH)
### 8.4 Protection contre les attaques
#### Rejeu de transactions (Replay):
✅ **Protection:** `state_id` unique (Merkle root inclut `commited_in`)
```rust
let hash = AnkPcdHash::from_pcd_value(data, field_name, outpoint);
// outpoint change à chaque état -> state_id différent
```
#### Double-spending:
✅ **Protection:** Consensus Bitcoin
- Chaque état possède un UTXO unique
- Dépense de l'UTXO = commit atomique
#### Manipulation des états:
✅ **Protection:** Validation multi-signatures + Merkle proofs
```rust
// Impossible de modifier un champ sans:
// 1. Recalculer le Merkle root (state_id)
// 2. Obtenir les signatures des membres autorisés
```
#### Attaque Sybil (création de fausses identités):
✅ **Protection:** Coût du pairing (frais Bitcoin)
- Créer un membre = créer un processus on-chain
- Coût: frais de transaction Bitcoin
#### Censure par le relay:
⚠️ **Risque résiduel:**
- Un relay malveillant peut ne pas diffuser les messages
- **Mitigation:** Multi-relays (connexion à plusieurs relays)
- **Futur:** Gossip protocol P2P
### 8.5 Audit et traçabilité
#### Traçabilité on-chain:
```
Process Timeline (on Bitcoin):
Txid 1 (création) → OutPoint 1 → OP_RETURN (state_id_1)
Txid 2 (update) → OutPoint 2 → OP_RETURN (state_id_2)
Txid 3 (update) → OutPoint 3 → OP_RETURN (state_id_3)
...
```
**Vérification:**
1. Récupérer la chaîne de transactions
2. Vérifier la continuité (input.prev_out = prev_state.commited_in)
3. Vérifier les OP_RETURN (state_id)
4. Recalculer les Merkle roots
5. Vérifier les signatures de chaque état
#### Logs off-chain:
**sdk_relay** conserve:
- Historique des messages WebSocket
- États proposés (même non committés)
- Signatures reçues
**Format de log recommandé:**
```json
{
"timestamp": "2025-10-01T12:00:00Z",
"event": "commit_proposal",
"process_id": "abc123:0",
"state_id": "def456...",
"proposer": "sp1qq...",
"validation_tokens": [
{
"public_key": "02abc...",
"signature": "304402...",
"timestamp": "2025-10-01T12:00:05Z"
}
],
"committed": true,
"txid": "xyz789..."
}
```
### 8.6 Bonnes pratiques
#### Pour les développeurs:
1. **Quorum élevé pour actions critiques** : ≥ 0.67 (2/3)
2. **min_sig_member = 1.0** : Tous les devices d'un membre doivent signer
3. **Rôle "apophis"** : Toujours définir pour permettre l'oblitération
4. **Chiffrement systématique** : Données sensibles dans PCD (privé)
5. **Validation côté client** : Avant d'envoyer `CommitMessage`
#### Pour les utilisateurs:
1. **Multi-device** : Pairer au moins 2 devices (backup)
2. **Sauvegarde des clés** : Export régulier du wallet
3. **Vérification des rôles** : Avant de signer un état
4. **Vérification des champs** : Utiliser `get_fields_to_validate_for_member()`
#### Configuration de roles sécurisée:
```rust
// Exemple: Gestion de contrat
let roles = Roles::new(BTreeMap::from([
// Rôle owner: peut tout modifier
("owner".to_string(), RoleDefinition {
members: vec![owner_pairing_id],
validation_rules: vec![
ValidationRule::new(1.0, vec!["roles".to_string()], 1.0)?, // 100% consensus
],
storages: vec![],
}),
// Rôle validator: valide les documents
("validator".to_string(), RoleDefinition {
members: vec![validator1_id, validator2_id],
validation_rules: vec![
ValidationRule::new(0.67, vec!["idCertified".to_string()], 0.5)?, // 2/3 consensus
],
storages: vec!["https://storage.4nkweb.com".to_string()],
}),
// Rôle client: lecture seule + modification de son profil
("client".to_string(), RoleDefinition {
members: vec![client_id],
validation_rules: vec![
ValidationRule::new(1.0, vec!["clientProfile".to_string()], 1.0)?,
],
storages: vec![],
}),
// Rôle apophis: oblitération (owner uniquement)
("apophis".to_string(), RoleDefinition {
members: vec![owner_pairing_id],
validation_rules: vec![
ValidationRule::new(1.0, vec!["".to_string()], 1.0)?,
],
storages: vec![],
}),
]));
```
---
## Conclusion
Le système d'identité et de processus de 4NK repose sur une architecture décentralisée innovante combinant:
1. **Identité cryptographique** : Silent Payments Bitcoin (BIP352)
2. **Multi-device** : Pairing flexible sans compromis de sécurité
3. **Consensus distribué** : Validation multi-signatures avec quorum configurables
4. **Immutabilité** : Commitments Bitcoin pour traçabilité
5. **Confidentialité** : Chiffrement bout-en-bout des données sensibles
Cette architecture permet des cas d'usage variés:
- Gestion de contrats collaboratifs
- Signature électronique multi-parties
- Workflows d'approbation décentralisés
- Identité numérique souveraine
**Points forts:**
- Pas de serveur central de confiance
- Résistance à la censure
- Auditabilité complète
- Interopérabilité Bitcoin native
**Axes d'amélioration:**
- Performance du scanning Silent Payments (optimisation Blindbit)
- Résilience réseau (multi-relays, gossip protocol)
- Gestion des clés (recovery social, hardware wallets)
- UX du pairing (NFC, QR codes dynamiques)
---
**Document généré le 1 octobre 2025**
**Basé sur l'analyse du code source 4NK (sdk_client, sdk_common, sdk_relay, sdk_storage, ihm_client)**