Files
decision/docs/content/dev/6.blockchain-integration.md
Yvv 290548703d Boîtes à outils enrichies : ContextMapper, SocioElection, WorkflowMilestones
- ContextMapper : 4 questions contexte → méthode de décision optimale
  (advice process Laloux, vote inertiel WoT, consentement sociocratique, Smith…)
- SocioElection : guide élection sociocratique 6 étapes + advice process + clarté de rôle
- WorkflowMilestones : 11 jalons de protocole (7 essentiels), durées recommandées, principes Ostrom
- WorkspaceSelector : sélecteur de collectif multi-site dans le header
- SectionLayout : toolbox en USlideover droit sur mobile, sidebar sticky desktop
- Décisions : ContextMapper intégré + guide consentement
- Mandats : SocioElection intégré + cycle de mandat
- Documents : guide inertie 4 niveaux + structure + IPFS
- Protocoles : WorkflowMilestones + protocole élection sociocratique ajouté
- Renommage projet Glibredecision → libreDecision (dossier + sources)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 00:13:08 +01:00

166 lines
4.2 KiB
Markdown

---
title: Integration blockchain
description: Integration Duniter V2, IPFS et ancrage on-chain
---
# Integration blockchain
libreDecision s'integre a la blockchain Duniter V2 pour trois fonctions essentielles :
1. **Authentification** -- Verification de l'identite des membres via signature Ed25519
2. **Donnees WoT** -- Recuperation des tailles WoT, Smith et TechComm pour le calcul des seuils
3. **Ancrage on-chain** -- Archivage immuable des resultats via `system.remark`
## Duniter V2 RPC
La communication avec le noeud Duniter V2 utilise la bibliotheque `substrate-interface` via WebSocket RPC.
### Configuration
```
DUNITER_RPC_URL=wss://gdev.p2p.legal/ws
```
### Requetes principales
#### Taille de la WoT (membres)
```python
from substrateinterface import SubstrateInterface
substrate = SubstrateInterface(url="wss://gdev.p2p.legal/ws")
result = substrate.query(
module="Membership",
storage_function="MembershipCount",
)
wot_size = int(result.value)
```
#### Taille Smith (forgerons)
```python
result = substrate.query(
module="SmithMembers",
storage_function="SmithMembershipCount",
)
smith_size = int(result.value)
```
#### Taille TechComm
```python
result = substrate.query(
module="TechnicalCommittee",
storage_function="Members",
)
techcomm_size = len(result.value) if result.value else 0
```
### Cache blockchain
Pour eviter des appels RPC repetes, les donnees blockchain sont mises en cache dans la table `blockchain_cache` avec une duree d'expiration configurable. La cle de cache est une chaine descriptive (ex: `"wot_size"`, `"smith_size"`), la valeur est stockee en JSONB.
## IPFS (kubo)
Le composant IPFS est un noeud kubo qui sert de stockage distribue pour le Sanctuaire. Chaque document adopte, resultat de vote ou decision finalisee est uploade sur IPFS.
### Configuration
```
IPFS_API_URL=http://localhost:5001
IPFS_GATEWAY_URL=http://localhost:8080
```
### Upload de contenu
```python
import httpx
async with httpx.AsyncClient() as client:
response = await client.post(
f"{IPFS_API_URL}/api/v0/add",
files={"file": ("content.txt", content.encode("utf-8"))},
)
response.raise_for_status()
ipfs_cid = response.json()["Hash"]
```
### Acces au contenu
Le contenu est accessible via la passerelle IPFS :
```
GET http://localhost:8080/ipfs/{cid}
```
## Ancrage on-chain (system.remark)
L'ancrage on-chain consiste a soumettre un extrinsic `system.remark` contenant le hash SHA-256 du contenu archive. Cela cree une preuve immuable et horodatee sur la blockchain Duniter V2.
### Format du remark
```
libredecision:sanctuary:{content_hash_sha256}
```
### Soumission
```python
from substrateinterface import SubstrateInterface, Keypair
substrate = SubstrateInterface(url="wss://gdev.p2p.legal/ws")
call = substrate.compose_call(
call_module="System",
call_function="remark",
call_params={"remark": f"libredecision:sanctuary:{content_hash}"},
)
extrinsic = substrate.create_signed_extrinsic(call=call, keypair=keypair)
receipt = substrate.submit_extrinsic(extrinsic, wait_for_inclusion=True)
tx_hash = receipt.extrinsic_hash
block_number = receipt.block_number
```
### Verification
Pour verifier qu'un contenu a ete ancre, il suffit de :
1. Recalculer le hash SHA-256 du contenu
2. Rechercher le remark correspondant dans la blockchain
3. Verifier que le hash correspond
## Flux complet du Sanctuaire
```
Contenu adopte
|
v
[SHA-256] --> content_hash
|
+---> [IPFS /api/v0/add] --> ipfs_cid
|
+---> [system.remark] --> tx_hash, block_number
|
v
[sanctuary_entries] -- Enregistrement en base avec content_hash, ipfs_cid, chain_tx_hash, chain_block
```
## Authentification Ed25519
Le flux d'authentification utilise un mecanisme challenge-response :
1. Le serveur genere un challenge aleatoire (64 caracteres hexadecimaux)
2. Le client signe le challenge avec sa cle privee Ed25519 (Duniter V2)
3. Le serveur verifie la signature a l'aide de la cle publique derivee de l'adresse SS58
```python
from substrateinterface import Keypair
keypair = Keypair(ss58_address=address)
is_valid = keypair.verify(challenge_bytes, signature_bytes)
```
Cette methode garantit que seul le proprietaire de l'adresse Duniter peut s'authentifier, sans jamais transmettre la cle privee.