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

4.2 KiB

title, description
title description
Integration blockchain 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)

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)

result = substrate.query(
    module="SmithMembers",
    storage_function="SmithMembershipCount",
)
smith_size = int(result.value)

Taille TechComm

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

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

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