---
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.
