SC-19-Ripple-XRP-Python - Protocole Ripple et XRP Ledger

<< Vyper | Bitcoin Scripting >>


Objectifs d’apprentissage

  1. Comprendre l’architecture du XRP Ledger et son consensus UNL
  2. Manipuler des comptes et transactions XRP via xrpl-py
  3. Explorer les trust lines, offers et le DEX integre
  4. Decouvrir les payment channels pour les micropaiements
  5. Comprendre les Hooks comme equivalent smart contract du XRPL

Prerequis

  • Python 3.10+ avec xrpl-py (kernel Jupyter smartcontracts : environnement ou xrpl-py est installe)
  • Connexion internet (acces au Testnet XRP)

Environnement : ce notebook utilise le kernel smartcontracts et l’API asyncio de xrpl-py (AsyncJsonRpcClient, await), seule compatible avec la boucle evenementielle de Jupyter. Le client synchrone JsonRpcClient declenche asyncio.run() cannot be called from a running event loop dans un notebook.

Duree estimee : 50 minutes


1. Introduction au protocole Ripple et au XRP Ledger

Historique

Le XRP Ledger (XRPL) a ete concu en 2012 par Jed McCaleb, Arthur Britto et David Schwartz, avec pour objectif principal de faciliter les paiements transfrontaliers rapides et peu couteux. Contrairement a Bitcoin (2009) et Ethereum (2015), le XRPL n’utilise pas de Proof-of-Work ni de Proof-of-Stake : il repose sur un mécanisme de consensus unique base sur une Unique Node List (UNL).

Consensus UNL vs Proof-of-Work

Propriete Bitcoin (PoW) Ethereum (PoS) XRP Ledger (UNL)
Temps de confirmation ~10 min ~12 sec 3-5 sec
Cout de transaction Variable, souvent eleve Variable (gas) ~0.00001 XRP
Consommation energetique Très elevee Moderee Negligeable
Finalite Probabiliste Probabiliste Déterministe
Decentralisation Milliers de mineurs Milliers de validateurs ~150 validateurs UNL

Le consensus UNL fonctionne par accord iteratif : chaque validateur maintient une liste de validateurs de confiance (la UNL). Un ledger est valide quand 80% ou plus des validateurs de la UNL s’accordent sur le même ensemble de transactions.

Concepts cles du XRPL

Concept Description
Account Adresse avec reserve de base (actuellement 10 XRP)
Trust Line Lien bilateral pour detenir des IOU (devises personnalisees)
Offer Ordre d’achat/vente sur le DEX integre au protocole
Payment Channel Canal off-chain pour micropaiements en XRP
Escrow Verrouillage conditionnel de XRP (time-lock, crypto-condition)
Hook Programme WASM execute lors d’une transaction (smart contract)
# Comparaison des architectures blockchain
architectures = {
    "Bitcoin (2009)": {
        "Consensus": "Proof-of-Work (SHA-256)",
        "Langage SC": "Bitcoin Script (limite)",
        "TPS": "~7",
        "Finalite": "~60 min (6 confirmations)",
    },
    "Ethereum (2015)": {
        "Consensus": "Proof-of-Stake (Casper)",
        "Langage SC": "Solidity / Vyper (Turing-complet)",
        "TPS": "~15-30",
        "Finalite": "~15 min (64 slots)",
    },
    "XRP Ledger (2012)": {
        "Consensus": "UNL (Unique Node List)",
        "Langage SC": "Hooks (WASM) + primitives natives",
        "TPS": "~1500",
        "Finalite": "3-5 sec (deterministe)",
    },
}

print("COMPARAISON DES ARCHITECTURES BLOCKCHAIN")
print("=" * 65)
for chain, props in architectures.items():
    print(f"\n{chain}")
    print("-" * 40)
    for key, val in props.items():
        print(f"  {key:20s} : {val}")
COMPARAISON DES ARCHITECTURES BLOCKCHAIN
=================================================================

Bitcoin (2009)
----------------------------------------
  Consensus            : Proof-of-Work (SHA-256)
  Langage SC           : Bitcoin Script (limite)
  TPS                  : ~7
  Finalite             : ~60 min (6 confirmations)

Ethereum (2015)
----------------------------------------
  Consensus            : Proof-of-Stake (Casper)
  Langage SC           : Solidity / Vyper (Turing-complet)
  TPS                  : ~15-30
  Finalite             : ~15 min (64 slots)

XRP Ledger (2012)
----------------------------------------
  Consensus            : UNL (Unique Node List)
  Langage SC           : Hooks (WASM) + primitives natives
  TPS                  : ~1500
  Finalite             : 3-5 sec (deterministe)

Observation

Le XRPL se distingue par son approche pragmatique : plutot que de chercher la decentralisation maximale, il optimise pour la vitesse et le cout des paiements. Les fonctionnalites de smart contracts (trust lines, offers, escrows) sont natives au protocole plutot qu’executees dans une machine virtuelle generique.

Cette approche a des avantages (performance, fiabilite) et des inconvenients (flexibilite limitee comparee a Ethereum).


2. Installation et connexion au Testnet

La bibliotheque xrpl-py est le SDK Python officiel pour interagir avec le XRP Ledger. Le Testnet est un reseau de test gratuit ou l’on peut obtenir des XRP fictifs via un faucet (robinet) pour experimenter sans risque.

# Verification de l'installation de xrpl-py
try:
    import xrpl
    from xrpl.asyncio.clients import AsyncJsonRpcClient
    from xrpl.asyncio.wallet import generate_faucet_wallet
    from xrpl.asyncio.transaction import submit_and_wait
    from xrpl.models.requests import AccountInfo, Ledger
    from importlib.metadata import version as pkg_version
    import sys
    print(f"xrpl-py version : {pkg_version('xrpl-py')}")
    print(f"Python         : {sys.version.split()[0]}")
    print("Installation OK - API asyncio chargee (compatible boucle Jupyter)")
except ImportError:
    print("xrpl-py n'est pas installe dans le kernel smartcontracts.")
    print("Installation : pip install xrpl-py")
    print("Les cellules suivantes ne fonctionneront pas sans cette bibliotheque.")
xrpl-py version : 4.5.0
Python         : 3.13.3
Installation OK - API asyncio chargee (compatible boucle Jupyter)

Connexion au testnet XRP Ledger via un client JSON-RPC pour interagir avec le réseau de test.

# Connexion au Testnet XRP - API asyncio native
import xrpl
from xrpl.asyncio.clients import AsyncJsonRpcClient
from xrpl.models.requests import Ledger

TESTNET_URL = "https://s.altnet.rippletest.net:51234"

# AsyncJsonRpcClient s'integre a la boucle evenementielle de Jupyter :
# chaque appel est un simple `await`, sans asyncio.run() interdit.
client = AsyncJsonRpcClient(TESTNET_URL)

response = await client.request(Ledger(ledger_index="validated"))
ledger_index = response.result.get("ledger_index", "inconnu")
close_time = response.result.get("ledger", {}).get("close_time_human", "inconnu")

print("Connexion au Testnet XRP reussie")
print(f"  URL           : {TESTNET_URL}")
print(f"  Ledger valide : #{ledger_index}")
print(f"  Heure         : {close_time}")
Connexion au Testnet XRP reussie
  URL           : https://s.altnet.rippletest.net:51234
  Ledger valide : #20235788
  Heure         : 2026-Aug-26 12:42:52.000000000 UTC

Interpretation : connexion au Testnet XRP

Resultat obtenu : la lecture du dernier ledger valide reussit en direct - le numero de ledger et son horodatage ci-dessus viennent du reseau Testnet au moment de l’execution.

Le piege asyncio de xrpl-py : le client synchrone JsonRpcClient encapsule chaque requete dans asyncio.run(), ce qui leve asyncio.run() cannot be called from a running event loop sous Jupyter (le notebook possede deja sa boucle). Le notebook bascule alors entierement en mode simule sans erreur visible - un defaut silencieux typique. La solution n’est pas de contourner (threads, nest_asyncio) mais d’utiliser l’API officielle asynchrone : AsyncJsonRpcClient + await client.request(...), native dans xrpl-py.

Information Exemple Source
ledger_index Numero entier Compteur sequentiel des ledgers
close_time_human Date et heure Horodatage de fermeture du ledger
URL Testnet s.altnet.rippletest.net Point d’acces au reseau de test

Note technique : Le XRPL utilise un modele JSON-RPC (et WebSocket) plutot que REST. Chaque appel au client envoie une requete JSON structuree et recoit une reponse JSON.”

Création d’un wallet de test via le faucet du testnet XRP pour obtenir des fonds de développement.

# Creation de wallets de test via le faucet - sans jamais exposer la seed
from xrpl.asyncio.wallet import generate_faucet_wallet
from xrpl.models.requests import AccountInfo

print("Demande de XRP au faucet Testnet (10-20 s par wallet)...\n")

# debug=False est indispensable : debug=True imprimerait la seed dans les outputs.
wallet_1 = await generate_faucet_wallet(client, debug=False)
wallet_2 = await generate_faucet_wallet(client, debug=False)

print("WALLET 1 (Alice)")
print(f"  Adresse classique : {wallet_1.address}")
print(f"  Cle publique      : {wallet_1.public_key[:32]}...")
print("  Seed              : jamais affichee (secret de signature)")
print()
print("WALLET 2 (Bob)")
print(f"  Adresse classique : {wallet_2.address}")
print(f"  Cle publique      : {wallet_2.public_key[:32]}...")
print("  Seed              : jamais affichee (secret de signature)")
print()

# Consulter les soldes reels
for label, w in (("Alice", wallet_1), ("Bob", wallet_2)):
    acct_info = await client.request(AccountInfo(
        account=w.address, ledger_index="validated"))
    balance_drops = int(acct_info.result["account_data"]["Balance"])
    print(f"Solde {label} : {balance_drops / 1_000_000:.6f} XRP ({balance_drops:,} drops)")
print("  (1 XRP = 1,000,000 drops)")
Demande de XRP au faucet Testnet (10-20 s par wallet)...

WALLET 1 (Alice)
  Adresse classique : rsV9uq1DVWrHWbx66jhkeyxa5jKfgmErg9
  Cle publique      : ED4BCB4D0E298410ECB3E4D6FC7A3EC5...
  Seed              : jamais affichee (secret de signature)

WALLET 2 (Bob)
  Adresse classique : rnAwyYV3R7CShvr4hVuBwHSurRbtWpzRSX
  Cle publique      : ED7DCA1B2D6E2A4DDABFF6E64524E308...
  Seed              : jamais affichee (secret de signature)

Solde Alice : 100.000000 XRP (100,000,000 drops)
Solde Bob : 100.000000 XRP (100,000,000 drops)
  (1 XRP = 1,000,000 drops)

Interpretation : creation de wallets et reserves

Resultat obtenu : deux wallets finances par le faucet, avec soldes verifies en direct. La seed existe en memoire (elle signe les transactions des cellules suivantes) mais n’est jamais imprimee ni persistee : exposer la seed d’un wallet - meme de test - dans un output committe revient a publier sa cle privee.

Element Format Utilite
Adresse r... (25-35 caracteres) Identifiant public du compte
Cle publique Hex (64 octets) Verification des signatures
Seed s... (cle privee) Signature des transactions - secret, jamais affichee

Points cles : - La reserve de base est un mecanisme anti-spam : chaque objet supplementaire (trust line, offer, channel) consomme de la reserve - 1 XRP = 1 000 000 drops. Le XRPL utilise les drops comme unite atomique dans toutes les operations - Regle de securite : la seed ne quitte jamais la memoire du process. En production, elle vit dans un wallet hardware ou un gestionnaire de secrets.

Le faucet Testnet distribue des XRP gratuits pour les tests. Chaque compte XRP necessite une reserve de base (actuellement 10 XRP sur le mainnet) pour exister. Cette reserve empeche la creation massive de comptes spam.

Anatomie d’une adresse XRP : - Commence par r (Base58Check, similaire a Bitcoin) - 25-34 caractères - Derivee de la cle publique Ed25519 ou secp256k1


3. Transactions XRP

Le XRPL supporte nativement plusieurs types de transactions, sans avoir besoin de smart contracts :

Type Description
Payment Transfert de XRP ou d’IOU
TrustSet Etablir une trust line (autoriser un IOU)
OfferCreate Placer un ordre sur le DEX integre
OfferCancel Annuler un ordre DEX
EscrowCreate Verrouiller des XRP conditionnellement
PaymentChannelCreate Ouvrir un canal de micropaiement

Chaque transaction est signee par la cle privee de l’expediteur et validee par le consensus en 3-5 secondes.

# Envoi d'un paiement XRP - transaction Testnet reelle
from xrpl.asyncio.transaction import submit_and_wait as submit_and_wait_async
from xrpl.models.transactions import Payment
from xrpl.utils import xrp_to_drops

# Paiement de 25 XRP d'Alice vers Bob
payment_tx = Payment(
    account=wallet_1.address,
    destination=wallet_2.address,
    amount=xrp_to_drops(25),  # 25 XRP en drops
)

print("ENVOI DE PAIEMENT XRP")
print("=" * 60)
print(f"De      : {wallet_1.address}")
print(f"Vers    : {wallet_2.address}")
print(f"Montant : 25 XRP ({xrp_to_drops(25)} drops)")
print("\nSoumission en cours...")

response = await submit_and_wait_async(payment_tx, client, wallet_1)
result = response.result

tx_hash = result.get("hash", "inconnu")
status = result.get("meta", {}).get("TransactionResult", "inconnu")
# Frais reels, mesures par difference de solde avant/apres paiement :
# le solde d'Alice ne baisse que du montant envoye + des frais.
acct_after = await client.request(AccountInfo(
    account=wallet_1.address, ledger_index="validated"))
balance_after = int(acct_after.result["account_data"]["Balance"])
fee_drops = 100_000_000 - 25_000_000 - balance_after

print(f"\nResultat :")
print(f"  Hash        : {tx_hash}")
print(f"  Statut      : {status}")
print(f"  Frais       : {fee_drops} drops ({fee_drops / 1_000_000:.6f} XRP), mesures par solde avant/apres")
print(f"  Solde Alice : 100.000000 -> {balance_after / 1_000_000:.6f} XRP")
print(f"  Ledger      : {result.get('ledger_index', 'inconnu')}")

if status == "tesSUCCESS":
    print("\n-> Transaction validee par le consensus Testnet en quelques secondes")
else:
    print(f"\n-> Resultat inattendu : {status}")
ENVOI DE PAIEMENT XRP
============================================================
De      : rsV9uq1DVWrHWbx66jhkeyxa5jKfgmErg9
Vers    : rnAwyYV3R7CShvr4hVuBwHSurRbtWpzRSX
Montant : 25 XRP (25000000 drops)

Soumission en cours...

Resultat :
  Hash        : 4E1FF3141312183FC34E8C9284B109A6653705CD0CE9C4571A8E65DED4A0D4A9
  Statut      : tesSUCCESS
  Frais       : 10 drops (0.000010 XRP), mesures par solde avant/apres
  Solde Alice : 100.000000 -> 74.999990 XRP
  Ledger      : 20235798

-> Transaction validee par le consensus Testnet en quelques secondes

Interpretation : transaction de paiement XRP

Resultat obtenu : transaction validee en direct sur le Testnet - le hash ci-dessus est verifiable dans l’explorateur (testnet.xrpl.org), le code tesSUCCESS et le numero de ledger sont ceux du reseau au moment de l’execution.

Champ Valeur typique Signification
hash 64 caracteres hex Identifiant unique de la transaction
TransactionResult tesSUCCESS Code de resultat (tes = Transaction Engine Success)
Fee ~12 drops (~0.000012 XRP) Frais de transaction
ledger_index Numero du ledger Bloc dans lequel la tx est incluse

Points cles : - Le code tesSUCCESS signifie une validation reussie. Le prefixe tes est un des prefixes de codes du XRPL (tec, tef, tel, tem pour les differents types d’echec) - Les frais sont fixes et ne dependent pas de la congestion du reseau - a contraster avec le gas d’Ethereum - La finalite est deterministe : 3-5 secondes, pas de reorganisation possible comme sur Ethereum

Création d’une Trust Line pour accepter un IOU (dette émise) d’un autre compte sur le XRP Ledger.

# Creation d'une Trust Line pour un IOU (devise personnalisee) - transaction reelle
from xrpl.models.transactions import TrustSet
from xrpl.models.amounts import IssuedCurrencyAmount

# Bob autorise Alice a emettre jusqu'a 1000 EUR (IOU)
trust_set_tx = TrustSet(
    account=wallet_2.address,
    limit_amount=IssuedCurrencyAmount(
        currency="EUR",
        issuer=wallet_1.address,
        value="1000",
    ),
)

print("CREATION D'UNE TRUST LINE")
print("=" * 60)
print("Bob autorise Alice a emettre jusqu'a 1000 EUR")
print(f"  Compte      : {wallet_2.address} (Bob)")
print(f"  Emetteur    : {wallet_1.address} (Alice)")
print(f"  Devise      : EUR")
print(f"  Limite      : 1000")
print("\nSoumission en cours...")

response = await submit_and_wait_async(trust_set_tx, client, wallet_2)
status = response.result.get("meta", {}).get("TransactionResult", "inconnu")

print(f"  Hash   : {response.result.get('hash', 'inconnu')}")
print(f"  Statut : {status}")

if status == "tesSUCCESS":
    print("\n-> Trust line creee sur le ledger. Bob peut maintenant recevoir des EUR d'Alice.")
    print("   Les trust lines sont la base du systeme de credit du XRPL.")
CREATION D'UNE TRUST LINE
============================================================
Bob autorise Alice a emettre jusqu'a 1000 EUR
  Compte      : rnAwyYV3R7CShvr4hVuBwHSurRbtWpzRSX (Bob)
  Emetteur    : rsV9uq1DVWrHWbx66jhkeyxa5jKfgmErg9 (Alice)
  Devise      : EUR
  Limite      : 1000

Soumission en cours...
  Hash   : 96C839AB3EF1CE83A25591F6DAEC7DCF601307EDAF3914414F39C467B4A62FEF
  Statut : tesSUCCESS

-> Trust line creee sur le ledger. Bob peut maintenant recevoir des EUR d'Alice.
   Les trust lines sont la base du systeme de credit du XRPL.

Interpretation : Trust Lines et systeme de credit

Resultat obtenu : la Trust Line de Bob vers Alice (limite 1000 EUR) est reellement inscrite au ledger - le hash et le statut tesSUCCESS ci-dessus proviennent de la transaction Testnet validee.

Propriete Valeur Role
Compte Bob Celui qui accorde sa confiance
Emetteur Alice Celui qui peut emettre l’IOU
Devise EUR Code devise ISO 4217 (ou libre)
Limite 1000 Plafond de confiance de Bob

Points cles : - Sans Trust Line, il est impossible de recevoir un IOU d’un emetteur donne - La limite protege Bob : Alice ne peut pas lui envoyer plus de 1000 EUR sans son consentement - Les Trust Lines sont bilaterales : Bob autorise Alice, mais Alice doit aussi autoriser Bob pour un flux bidirectionnel - Ce mecanisme remplace les smart contracts ERC-20 d’Ethereum : pas de code a deployer pour creer un token

Création d’un Offer sur le DEX intégré du XRP Ledger pour échanger des devises de manière décentralisée.

# Creation d'un Offer sur le DEX integre - transaction reelle
from xrpl.models.transactions import OfferCreate

# Alice propose de vendre 50 XRP contre 45 EUR
offer_tx = OfferCreate(
    account=wallet_1.address,
    taker_pays=xrp_to_drops(50),      # Ce que l'acheteur paie : 50 XRP
    taker_gets=IssuedCurrencyAmount(   # Ce que l'acheteur recoit : 45 EUR
        currency="EUR",
        issuer=wallet_1.address,
        value="45",
    ),
)

print("CREATION D'UN OFFER (DEX INTEGRE)")
print("=" * 60)
print("Le XRPL possede un DEX (Decentralized Exchange) natif.")
print("Pas besoin de smart contract comme Uniswap sur Ethereum.\n")
print("Alice propose :")
print("  Vend  : 45 EUR (IOU)")
print("  Recoit: 50 XRP")
print("  Taux  : 1 EUR = 1.11 XRP\n")
print("Soumission en cours...")

response = await submit_and_wait_async(offer_tx, client, wallet_1)
status = response.result.get("meta", {}).get("TransactionResult", "inconnu")
print(f"  Hash   : {response.result.get('hash', 'inconnu')}")
print(f"  Statut : {status}")

if status == "tesSUCCESS":
    print("\n-> Offer placee sur le carnet d'ordres du XRPL")
    print("   Elle sera executee automatiquement si un offer correspondant apparait.")
CREATION D'UN OFFER (DEX INTEGRE)
============================================================
Le XRPL possede un DEX (Decentralized Exchange) natif.
Pas besoin de smart contract comme Uniswap sur Ethereum.

Alice propose :
  Vend  : 45 EUR (IOU)
  Recoit: 50 XRP
  Taux  : 1 EUR = 1.11 XRP

Soumission en cours...
  Hash   : B5F36C3F9BC240F4D0DF063059CD91AD62C404A7AF84A58472CCC275A5E793B6
  Statut : tesSUCCESS

-> Offer placee sur le carnet d'ordres du XRPL
   Elle sera executee automatiquement si un offer correspondant apparait.

Interpretation : DEX integre au protocole

Resultat obtenu : l’offre de vente de 45 EUR contre 50 XRP est reellement placee sur le carnet d’ordres Testnet (hash et tesSUCCESS ci-dessus). Comme aucun ordre correspondant n’existe, elle reste en attente - c’est le comportement attendu d’un carnet d’ordres.

Element Valeur Explication
taker_pays 50 XRP Ce que l’acheteur paie
taker_gets 45 EUR (IOU) Ce que l’acheteur recoit
Taux implicite 1 EUR = 1.11 XRP Ratio taker_pays / taker_gets
Matching Automatique Le protocole execute si un offer inverse existe

Note technique : L’auto-bridging est une fonctionnalite unique du XRPL. Si Alice veut vendre des EUR contre des USD et qu’un chemin optimal passe par XRP, le protocole effectue automatiquement la conversion EUR -> XRP -> USD sans intervention de l’utilisateur.

Les trois types de transactions ci-dessus illustrent la philosophie du XRPL : les fonctionnalites financieres sont integrees au protocole, pas ajoutees via des smart contracts.

Fonctionnalite Ethereum XRP Ledger
Transfert de tokens Smart contract ERC-20 Trust line + Payment natif
Echange decentralise Uniswap (smart contract) DEX natif (OfferCreate)
Depot conditionnel Smart contract Escrow EscrowCreate natif
Micropaiements State channels (complexe) PaymentChannel natif

Avantage : fiabilite, performance, cout minimal. Inconvenient : impossible d’inventer de nouvelles primitives sans modifier le protocole (avant l’arrivee des Hooks, cf. section 5).


3b. Exercice : Analyseur de transactions XRP

Objectif

Implementez une fonction analyze_xrp_transaction qui analyse une transaction XRP brute (sous forme de dictionnaire) et en extrait les informations cles : type de transaction, parties impliquees, montants et frais.

Contexte

Chaque transaction XRP contient des champs standards (Account, Fee, Sequence) et des champs spécifiques au type (Amount, Destination pour un paiement, LimitAmount pour une trust line, etc.). Un analyseur doit extraire et formater ces informations de maniere lisible.

Indice :

  • Utilisez des if/elif sur le champ TransactionType pour distinguer les types
  • Pour un Payment, les champs cles sont Account, Destination, Amount, Fee
  • Pour un TrustSet, le champ est LimitAmount avec les sous-champs currency, issuer, value
  • Retournez un dictionnaire structure avec un resume human-readable
def analyze_xrp_transaction(tx):
    """Analyser une transaction XRP et extraire un resume lisible.

    TODO etudiant : implementez l'analyse pour les types suivants :
    - Payment : extraire Account, Destination, Amount, Fee
    - TrustSet : extraire Account, LimitAmount (currency, issuer, value)
    - OfferCreate : extraire Account, TakerPays, TakerGets

    Args:
        tx: dictionnaire representant une transaction XRP brute
            (contient au minimum 'TransactionType', 'Account', 'Fee')

    Returns:
        dict avec les cles 'type', 'summary', 'fee_drops', 'details'
    """
    # TODO etudiant : implementez l'analyse
    return {"type": None, "summary": None, "fee_drops": None, "details": {}}


# Test avec des transactions simulees
sample_payment = {
    "TransactionType": "Payment",
    "Account": "rAlice...",
    "Destination": "rBob...",
    "Amount": "25000000",  # 25 XRP en drops
    "Fee": "12",
    "Sequence": 1,
}

sample_trustset = {
    "TransactionType": "TrustSet",
    "Account": "rBob...",
    "LimitAmount": {"currency": "EUR", "issuer": "rAlice...", "value": "1000"},
    "Fee": "12",
    "Sequence": 2,
}

print("Exercice a completer : implementez analyze_xrp_transaction")
print(f"  Transaction test 1 : {sample_payment['TransactionType']}")
print(f"  Transaction test 2 : {sample_trustset['TransactionType']}")
Exercice a completer : implementez analyze_xrp_transaction
  Transaction test 1 : Payment
  Transaction test 2 : TrustSet

4. Payment Channels (micropaiements)

Les Payment Channels permettent des flux de micropaiements off-chain entre deux parties, avec un reglement final on-chain.

Principe

1. Alice ouvre un canal : verrouille 100 XRP on-chain
2. Alice envoie des "claims" signes a Bob (off-chain, instantane)
   - Claim 1 : Bob peut reclamer 1 XRP
   - Claim 2 : Bob peut reclamer 2 XRP (remplace claim 1)
   - Claim 3 : Bob peut reclamer 3 XRP (remplace claim 2)
   - ...
3. Bob soumet le dernier claim on-chain pour recevoir ses XRP
4. Le canal est ferme, Alice recupere le reste

Cas d’usage : streaming de paiements (payer a la seconde pour un service), microtransactions (IoT, API pay-per-call), et tout scénario ou les frais de transaction on-chain seraient prohibitifs.

# Demonstration conceptuelle des Payment Channels
# (La creation reelle necessite un canal ouvert sur le ledger)

import hashlib
import json
import time

print("PAYMENT CHANNEL - SIMULATION CONCEPTUELLE")
print("=" * 60)

# Etape 1 : Ouverture du canal
channel_params = {
    "source": "rAlice...",
    "destination": "rBob...",
    "amount_xrp": 100,
    "settle_delay_seconds": 3600,  # 1 heure pour contester
}

print("1. OUVERTURE DU CANAL")
print(f"   Source      : {channel_params['source']}")
print(f"   Destination : {channel_params['destination']}")
print(f"   Reserve     : {channel_params['amount_xrp']} XRP")
print(f"   Delai       : {channel_params['settle_delay_seconds']}s")
print()

# Etape 2 : Flux de claims off-chain
print("2. FLUX DE CLAIMS OFF-CHAIN")
print("-" * 40)

cumulative_amount = 0
claims = []
for i in range(1, 11):
    cumulative_amount += 0.5  # 0.5 XRP par increment
    claim = {
        "channel_id": "ABC123...",
        "amount_drops": int(cumulative_amount * 1_000_000),
        "signature": hashlib.sha256(
            f"claim:{cumulative_amount}".encode()
        ).hexdigest()[:16] + "...",
    }
    claims.append(claim)
    print(f"   Claim {i:2d} : {cumulative_amount:.1f} XRP"
          f"  (sig: {claim['signature'][:12]}...)")

print()

# Etape 3 : Reglement final
last_claim = claims[-1]
remaining = channel_params["amount_xrp"] - cumulative_amount

print("3. REGLEMENT FINAL ON-CHAIN")
print(f"   Bob soumet le dernier claim : {cumulative_amount:.1f} XRP")
print(f"   Bob recoit                  : {cumulative_amount:.1f} XRP")
print(f"   Alice recupere le reste     : {remaining:.1f} XRP")
print(f"   Total = {cumulative_amount + remaining:.1f} XRP (integre)")
print()
print("-> 10 transferts realises pour le cout d'1 seule transaction on-chain")
print("-> Latence quasi-nulle pour les claims off-chain")
PAYMENT CHANNEL - SIMULATION CONCEPTUELLE
============================================================
1. OUVERTURE DU CANAL
   Source      : rAlice...
   Destination : rBob...
   Reserve     : 100 XRP
   Delai       : 3600s

2. FLUX DE CLAIMS OFF-CHAIN
----------------------------------------
   Claim  1 : 0.5 XRP  (sig: 32ba101f6d7d...)
   Claim  2 : 1.0 XRP  (sig: 1e3baf0a3b58...)
   Claim  3 : 1.5 XRP  (sig: 9957d0d4212a...)
   Claim  4 : 2.0 XRP  (sig: 5d96607fa8ea...)
   Claim  5 : 2.5 XRP  (sig: 8f86e267d0ab...)
   Claim  6 : 3.0 XRP  (sig: 5cd139a825bc...)
   Claim  7 : 3.5 XRP  (sig: 1ab175b5374d...)
   Claim  8 : 4.0 XRP  (sig: 44e2386e70cf...)
   Claim  9 : 4.5 XRP  (sig: b0d46f517113...)
   Claim 10 : 5.0 XRP  (sig: b5d920a7f36a...)

3. REGLEMENT FINAL ON-CHAIN
   Bob soumet le dernier claim : 5.0 XRP
   Bob recoit                  : 5.0 XRP
   Alice recupere le reste     : 95.0 XRP
   Total = 100.0 XRP (integre)

-> 10 transferts realises pour le cout d'1 seule transaction on-chain
-> Latence quasi-nulle pour les claims off-chain

Interpretation : simulation d’un Payment Channel

Résultat obtenu : 10 claims cumulatifs sont generes off-chain, passant de 0.5 a 5.0 XRP. Seul le dernier claim sera soumis on-chain.

Étape Action On-chain ? Cout
Ouverture du canal Verrouillage de 100 XRP Oui 1 transaction
Claims 1 a 10 Envoi de 0.5 XRP chacun Non 0
Reglement final Soumission du dernier claim Oui 1 transaction

Points cles : - Le rapport cout/transfert est de 2 transactions pour 10 paiements, soit un cout effectif de ~0.0000024 XRP par paiement : 2 transactions x 12 drops = 24 drops = 0.000024 XRP au total, reparti sur 10 paiements (vs 0.000012 XRP par paiement, soit 10 x 12 drops = 0.00012 XRP au total, si chaque paiement etait on-chain) - L’avantage principal n’est pas le cout mais la latence : les claims off-chain sont instantanes - L’integrite est garantie : 5.0 + 95.0 = 100.0 XRP (conservation parfaite)

Ouverture réelle d’un Payment Channel sur le testnet XRP via la transaction PaymentChannelCreate.

# Ouverture d'un Payment Channel reelle - transaction Testnet
from xrpl.models.transactions import PaymentChannelCreate

# Ouverture d'un canal de 20 XRP
channel_tx = PaymentChannelCreate(
    account=wallet_1.address,
    destination=wallet_2.address,
    amount=xrp_to_drops(20),
    settle_delay=3600,           # 1 heure de delai de reglement
    public_key=wallet_1.public_key,
)

print("OUVERTURE D'UN PAYMENT CHANNEL REEL")
print("=" * 60)
print(f"  De      : {wallet_1.address}")
print(f"  Vers    : {wallet_2.address}")
print(f"  Reserve : 20 XRP")
print(f"  Delai   : 3600 secondes\n")
print("Soumission en cours...")

response = await submit_and_wait_async(channel_tx, client, wallet_1)
status = response.result.get("meta", {}).get("TransactionResult", "inconnu")
print(f"  Hash   : {response.result.get('hash', 'inconnu')}")
print(f"  Statut : {status}")

if status == "tesSUCCESS":
    # Extraire le channel ID des metadonnees
    meta = response.result.get("meta", {})
    affected = meta.get("AffectedNodes", [])
    channel_id = None
    for node in affected:
        created = node.get("CreatedNode", {})
        if created.get("LedgerEntryType") == "PayChannel":
            channel_id = created.get("LedgerIndex")
            break
    if channel_id:
        print(f"  Channel ID : {channel_id}")
    print("\n-> Canal ouvert sur le ledger. Les claims off-chain peuvent commencer.")
OUVERTURE D'UN PAYMENT CHANNEL REEL
============================================================
  De      : rsV9uq1DVWrHWbx66jhkeyxa5jKfgmErg9
  Vers    : rnAwyYV3R7CShvr4hVuBwHSurRbtWpzRSX
  Reserve : 20 XRP
  Delai   : 3600 secondes

Soumission en cours...
  Hash   : 561A526FB4B1B0E5CCB956CB6E5F560B117A43D7776AD4D5F0AAA4528FD392F4
  Statut : tesSUCCESS
  Channel ID : 8F9AACFF78829EA1552E00458707AB983C85039027B27B16492CF573287A1419

-> Canal ouvert sur le ledger. Les claims off-chain peuvent commencer.

Interpretation : ouverture d’un Payment Channel reel

Resultat obtenu : le canal est reellement ouvert sur le Testnet - le channel_id ci-dessus est l’identifiant du ledger entry PayChannel cree par la transaction (extrait des AffectedNodes de la reponse validee).

Parametre Valeur Signification
amount 20 XRP Reserve bloquee on-chain
settle_delay 3600s Delai de contestation apres fermeture
public_key cle Alice Authentification des claims

Note technique : Le settle_delay est crucial pour la securite. Si Alice veut fermer le canal, elle doit attendre 3600 secondes pendant lesquelles Bob peut soumettre son dernier claim. Cela empeche Alice de fermer le canal brutalement avant que Bob n’ait recu ses fonds.

Les Payment Channels du XRPL sont une solution native aux micropaiements :

Aspect On-chain classique Payment Channel
Cout par transfert ~0.000012 XRP 0 (off-chain)
Latence 3-5 secondes Instantane
Debit ~1500 TPS Illimite (off-chain)
Securite Consensus complet Signature cryptographique

Bob est protege car il peut soumettre le dernier claim a tout moment. Alice est protegee par le delai de reglement (settle_delay) qui empeche Bob de reclamer des fonds après la fermeture.


4b. Exercice : Validateur de claims pour Payment Channel

Objectif

Implementez une fonction validate_claim_chain qui valide une sequence de claims off-chain pour un Payment Channel. La fonction doit verifier l’integrite de la chaîne de claims : montants strictement croissants et signatures valides.

Contexte

Dans un Payment Channel, Bob recoit une serie de claims signes par Alice. Chaque claim remplace le précédent : seul le dernier compte. Bob doit pouvoir verifier que les claims sont valides (montant croissant, signature correcte) avant de soumettre le dernier on-chain.

Indice :

  • Parcourez la liste de claims et verifiez que chaque montant est > au précédent
  • Verifiez que chaque signature correspond au montant (hash SHA-256 du montant)
  • Retournez le montant total valide (dernier claim) et le nombre de claims valides
  • Utilisez hashlib.sha256 pour verifier les signatures simulees
import hashlib


def generate_claim(channel_id, cumulative_drops, sender_key):
    """Generer un claim simule (fourni, ne pas modifier)."""
    msg = f"{channel_id}:{cumulative_drops}"
    sig = hashlib.sha256((msg + sender_key).encode()).hexdigest()[:16]
    return {"channel_id": channel_id, "amount_drops": cumulative_drops, "signature": sig}


def validate_claim_chain(claims, channel_id, sender_key, max_channel_amount):
    """Valider une chaine de claims pour un Payment Channel.

    TODO etudiant : implementez les verifications :
    1. Les montants sont strictement croissants
    2. Chaque signature correspond a SHA-256(channel_id:amount + sender_key)[:16]
    3. Le dernier montant ne depasse pas max_channel_amount
    4. Tous les channel_id correspondent

    Args:
        claims: liste de dictionnaires (channel_id, amount_drops, signature)
        channel_id: identifiant attendu du canal
        sender_key: cle de l'emetteur pour la verification de signature
        max_channel_amount: montant maximal du canal (en drops)

    Returns:
        dict avec 'valid', 'last_valid_amount', 'num_validated', 'errors'
    """
    # TODO etudiant : implementez la validation
    return {"valid": False, "last_valid_amount": 0, "num_validated": 0, "errors": []}


# Generation de claims de test
test_channel_id = "ABC123..."
test_sender_key = "alice_secret_key"
test_claims = [
    generate_claim(test_channel_id, 500_000, test_sender_key),   # 0.5 XRP
    generate_claim(test_channel_id, 1_000_000, test_sender_key), # 1.0 XRP
    generate_claim(test_channel_id, 1_500_000, test_sender_key), # 1.5 XRP
    generate_claim(test_channel_id, 2_000_000, test_sender_key), # 2.0 XRP
]

print("Exercice a completer : implementez validate_claim_chain")
print(f"  Canal      : {test_channel_id}")
print(f"  Claims     : {len(test_claims)} claims generes")
print(f"  Montant max: 5.0 XRP (5_000_000 drops)")
Exercice a completer : implementez validate_claim_chain
  Canal      : ABC123...
  Claims     : 4 claims generes
  Montant max: 5.0 XRP (5_000_000 drops)

5. Hooks : les smart contracts du XRPL

Les Hooks sont l’equivalent des smart contracts pour le XRP Ledger. Introduits via l’amendement Hooks (en cours de deploiement), ils permettent d’attacher une logique programmable aux comptes du XRPL.

Architecture des Hooks

Propriete Ethereum Smart Contract XRP Hook
Langage Solidity / Vyper C (compile en WASM)
Exécution EVM WebAssembly (WASM)
Declenchement Appel explicite Automatique (pre/post transaction)
Stockage Storage slots (256 bits) Hook State (32 bytes key-value)
Limites Gas limit Instruction count

Declenchement

Un Hook est declenche automatiquement lorsqu’une transaction implique le compte auquel il est attache :

Transaction entrante -> Hook cbak() -> accepter / rejeter
Transaction sortante -> Hook hook() -> modifier / bloquer

Exemple conceptuel de Hook (C)

#include "hookapi.h"

// Hook qui limite les paiements sortants a 100 XRP maximum
int64_t hook(uint32_t reserved) {
    // Lire le montant de la transaction
    int64_t amount = otxn_amount();
    
    // Limite de 100 XRP (en drops)
    int64_t limit = 100000000; // 100 * 1,000,000 drops
    
    if (amount > limit) {
        // Rejeter la transaction
        rollback(SBUF("Payment exceeds 100 XRP limit"), 1);
    }
    
    // Accepter la transaction
    accept(SBUF("Payment approved"), 0);
    return 0;
}

API Hook principale

Fonction Description
hook() Point d’entree pour les transactions sortantes
cbak() Callback pour les transactions entrantes
accept() Approuver la transaction
rollback() Rejeter la transaction
otxn_amount() Montant de la transaction courante
otxn_field() Lire un champ de la transaction
state_set() Ecrire dans le stockage du Hook
state() Lire le stockage du Hook
emit() Emettre une transaction depuis le Hook

Note : Les Hooks sont en cours de deploiement sur le mainnet XRPL. Ils sont disponibles sur le testnet Hooks (hooks-testnet-v3.xrpl-labs.com). La compilation C -> WASM necessite un toolchain spécifique (wasmcc).

# Les Hooks ne sont pas encore deployables via xrpl-py standard.
# Voici une representation structurelle d'un Hook.

print("ANATOMIE D'UN HOOK XRPL")
print("=" * 60)

hook_example = {
    "nom": "Limite de paiement sortant",
    "description": "Rejette tout paiement sortant > 100 XRP",
    "langage": "C -> WebAssembly (WASM)",
    "declenchement": "Automatique sur chaque transaction sortante",
    "fonctions": {
        "hook()": "Verifie le montant, accept/rollback",
        "cbak()": "Non utilise (transactions entrantes)",
    },
    "etat_stocke": {
        "limite": "100 XRP (modifiable par le proprietaire)",
        "compteur_rejets": "Nombre de transactions rejetees",
    },
    "taille_wasm": "~2 Ko",
    "cout_installation": "~2 XRP (reserve d'objet)",
}

for key, val in hook_example.items():
    if isinstance(val, dict):
        print(f"\n{key} :")
        for k, v in val.items():
            print(f"  {k:25s} : {v}")
    else:
        print(f"{key:25s} : {val}")

print()
print("Cycle de vie d'un Hook :")
print("  1. Ecriture en C avec hookapi.h")
print("  2. Compilation en WASM (wasmcc)")
print("  3. Installation via SetHook transaction")
print("  4. Declenchement automatique a chaque transaction")
print("  5. Desinstallation via SetHook (wasm vide)")
ANATOMIE D'UN HOOK XRPL
============================================================
nom                       : Limite de paiement sortant
description               : Rejette tout paiement sortant > 100 XRP
langage                   : C -> WebAssembly (WASM)
declenchement             : Automatique sur chaque transaction sortante

fonctions :
  hook()                    : Verifie le montant, accept/rollback
  cbak()                    : Non utilise (transactions entrantes)

etat_stocke :
  limite                    : 100 XRP (modifiable par le proprietaire)
  compteur_rejets           : Nombre de transactions rejetees
taille_wasm               : ~2 Ko
cout_installation         : ~2 XRP (reserve d'objet)

Cycle de vie d'un Hook :
  1. Ecriture en C avec hookapi.h
  2. Compilation en WASM (wasmcc)
  3. Installation via SetHook transaction
  4. Declenchement automatique a chaque transaction
  5. Desinstallation via SetHook (wasm vide)

Interpretation : anatomie d’un Hook

Résultat obtenu : Le Hook affiche une structure complete avec fonctions hook() et cbak(), un etat persistant et un cycle de vie en 5 étapes.

Composant Rôle Equivalent Ethereum
hook() Intercepte les transactions sortantes Fallback function
cbak() Intercepte les transactions entrantes Event listener
Hook State Stockage key-value (32 bytes) Contract storage
accept() / rollback() Valide ou rejette la transaction revert() / return

Points cles : - Le Hook est compile en WASM (~2 Ko), ce qui le rend déterministe et rapide a executer - Le cout d’installation (~2 XRP) est une reserve, pas un frais perdu - Contrairement a un smart contract Ethereum, un Hook n’a pas d’adresse propre : il est attache au compte existant

Comparaison : Hooks vs Smart Contracts Ethereum

Aspect Smart Contract Ethereum Hook XRPL
Deploiement Adresse propre Attache a un compte existant
Appel Explicite (tx vers le contrat) Implicite (toute tx du compte)
Turing-complet Oui Non (pas de boucles infinies)
Composabilite Appels inter-contrats Limitee (emit)
Maturite Production depuis 2015 En cours de deploiement

Les Hooks ne remplacent pas les smart contracts Ethereum pour les cas d’usage complexes (DeFi avancee, NFT), mais ils couvrent efficacement les scénarios lies aux paiements : limites, compliance, routage automatique.


6. Exercice : scénario de paiement cross-currency

Implementez un scénario complet de paiement cross-currency sur le XRPL Testnet :

  1. Créer 3 wallets (Alice, Bob, Gateway)
  2. Gateway etablit des trust lines pour EUR et USD
  3. Alice et Bob etablissent des trust lines vers Gateway
  4. Gateway emet des EUR a Alice et des USD a Bob
  5. Alice paie Bob en EUR, qui arrive en USD (via le DEX)

Ce scénario illustre le cas d’usage principal du XRPL : le paiement transfrontalier avec conversion automatique de devises.

Transition : de la théorie a la pratique

Les sections précédentes ont couvert les mécanismes fondamentaux du XRPL : consensus UNL, transactions natives, trust lines, DEX, payment channels et Hooks. L’exercice suivant combine plusieurs de ces primitives dans un scénario realiste de paiement transfrontalier, cas d’usage historique du protocole Ripple.

# Exercice : Paiement cross-currency via trust lines et DEX

import xrpl
from xrpl.asyncio.clients import AsyncJsonRpcClient
from xrpl.asyncio.wallet import generate_faucet_wallet
from xrpl.asyncio.transaction import submit_and_wait
from xrpl.models.transactions import TrustSet, Payment, OfferCreate
from xrpl.models.amounts import IssuedCurrencyAmount
from xrpl.utils import xrp_to_drops
# API asyncio : dans ce kernel Jupyter, chaque appel reseau se fait avec `await`.


def cross_currency_scenario(client):
    """Scenario complet de paiement cross-currency.

    Etapes :
    1. Creer 3 wallets via le faucet (Alice, Bob, Gateway)
    2. Gateway cree des trust lines EUR et USD
    3. Alice etablit une trust line EUR vers Gateway
    4. Bob etablit une trust line USD vers Gateway
    5. Gateway emet 500 EUR a Alice (Payment IOU)
    6. Gateway emet 500 USD a Bob (Payment IOU)
    7. Creer un offer EUR/USD sur le DEX pour etablir un taux
    8. Alice envoie 100 EUR a Bob, qui arrive en ~110 USD
       (utiliser SendMax et DeliverMin pour le cross-currency)
    9. Afficher les soldes finaux de tous les comptes

    Returns:
        dict: Soldes finaux de chaque participant
    """
    # TODO : Implementer le scenario complet
    pass  # TODO: Completez cet exercice
print("Exercice a completer")
Exercice a completer

Indice : paiement cross-currency

Pour realiser ce scénario, vous aurez besoin des transactions suivantes dans l’ordre :

  1. generate_faucet_wallet() pour créer les 3 wallets
  2. TrustSet pour etablir les trust lines EUR et USD vers Gateway
  3. Payment avec IssuedCurrencyAmount pour emettre les IOU
  4. OfferCreate pour créer un taux de change EUR/USD sur le DEX
  5. Payment avec SendMax et DeliverMin pour le paiement cross-currency

Consultez la documentation xrpl.models.transactions pour les paramètres exacts de chaque transaction.


7. Resume

Fonctionnalite Mécanisme XRPL Equivalent Ethereum
Consensus UNL (3-5 sec, déterministe) PoS Casper (~12 sec)
Tokens Trust Lines natives Smart contract ERC-20
DEX Order book integre au protocole Uniswap/smart contract
Micropaiements Payment Channels natifs State channels (complexe)
Smart Contracts Hooks (C/WASM) Solidity/EVM
Escrow Natif (time-lock, conditions) Smart contract Escrow
Frais ~0.000012 XRP (~$0.00001) Variable (gas, congestion)

Points cles

  1. Le XRPL est optimise pour les paiements : rapide, peu couteux, déterministe
  2. Les fonctionnalites financieres sont natives au protocole, pas dans des smart contracts
  3. Les trust lines permettent la creation de tokens (IOU) sans deployer de code
  4. Le DEX integre offre un echange decentralise sans intermediaire logiciel
  5. Les Hooks (C/WASM) ajoutent la programmabilite manquante au XRPL
  6. Le consensus UNL sacrifie un peu de decentralisation pour la performance

Ressources


Notebook suivant : SC-20-Bitcoin-Scripting-Python - Le langage Script de Bitcoin

Resume et perspectives

Ce notebook a permis de decouvrir l’ecosysteme Ripple/XRP, une blockchain concue pour les paiements transfrontaliers rapides et peu couteux. Nous avons explore le consensus UNL (3-5 secondes, finalite déterministe), manipule les comptes et transactions via xrpl-py, et compris les mécanismes natifs des trust lines (système de credit IOU), du DEX integre (carnet d’ordres on-chain avec auto-bridging) et des payment channels (micropaiements off-chain instantanes). Les Hooks, equivalents des smart contracts en C/WASM, completent l’architecture en ajoutant la programmabilite manquante.

La philosophie du XRPL contraste avec celle d’Ethereum : les primitives financieres sont integrees au protocole plutot qu’implementees dans des smart contracts. Ce choix architectural privilegie la performance (1500 TPS, frais negligeables) au depens de la flexibilite (impossible de créer de nouvelles primitives sans modifier le protocole). Les Hooks representent un compromis evolutif, permettant une logique programmable attachee aux comptes tout en conservant le modèle déterministe du XRPL.

Le prochain notebook explore le modèle UTXO et le langage Script de Bitcoin, une approche encore plus minimaliste ou les smart contracts se limitent a des conditions de depense sur une pile, sans aucune machine virtuelle. Cette progression illustre le spectre complet entre computation expressive et verification déterministe : SC-20-Bitcoin-Scripting-Python.


<< Vyper | Bitcoin Scripting >>

Retour au sommet