1511 lines
47 KiB
Markdown
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)**
|
|
|
|
|