4NK_env/docs/4NK_IDENTITY_AND_PROCESS_SPEC.md
2025-10-02 15:32:58 +00:00

1516 lines
49 KiB
Markdown

# Spécification du Système d'Identité et de Processus 4NK
**Version:** 1.0
**Date:** 1 octobre 2025
**Auteur:** Nicolas Cantu
**Description:** 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 **Silent Payments ([BIP352](https://bips.dev/352/))** pour l'identité et le partage de secrets de chiffrement. Le système permet à plusieurs parties de collaborer sur des processus dont les états successifs sont vérifiables, avec des permissions (lecture, modification) granulaires définies pour différents rôles et utilisateurs.
### 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 dans des transactions Bitcoin
- **Validation distribuée** : Signatures cryptographiques multiples requises pour chaque changement d'état
- **Tout dans le browser** : Connexions WebSocket avec des relais 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
- D'autres adresses Silent Payment peuvent être ajoutées à ce processus
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 (premier commit)
├─ State 1 (update 1)
├─ State 2 (update 2)
├─ ...
└─ State n (dernier = toujours vide)
```
**Règle fondamentale:** Le dernier état est toujours vide, représentant le prochain UTXO à dépenser. Cela permet d'annoncer publiquement et sans équivoque où se trouvera le prochain état valide de notre processus, voir le principe du [single-use seal](https://docs.rgb.info/distributed-computing-concepts/single-use-seals)
### 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 (`commited_in` du 1er `ProcessState`)
2. **`pcd_commitment`** : Map contenant en clé le nom des attributs contenu dans l'état (privés + publics yc `roles`) et en valeur le sha256 de la donnée
- Map `field_name -> hash(compressed_value)` La donnée est compressée avant d'être hashé
- Hash taggé: `AnkPcdHash::from_pcd_value(data, field, outpoint)` Le hash contient en plus de la donnée le nom de l'attribut (deux attributs différents avec la même valeur ont ainsi 2 hash différents) ainsi que l'outpoint (la même valeur sur 2 commitments successifs produira 2 hashs différents aussi)
3. **`state_id`** : Racine de Merkle de tous les commitments
- 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, yc ceux qui ne font pas partie du processus
- 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
- Information publique (Cela permet à n'importe qui de valider un processus même sans en faire partie)
### 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, les adresses sont contenues dans `pairedAddresses`
#### 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 (Plus possible d'ajouter des états supplémentaires)
- **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)
- **Commentaire:** Ce rôle peut être utile si l'une des parties prenantes doit initialiser le processus sans cependant avoir un rôle qui lui donne toutes les autorisations nécessaires. Ce rôle est ensuite ignoré dans les commitments suivants et peut être retiré si nécessaire.
---
## 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` (IDs des processus de pairing) plutôt que des adresses
- Permet de modifier les devices d'un membre sans modifier les rôles dans tous les processus auxquels il prend part
- Résolu via `OutPointMemberMap` au moment de la validation
- **validation_rules** : Définissent quels champs peuvent être modifiés
- Quorum requis (n/total membres dans le rôle)
- Signatures minimales par membre (n/total de devices enregistrés par membre)
- **storages** : Liste d'url 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 la moitié de leurs devices enregistrés
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 signer avec au moins 50% de ses devices pairés
- 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
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)**