name: Le protocole RGB, de la théorie à la pratique goal: Acquérir les compétences nécessaires pour comprendre et utiliser RGB objectives:


À la découverte du protocole RGB

Plongez dans l’univers de RGB, un protocole conçu pour appliquer et faire respecter des droits numériques, sous forme de contrats et d’actifs, en s’appuyant sur les règles de consensus et les opérations de la blockchain Bitcoin. Cette formation complète vous guide à travers les fondations techniques et pratiques de RGB, depuis les concepts de la "Client-side Validation" et des "Single-use Seals", jusqu'à l'implémentation de contrats intelligents avancés.

À travers un programme structuré et progressif, vous découvrirez les mécanismes de la validation côté client, les engagements déterministes sur Bitcoin et les schémas d’interaction entre les utilisateurs. Apprenez à créer, gérer et transférer des tokens RGB sur Bitcoin ou bien sur le Lightning Network.

Que vous soyez développeur, passionné de Bitcoin, ou simplement curieux d’en apprendre davantage sur cette technologie, cette formation vous fournira les outils et les connaissances nécessaires pour maîtriser RGB et construire des solutions innovantes sur Bitcoin.

Le cours est issu d'un séminaire en direct organisé par Fulgur'Ventures et enseigné par trois enseignants renommés et experts de RGB.

Introduction

Présentation du cours

Bonjour à tous et bienvenue dans cette formation dédiée à RGB, un système de contrats intelligents validés côté client, fonctionnant sur Bitcoin et le Lightning Network. La structure de cette formation est pensée pour permettre une exploration approfondie de ce sujet complexe. Voici comment la formation est organisée :

Section 1 : Théorie

La première section est dédiée aux concepts théoriques nécessaires pour comprendre les principes fondamentaux de la validation côté client et de RGB. Comme vous le découvrirez dans cette formation, RGB introduit une multitude de concepts techniques que l'on n'a pas l'habitude de voir sur Bitcoin. Vous trouverez donc également dans cette section un glossaire fournissant des définitions pour tous les termes spécifiques au protocole RGB.

Section 2 : Pratique

La deuxième section portera sur l'application des concepts théoriques vus dans la section 1. Nous apprendrons à créer et manipuler des contrats RGB. Nous allons également voir comment programmer avec ces outils. Ces deux premières sections sont présentées par Maxim Orlovsky.

Section 3 : Applications

La dernière section est animée par d'autres intervenants qui présentent des applications concrètes basées sur RGB, afin de mettre en lumière des cas d'utilisation réels.


Cette formation est initialement issue d'un bootcamp de développement avancé de deux semaines à Viareggio, en Toscane, organisé par Fulgur'Ventures. La première semaine, centrée sur Rust et les SDK, peut être retrouvée dans cet autre cours :

https://planb.network/courses/9fbd8b57-f278-4304-8d88-a2d384eaff58

Dans ce cours, nous nous concentrons sur la deuxième semaine du bootcamp, qui porte sur RGB.

Semaine 1 - LNP402 :

RGB-Bitcoin

Semaine 2 - Formation actuelle CSV402 :

RGB-Bitcoin

Un grand merci aux organisateurs de ces cours en direct et aux 3 enseignants qui y ont participé :

La version écrite de cette formation a été rédigée en s'appuyant sur 2 ressources principales :

Prêt à plonger dans l'univers complexe et fascinant de RGB ? C'est parti !

RGB en théorie

Introduction aux concepts de l'informatique distribuée

RGB est un protocole conçu pour appliquer et faire respecter des droits numériques (sous forme de contrats et d’actifs) de manière évolutive et confidentielle, en s’appuyant sur les règles de consensus et les opérations de la blockchain Bitcoin. L’objectif de ce premier chapitre est de présenter les concepts et la terminologie de base autour du protocole RGB, en soulignant notamment ses liens étroits avec des concepts de base de l’informatique distribuée tels que la Client-side Validation et les Single-use Seals.

Dans ce chapitre, nous explorons les fondements des systèmes de consensus distribué et nous verrons comment RGB s’intègre dans cette famille de technologies. Nous introduirons également les grands principes qui permettent de comprendre pourquoi RGB se veut, d’une part, extensible, et d’autre part, indépendant du mécanisme de consensus propre à Bitcoin, tout en s’appuyant sur lui lorsqu’il le faut.

Introduction

L’informatique distribuée (Distributed Computing), une branche spécifique de l’informatique, étudie les protocoles permettant de faire circuler et de traiter des informations sur un réseau de nœuds. L’ensemble de ces nœuds et des règles du protocole constitue ce qu’on appelle un système distribué. Parmi les propriétés essentielles qui caractérisent un tel système, on retrouve :

En particulier, la notion de consensus dans un système distribué recouvre deux aspects :

La première implémentation sans permission et fonctionnelle d’un mécanisme de consensus distribué a été introduite par Satoshi Nakamoto avec Bitcoin, grâce à l’utilisation conjointe d’une structure de données en blockchain et d’un algorithme de Proof-of-Work (PoW). Dans ce système, la crédibilité de l’historique des blocs dépend de la puissance de calcul que les nœuds (mineurs) y consacrent. Bitcoin est donc un exemple historique et majeur de système de consensus distribué ouvert à tous (permissionless).

Dans l'univers de la blockchain et de l'informatique distribuée, nous pouvons distinguer deux paradigmes fondamentaux : la blockchain au sens traditionnel, et les state channels (canaux d'état), dont le meilleur exemple en production est le Lightning Network. La blockchain se définit comme un registre d'événements ordonnés chronologiquement, répliqué par consensus au sein d'un réseau ouvert et sans permission. Les state channels, eux, sont des canaux en pair-à-pair qui permettent à deux (ou plusieurs) participants de maintenir un état mis à jour off-chain, ne recourant à la blockchain qu'au moment de l'ouverture et de la fermeture de ces canaux.

Dans le cadre de Bitcoin, vous connaissez sans doute les principes du minage, de la décentralisation et de la finalité des transactions sur la blockchain, ainsi que le fonctionnement des canaux de paiement. Avec RGB, nous allons introduire un nouveau paradigme appelé Client-side Validation (validation côté client), qui, contrairement à la blockchain ou à Lightning, consiste à conserver et à valider localement (côté client) les transitions d'état d'un contrat intelligent. Ceci se différencie aussi d'autres techniques de la "DeFi" (rollups, plasma, ARK, etc.), dans la mesure où la Client-side Validation s'appuie sur la blockchain pour empêcher la double dépense et pour avoir un système d'horodatage, tout en conservant le registre des états et des transitions off-chain, uniquement chez les participants concernés.

RGB-Bitcoin

Nous allons également plus tard introduire un terme important : la notion de "stash", qui désigne l'ensemble des données côté client nécessaires pour préserver l'état d'un contrat, ces données n'étant pas répliquées de façon globale sur le réseau. Enfin, nous aborderons la raison d'être de RGB, un protocole qui tire parti de la Client-side Validation, et pourquoi il se révèle complémentaire aux approches existantes (blockchain et state channels).

Les trilemmes en informatique distribuée

Pour comprendre en quoi la Client-side Validation et RGB répondent à des problématiques non résolues par la blockchain et Lightning, découvrons 3 "trilemmes" majeurs en informatique distribuée :

1. Scalabilité, décentralisation et confidentialité

La blockchain est très décentralisée, mais peu scalable. De plus, comme tout se trouve dans un registre global et public, la confidentialité est limitée. On peut tenter d'améliorer la confidentialité avec des technologies zero-knowledge (transactions confidentielles, schémas mimblewimble, etc.), mais la chaîne publique ne peut pas cacher le graphe des transactions.

Les canaux d'état (comme avec le Lightning Network) sont plus scalables et plus privés que la blockchain, car les transactions s'effectuent off-chain. Toutefois, l'obligation d'annoncer publiquement certains éléments (transactions de financement, topologie du réseau) et la surveillance du trafic réseau peuvent compromettre en partie la confidentialité. Aussi, la décentralisation en pâtit : le routage requiert une grande quantité de liquidités et les nœuds majeurs peuvent devenir des points de centralisation. C'est justement un phénomène que l'on peut commencer à observer actuellement sur Lightning.

Ce nouveau paradigme est encore plus scalable et plus confidentiel, car non seulement on peut intégrer des techniques de preuves à divulgation nulle de connaissance, mais il n'y a pas de graphe global des transactions, puisque personne ne détient la totalité du registre. En revanche, cela implique aussi un certain compromis sur la décentralisation : l'émetteur d'un contrat intelligent peut avoir un rôle central (à l'instar d'un "contract deployer" dans Ethereum). Néanmoins, contrairement à la blockchain, avec la Client-side Validation, vous ne stockez et ne validez que les contrats qui vous intéressent, ce qui améliore la scalabilité en évitant de télécharger et de vérifier tous les états existants.

RGB-Bitcoin

2. Théorème CAP (Consistency, Availability, Partition tolerance)

Le théorème CAP souligne qu'il est impossible pour un système distribué de satisfaire simultanément la cohérence (Consistency), la disponibilité (Availability) et la tolérance au partitionnement (Partition tolerance).

La blockchain privilégie la cohérence et la disponibilité, mais s'accommode mal de la partition du réseau : si vous ne voyez pas un bloc, vous n'êtes pas en mesure d'agir et d'avoir la même vue que l'ensemble du réseau.

Un système de canaux d'états dispose de la disponibilité et de la tolérance au partitionnement (puisque deux nœuds peuvent rester connectés entre eux même si le réseau est fragmenté), mais la cohérence globale dépend de l'ouverture et de la fermeture des canaux sur la blockchain.

Un système comme RGB offre la cohérence (chaque participant valide ses données localement, sans ambiguïté) et la tolérance au partitionnement (vous conservez vos données de manière autonome), mais ne garantit pas la disponibilité globale (chacun doit s'assurer d'avoir les morceaux d'historique pertinents, et certains participants peuvent ne rien publier ou cesser de partager certaines informations).

RGB-Bitcoin

3. Trilemme CIA (Confidentiality, Integrity, Availability)

Ce trilemme rappelle que la confidentialité, l'intégrité et la disponibilité ne peuvent être optimisées toutes les trois en même temps. Blockchain, Lightning et Client-side Validation se répartissent différemment dans cet équilibre. L'idée est qu'aucun système unique ne peut tout fournir ; il faut combiner plusieurs approches (l'horodatage de la blockchain, l'approche synchrone de Lightning, et la validation locale avec RGB) pour obtenir un ensemble cohérent offrant de bonnes garanties dans chaque dimension.

RGB-Bitcoin

Le rôle de la blockchain et la notion de sharding

La blockchain (ici Bitcoin) sert surtout de mécanisme de time-stamping et de protection contre la double dépense. Au lieu d'y insérer l'intégralité des données d'un smart contract ou d'un système décentralisé, on se contente d'y inclure des engagements cryptographiques (commitments) à des transactions (au sens de la Client-side Validation, que nous appellerons "transitions d'état"). Ainsi :

Le sharding est un concept né dans les bases de données distribuées (par exemple MySQL pour des réseaux sociaux comme Facebook ou Twitter). Pour résoudre le problème de volume de données et de latences de synchronisation, on segmente la base en shards (États-Unis, Europe, Asie, etc.). Chaque segment est cohérent localement et ne se synchronise que partiellement avec les autres.

Pour les smart contracts de type RGB, on sharde selon les contrats eux-mêmes. Chaque contrat constitue un shard indépendant. Par exemple, si vous ne détenez que des jetons USDT, vous n'avez pas à stocker ou valider tout l'historique d'un autre token comme l'USDC. Sur Bitcoin, la blockchain ne fait pas de sharding : vous avez un ensemble d'UTXOs global. Avec la Client-side Validation, chaque participant conserve seulement les données des contrats qu'il détient ou utilise.

On peut donc imaginer l'écosystème ainsi :

RGB-Bitcoin

Ces trois éléments forment un ensemble triangulaire plus qu'un empilement linéaire de "layer 2", "layer 3", etc. Lightning peut se brancher directement sur Bitcoin, ou bien être associé à des transactions Bitcoin qui intègrent des données RGB. De même, un usage de la "BiFi" (finance sur Bitcoin) peut composer avec la blockchain, avec Lightning et avec RGB selon les besoins en confidentialité, scalabilité, ou logique de contrat.

RGB-Bitcoin

La notion de transitions d'état

Dans tout système distribué, l’objectif du mécanisme de validation est de pouvoir déterminer la validité et l’ordre chronologique des changements d’état. Il s’agit de vérifier que les règles du protocole sont bien respectées et de prouver que ces changements d’état se succèdent dans un ordre définitif et inattaquable.

Pour comprendre comment se présente cette validation dans le cadre de Bitcoin et, plus généralement, pour saisir la philosophie derrière la Client-side Validation, revenons d’abord sur les mécanismes de la blockchain Bitcoin, avant de voir comment la validation côté client s’en démarque et quelles optimisations elle rend possibles.

RGB-Bitcoin

Dans le cas de la blockchain Bitcoin, la validation des transactions repose sur une règle simple :

RGB-Bitcoin

Ce modèle présente toutefois deux inconvénients majeurs :

RGB-Bitcoin

En pratique, ce modèle fonctionne pour Bitcoin en tant que couche de base (Layer 1), mais peut devenir insuffisant pour des usages plus complexes qui exigent simultanément un haut débit de transactions et un certain degré de confidentialité.

La Client-side Validation repose sur l’idée inverse : plutôt que d’exiger que tout le réseau valide et stocke toutes les transactions, chaque participant (client) va valider uniquement la partie de l’historique qui le concerne :

RGB-Bitcoin

Parallèlement, pour que le reste du réseau (ou plus exactement la couche sous-jacente, telle que Bitcoin) puisse verrouiller l’état final sans pour autant voir le détail de ces données, la Client-side Validation s’appuie sur la notion de commitment.

Un commitment est un engagement cryptographique, typiquement un hash (SHA-256 par exemple) inséré dans une transaction Bitcoin, qui prouve qu’on a englobé des données privées, sans révéler ces données.

Grâce à ces commitments, on peut prouver :

En revanche, le contenu exact n’est pas révélé, ce qui préserve sa confidentialité.

Concrètement, voici le déroulé d'une transition d'état sur RGB :

RGB-Bitcoin

La Client-side Validation présente ainsi deux bénéfices majeurs :

Dans un système comme RGB, plusieurs transitions d'état de différents contrats (ou différents actifs) peuvent être agrégées dans une même transaction Bitcoin via un seul commitment. Ce mécanisme établit un lien déterministe et horodaté entre la transaction on-chain et les données off-chain (les transitions validées côté client), et permet d’enregistrer simultanément plusieurs shards dans un même point d’ancrage, ce qui réduit encore plus le coût et l’empreinte on-chain.

En pratique, lorsque cette transaction Bitcoin est validée, elle "verrouille" définitivement l’état des contrats sous-jacents, puisqu’il devient impossible de modifier le hash déjà inscrit dans la blockchain.

RGB-Bitcoin

Le concept de stash

Un stash est l'ensemble de données côté client qu'un participant doit absolument conserver pour maintenir l'intégrité et l'historique d'un smart contract RGB. Contrairement à un canal Lightning, où l'on peut reconstruire certains états localement à partir d'informations partagées, le stash d'un contrat RGB n'est pas répliqué ailleurs : si vous le perdez, personne ne pourra vous le restaurer, car vous êtes responsable de votre part de l'historique. C'est pourquoi il faut adopter un système avec des procédures de sauvegarde fiables dans RGB.

RGB-Bitcoin

Single-use Seal : origines et fonctionnement

Lors de l'acceptation d'un actif comme par exemple une monnaie, deux garanties sont essentielles :

Pour les actifs physiques, comme un billet de banque, la présence physique suffit à prouver qu'il n'est pas dupliqué. Cependant, dans le monde numérique, où les actifs sont purement informationnels, cette vérification est plus complexe, car l'information peut facilement se multiplier et être dupliquée.

Comme nous l'avons vu précédemment, la révélation par l'envoyeur de l'historique des transitions d'état permet de s'assurer de l'authenticité d'un jeton RGB. En ayant accès à toutes les transactions depuis la transaction génésique, on peut confirmer l'authenticité du jeton. Ce principe est similaire à celui de Bitcoin où l'on peut suivre l'historique des pièces jusqu'à la transaction coinbase originelle pour vérifier leur validité. Toutefois, contrairement à Bitcoin, cet historique des transitions d'état dans RGB est privé et conservé côté client.

Pour prévenir la double dépense des jetons RGB, nous utilisons un mécanisme appelé "Single-use Seal". Ce système assure que chaque jeton, une fois utilisé, ne peut être réutilisé une seconde fois frauduleusement.

Les Single-use Seals sont des primitives cryptographiques, proposées en 2016 par Peter Todd, qui s’apparentent au concept de scellés physiques : une fois qu’on a placé un sceau sur un conteneur, il devient impossible de l’ouvrir ou de le modifier sans briser le sceau de manière irréversible.

RGB-Bitcoin

Cette approche, transposée à l’univers numérique, permet de prouver qu’une séquence d’événements a bel et bien eu lieu et qu’elle ne peut plus être altérée a posteriori. Les Single-use Seals dépassent donc la simple logique de hash + timestamp en y ajoutant la notion d’un "sceau" fermable une seule et unique fois.

RGB-Bitcoin

Pour que les Single-use Seals fonctionnent, il faut un support de preuve de publication capable de prouver l’existence ou l’absence d’une publication et difficile (voire impossible) à falsifier une fois l’information diffusée. Une blockchain (comme Bitcoin) peut tenir ce rôle, tout comme un journal papier au tirage public par exemple. L’idée est la suivante :

Une blockchain se prête idéalement à ce rôle : dès qu’une transaction est incluse dans un bloc, tout le réseau possède la même preuve infalsifiable de son existence et de son contenu (du moins en partie, puisque le commitment peut masquer les détails tout en prouvant l’authenticité du message).

On peut donc voir un Single-use Seal comme une promesse formelle de publier un message (encore inconnu à ce stade) une et une seule fois, de manière vérifiable par toutes les parties intéressées.

Contrairement aux simples commitments (hash) ou aux horodatages qui attestent d’une date d’existence, un Single-use Seal offre la garantie supplémentaire qu’aucun engagement alternatif ne peut coexister : on ne peut pas fermer deux fois le même sceau ou tenter de remplacer le message scellé.

La comparaison suivante aide à comprendre ce principe :

Engagement simple (digest/hash)TimestampsSingle-use Seals
La publication de l'engagement ne révèle pas le messageOuiOuiOui
Preuve de la date de l'engagement / existence du message avant une certaine dateImpossiblePossiblePossible
Preuve qu'aucun autre engagement alternatif ne peut existerImpossibleImpossiblePossible

Le fonctionnement des Single-use Seals s’articule autour de trois grandes étapes :

Seal Definition :

RGB-Bitcoin

Seal Closing :

RGB-Bitcoin

Seal Verification :

On peut résumer le processus :

# Défini par Alice, validé ou accepté par Bob

seal <- Define()

# Fermeture du sceau par Alice avec le message

witness <- Close(seal, message)

# Vérification par Bob

bool <- Verify(seal, witness, message)

Dans le cadre de la Client-side Validation, il faut toutefois aller plus loin : si la définition d’un sceau reste elle-même hors de la blockchain, il est possible (en théorie) que quelqu’un conteste l’existence ou la légitimité du sceau en question. Pour pallier ce problème, on recourt à une chaîne de Single-use Seals, imbriqués les uns dans les autres :

C’est précisément ce que fait le système RGB :

Pour résumer :

Cette unicité est importante pour la Client-side Validation : quand vous validez une transition d'état, vous vérifiez qu'elle correspond à un UTXO unique, non dépensé préalablement dans un engagement concurrent. C'est ce qui garantit l'absence de double dépense au niveau des smart contracts off-chain.

Engagements multiples et ancrages

Un smart contract RGB peut avoir besoin de dépenser simultanément plusieurs Single-use Seals (plusieurs UTXOs). De plus, une seule transaction Bitcoin peut référencer plusieurs contrats distincts, chacun venant sceller sa propre transition d'état. Cela nécessite un mécanisme de multi-commitments permettant de prouver, de manière déterministe et unique, qu'aucun des engagements n'existe en double. C'est ici qu'intervient la notion d'anchor dans RGB : une structure spéciale reliant une transaction Bitcoin et un ou plusieurs engagements client-side (transitions d'état), chacun relevant potentiellement d'un contrat différent. Nous allons justement détailler ce concept dans le chapitre suivant.

RGB-Bitcoin

Deux principaux dépôts GitHub du projet (sous l'organisation LNPBP) regroupent les implémentations de base de ces concepts étudiés dans le premier chapitre :

RGB-Bitcoin

Notons que ces briques logicielles sont agnostiques par rapport à Bitcoin ; on pourrait, en théorie, les appliquer à tout autre support de preuve de publication (un autre registre, un journal, etc.). Dans la pratique, RGB repose sur Bitcoin pour sa robustesse et son large consensus.

RGB-Bitcoin

Questions du public

Vers un usage plus large des Single-use Seals

Peter Todd a également créé le protocole Open Timestamps, et le concept de Single-use Seal est un prolongement naturel de ces idées. Au-delà de RGB, on peut envisager d'autres cas d'utilisation, par exemple la construction de sidechains sans recourir au merge mining ni aux propositions liées aux drivechains comme le BIP300. Tout système nécessitant un engagement unique peut, en principe, exploiter cette primitive cryptographique. Aujourd'hui, RGB est la première grande mise en application concrète et complète.

Problèmes de disponibilité des données

Étant donné qu'en Client-side Validation, chaque utilisateur stocke sa partie de l'historique, la disponibilité des données n'est pas garantie globalement. Si un émetteur de contrat ne publie pas certaines informations ou les révoque, vous pourriez ignorer l'évolution réelle de l'offre. Dans certains cas (comme les stablecoins), on s'attend à ce que l'émetteur tienne à jour des données publiques pour prouver le volume en circulation, mais rien ne l'y contraint techniquement. Il est donc possible de concevoir des contrats volontairement opaques avec un stock illimité, ce qui pose des questions de confiance.

Sharding et isolement des contrats

Chaque contrat représente un shard isolé : USDT et USDC, par exemple, n'ont pas à partager leur historique. Les swaps atomiques restent possibles, mais cela n'implique pas de fusionner leurs registres. Tout se fait par engagement cryptographique, sans divulguer l'ensemble du graphe d'historique à chaque participant.

Conclusion

Nous avons vu où se situe le concept de Client-side Validation par rapport à la blockchain et aux state channels, en quoi il répond à des trilemmes de l'informatique distribuée, et comment il exploite la blockchain Bitcoin uniquement pour éviter la double dépense et pour l'horodatage (time-stamping). L'idée repose sur la notion de Single-use Seal, permettant la création d'engagements uniques que vous ne pouvez pas redépenser à volonté. Ainsi, chaque participant ne télécharge que l'historique strictement nécessaire, ce qui accroît la scalabilité et la confidentialité des smart contracts tout en conservant la sécurité de Bitcoin en toile de fond.

La prochaine étape consistera à expliquer plus en détail comment on applique concrètement ce mécanisme de Single-use Seal dans Bitcoin (via les UTXOs), comment on crée et on valide les anchors, puis comment on construit des smart contracts complets dans RGB. Nous verrons notamment la question des engagements multiples, le défi technique de prouver qu'une transaction Bitcoin scelle simultanément plusieurs transitions d'état dans différents contrats, sans introduire de vulnérabilités ou de doubles engagements.

Avant de plonger dans les détails plus techniques du deuxième chapitre, n'hésitez pas à relire les définitions clés (Client-side Validation, Single-use Seal, anchors, etc.) et à garder à l'esprit la logique globale : nous cherchons à concilier les atouts de la blockchain Bitcoin (sécurité, décentralisation, time-stamping) avec ceux des solutions off-chain (rapidité, confidentialité, scalabilité), et c'est précisément ce que RGB et la Client-side Validation tentent de réaliser.

La couche d'engagement

Dans ce chapitre, nous allons étudier la mise en application de la Client-side Validation et des Single-use Seals au sein de la blockchain Bitcoin. Nous allons présenter les principes majeurs de la couche d'engagement (layer 1) de RGB, en nous intéressant plus particulièrement au schémas TxO2, retenu par RGB pour définir et fermer un sceau dans le cadre d’une transaction Bitcoin. Ensuite, nous parlerons de deux points importants qui n’ont pas encore été traités en détail :

C’est la combinaison de ces concepts qui nous permet de superposer plusieurs systèmes ou contrats au-dessus d’un même UTXO et donc d’une même blockchain.

Il convient de rappeler que les opérations cryptographiques décrites peuvent s’appliquer, dans l’absolu, à d’autres blockchains ou médias de publication, mais les caractéristiques de Bitcoin (en matière de décentralisation, de résistance à la censure et d’ouverture à tous) en fait le socle idéal pour développer de la programmabilité avancée comme celle requise par RGB.

Les schémas d'engagement dans Bitcoin et leur utilisation par RGB

Comme vu dans le premier chapitre de la formation, les Single-use Seals sont un concept général : on fait une promesse d’inclure un engagement (commitment) dans un emplacement précis d’une transaction, cet emplacement agit comme un scellé que l’on ferme sur un message. Toutefois, sur la blockchain Bitcoin, plusieurs options existent pour choisir où placer ce commitment.

Pour comprendre la logique, rappelons le principe de base : pour fermer un single-use seal, on dépense l’endroit scellé en y insérant le commitment sur un message donné. Dans Bitcoin, cela peut se faire de différentes manières :

On peut décider qu’une clé publique ou une adresse spécifique est le single-use seal. Dès que cette clé ou cette adresse apparaît on-chain dans une transaction, cela signifie que le scellé est fermé avec un certain message.

Cela signifie que l’on définit un single-use seal comme un outpoint précis (un couple TXID + numéro d’output). Dès que cet outpoint est dépensé, il s’agit de l’acte de fermeture du scellé.

En travaillant sur RGB, nous avons identifié au moins 4 manières différentes d’implémenter ces scellés sur Bitcoin :

Nom du schémaDéfinition du scelléFermeture du scelléExigences supplémentairesApplication principaleSchémas d'engagement possibles
PkOValeur de la clé publiqueSortie de transactionP2(W)PKHAucune pour le momentKeytweak, taptweak, opret
TxO2Sortie de transactionSortie de transactionNécessite des engagements déterministes sur BitcoinRGBv1 (universel)Keytweak, tapret, opret
PkIValeur de la clé publiqueEntrée de transactionUniquement Taproot & non compatible avec les portefeuilles LegacyIdentités basées sur BitcoinSigtweak, witweak
TxO1Sortie de transactionEntrée de transactionUniquement Taproot & non compatible avec les portefeuilles LegacyAucune pour le momentSigtweak, witweak

Nous ne détaillerons pas chacune de ces configurations, car dans RGB, nous avons choisi d’utiliser un outpoint comme définition du scellé, et de placer le commitment dans l’output de la transaction dépensant cet outpoint. On peut donc introduire les concepts suivants pour la suite :

Ce schéma a été sélectionné pour sa compatibilité avec l’architecture RGB, mais d’autres configurations pourraient être utiles pour des usages différents.

La mention "O2" dans "TxO2" rappelle que la définition et la fermeture reposent toutes deux sur la dépense (ou la création) d’une sortie de transaction.

Exemple d'utilisation du schéma TxO2

Pour rappel, définir un single-use seal ne nécessite pas nécessairement de publier une transaction on-chain. Il suffit qu’Alice, par exemple, possède déjà un UTXO non dépensé. Elle peut décider : "Cet outpoint (déjà existant) est désormais mon scellé". Elle le note localement (client-side), et tant que cet UTXO n’est pas dépensé, le scellé est considéré comme ouvert.

RGB-Bitcoin

Le jour où elle veut fermer le scellé (pour signaler un événement, ou pour ancrer un message particulier), elle dépense cet UTXO dans une nouvelle transaction (on appelle souvent cette transaction la "witness transaction" (sans rapport avec segwit, c’est juste le terme qu’on lui donne). Cette nouvelle transaction contiendra le commitment au message.

RGB-Bitcoin

Notons que dans cet exemple :

Pour illustrer ce schéma TxO2, on peut utiliser un single-use seal comme mécanisme de révocation d’une clé PGP. Au lieu de publier un certificat de révocation sur des serveurs, Alice peut dire : "Cette sortie Bitcoin, si elle est dépensée, signifie que ma clé PGP est révoquée".

Alice dispose donc d’un UTXO spécifique, auquel est associé localement (côté client) un certain état ou des données (connues d’elle seule).

Alice informe Bob qu’en cas de dépense de cet UTXO, un événement particulier sera réputé s’être produit. De l’extérieur, on ne voit qu’une transaction Bitcoin ; mais Bob, lui, sait que cette dépense a une signification cachée.

RGB-Bitcoin

Au moment où Alice dépense cet UTXO, elle referme le scellé sur un message qui indique sa nouvelle clé, ou simplement la révocation de l’ancienne. Ainsi, toute personne surveillant on-chain verra que l’UTXO est dépensé, mais seule celle qui dispose de la preuve complète saura qu’il s’agit précisément de la révocation de la clé PGP.

RGB-Bitcoin

Pour que Bob ou toute autre personne concernée puisse vérifier le message caché, Alice doit lui fournir des informations off-chain.

RGB-Bitcoin

Alice doit donc fournir à Bob :

RGB-Bitcoin

Les tiers n’ont pas cette information. Ils voient seulement qu’un UTXO a été dépensé. La confidentialité est donc assurée.

Pour bien clarifier la structure, récapitulons le cheminement en deux transactions :

RGB-Bitcoin RGB-Bitcoin

Nous appelons donc la seconde transaction la "witness transaction".

Pour illustrer cela sous un autre angle, on peut représenter deux couches :

RGB-Bitcoin

Mais lors de cette fermeture du scellé, on peut se poser la question suivante : concrètement, où devons-nous insérer le commitment ?

Nous avons brièvement mentionné, dans la partie précédente, comment le modèle Client-side Validation peut s’appliquer à RGB ou à d’autres systèmes. Ici, nous abordons la partie concernant les deterministic Bitcoin commitments et la façon de les intégrer dans une transaction. L’idée est de comprendre pourquoi on cherche à insérer un unique engagement dans la witness transaction, et surtout comment s’assurer qu’il ne puisse y avoir d’autres engagements concurrents non dévoilés.

Les emplacements du commitment dans une transaction

Lorsque vous transmettez à quelqu’un la preuve qu’un certain message est ancré dans une transaction, vous devez pouvoir garantir qu’il n’existe pas, dans cette même transaction, une autre forme d’engagement (un second message caché) qui ne vous aurait pas été révélé. Pour que la validation côté client reste robuste, il faut donc un mécanisme déterministe permettant de placer un unique commitment dans la transaction qui ferme le single-use seal.

La witness transaction dépense le fameux UTXO (ou seal definition) et cette dépense correspond à la fermeture du scellé. Au niveau technique, on sait que chaque outpoint ne peut être dépensé qu’une seule fois. C’est justement ce qui sert de base à la résistance à la double dépense sur Bitcoin. Mais la transaction de dépense peut avoir plusieurs inputs, plusieurs outputs, ou être composée de façon complexe (coinjoins, cannaux Lightning, etc.). Il faut donc définir clairement où insérer le commitment dans cette structure, sans ambiguïté et de manière uniforme.

Quelle que soit la méthode (PkO, TxO2, etc.), le commitment peut être inséré :

RGB-Bitcoin

Voici le détail de chaque méthode :

RGB-Bitcoin

Sig tweak (sign-to-contract) :

Un schéma anciennement proposé consistait à exploiter la partie aléatoire d’une signature (ECDSA ou Schnorr) pour y intégrer le commitment : c’est la technique appelée "Sign-to-contract". Vous remplacez le nonce généré au hasard par un hash contenant la donnée. Ainsi, la signature révèle implicitement votre engagement, sans espace additionnel dans la transaction. Cette approche présente des avantages :

Cependant, 2 inconvénients majeurs ont émergé :

En pratique, sig tweak est également peu compatible avec le matériel (hardware wallets) et les formats existants (Lightning, etc.). Cette belle idée est donc difficile à mettre en place concrètement.

Key tweak (pay-to-contract) :

Le key tweak reprend le concept historique de pay-to-contract. On prend la clé publique X et on la tweak en lui ajoutant la valeur H(message). Concrètement, si X = x * G et h = H(message), alors la nouvelle clé sera X' = X + h * G. Cette clé tweakée dissimule l’engagement sur le message. Le détenteur de la clé privée d’origine peut, en ajoutant h à sa clé privée x, prouver qu’il possède la clé permettant de dépenser la sortie. En théorie, c’est élégant, car :

Néanmoins, dans la pratique, on se heurte aux difficultés suivantes :

Dans le cadre de RGB, cette piste a été envisagée jusqu’en 2021, mais il s’est avéré trop compliqué de la faire fonctionner avec les standards et l’infrastructure actuelle.

Witness tweak :

Une autre idée, que certains protocoles comme les inscriptions Ordinals ont concrétisée, est de placer les données directement dans la section witness de la transaction (d’où l’expression "witness tweak"). Cependant, cette méthode :

En plus, le witness est conçu pour être prunable dans certains contextes, ce qui peut rendre plus compliqué le fait d'avoir des preuves robustes.

Op-return (opret) :

Très simple dans son fonctionnement, un OP_RETURN permet de stocker un hash ou un message dans un champ spécial de la transaction. Mais c’est immédiatement détectable : tout le monde voit qu’il y a un commitment dans la transaction, et cela peut être censuré ou écarté, en plus d’ajouter un output supplémentaire. Puisque cela augmente la transparence et la taille, c’est donc considéré comme moins satisfaisant dans l’optique d’une solution de Client-side Validation.

34-byte_Opret_Commitment =
 OP_RETURN   OP_PUSHBYTE_32   <mpc::Commitment>
|_________| |______________| |_________________|
  1-byte       1-byte         32 bytes                      

Tapret

La dernière option est l’utilisation de Taproot (introduit avec le BIP341) avec le schéma Tapret. Tapret est une forme plus complexe d'engagement déterministe, qui apporte des améliorations en termes d’empreinte sur la blockchain et de confidentialité pour les opérations de contrat. L’idée directrice est de cacher le commitment dans la partie Script Path Spend d’une transaction taproot.

RGB-Bitcoin

Avant de décrire comment l’engagement est inséré dans une transaction taproot, examinons la forme exacte de l’engagement, qui doit impérativement correspondre à une chaîne de 64 octets construite de la manière suivante :

64-byte_Tapret_Commitment =

 OP_RESERVED ...  ... .. OP_RESERVED   OP_RETURN   OP_PUSHBYTE_33  <mpc::Commitment>  <Nonce>
|___________________________________| |_________| |______________| |_______________|  |______|
 OP_RESERVED x 29 times = 29 bytes      1 byte         1 byte          32 bytes        1 byte
|________________________________________________________________| |_________________________|
        TAPRET_SCRIPT_COMMITMENT_PREFIX = 31 bytes                    MPC commitment + NONCE = 33 bytes

Ainsi, la méthode Tapret de 64 octets ressemble à un Opret auquel on a préfixé 29 octets de OP_RESERVED et auquel on ajoute un octet supplémentaire en guise de Nonce.

Pour conserver une grande flexibilité d’implémentation, de confidentialité et de passage à l’échelle, le schéma Tapret prend en compte divers cas d’usage, selon les besoins :

Détaillons ensemble chacun de ces deux scénarios.

Incorporation Tapret sans Script Path existant

Dans ce premier cas, on part d’une sortie taproot (Taproot Output Key) Q qui ne comporte que la clé publique interne P (Internal Key), sans chemin de script associé (Script Path) :

RGB-Bitcoin

Pour inclure un commitment Tapret, il faut alors ajouter une Script Path Spend avec un unique script, selon le schéma suivant :

RGB-Bitcoin

La preuve d’inclusion et d’unicité dans l’arbre taproot se résume ici à la seule clé publique interne P.

Intégration Tapret dans un Script Path préexistant

Le second scénario concerne une sortie taproot Q plus complexe, qui comporte déjà plusieurs scripts. Par exemple, on dispose d’un arbre de 3 scripts :

RGB-Bitcoin

Pour ajouter le commitment Tapret, on doit insérer un script "inconsommable" (unspendable script) au premier niveau de l’arbre, en décalant les scripts déjà existants un niveau plus bas. Visuellement, l’arbre devient :

RGB-Bitcoin

Selon les règles de taproot, chaque branche/feuille doit être combinée en respectant un ordre lexicographique des hachages. Deux cas se présentent alors :

Exemple visuel pour le premier cas (tHABC < tHT) :

RGB-Bitcoin

Exemple pour le second cas (tHABC > tHT) :

RGB-Bitcoin

Optimisation avec le nonce

Pour améliorer la confidentialité, on peut "miner" (un terme plus juste serait "bruteforcer") la valeur du <Nonce> (le dernier octet du Tapret de 64 octets) pour tenter d’obtenir un hash tHT tel que tHABC < tHT. Dans ce cas, le commitment se place à droite, ce qui évite ainsi à l’utilisateur de devoir divulguer tout le contenu des scripts existants pour prouver l’unicité du Tapret.

En résumé, le Tapret offre un moyen discret et déterministe d’incorporer un engagement dans une transaction taproot, tout en respectant l’exigence d’unicité et de non-ambiguïté essentielle à la logique de Client-side Validation et des Single-use Seal de RGB.

Les sorties valides

Pour les opérations de commitment dans le cadre de RGB, l’exigence principale pour qu’un schéma de commitment Bitcoin soit valide est la suivante : La transaction (witness transaction) doit de manière prouvable contenir un seul commitment. Grâce à cette exigence, il devient impossible de construire, au sein d’une même transaction, une histoire alternative pour les données validées côté client. Ainsi, le message autour duquel se ferme le single-use seal est unique.

Pour satisfaire ce principe, et ce quel que soit le nombre de sorties d’une transaction, on impose qu’une seule et unique sortie puisse contenir un engagement (commitment). Pour chacun des schémas utilisés (Opret ou Tapret), les seules sorties valides pouvant contenir un commitment RGB sont :

Notez qu’il est tout à fait possible qu’une transaction contienne simultanément un unique commitment Opret et un unique commitment Tapret dans deux sorties distinctes. Grâce à la nature déterministe de la Seal Definition, ces deux engagements correspondent alors à deux données distinctes validées côté client.

Analyses et choix pratiques dans RGB

Quand nous avons démarré RGB, nous avons passé en revue toutes ces méthodes pour déterminer où et comment placer un commitment dans une transaction de manière déterministe. Nous avons défini des critères :

MéthodeTrace et taille on-chainTaille côté clientIntégration des portefeuillesCompatibilité matérielleCompatibilité LightningCompatibilité Taproot
Keytweak (P2C déterministe)🟢🟡🔴🟠🔴 BOLT, 🔴 Bifrost🟠 Taproot, 🟢 MuSig
Sigtweak (S2C déterministe)🟢🟢🟠🔴🔴 BOLT, 🔴 Bifrost🟠 Taproot, 🔴 MuSig
Opret (OP_RETURN)🔴🟢🟢🟠🔴 BOLT, 🟠 Bifrost-
Algorithme Tapret : noeud haut-gauche🟠🔴🟠🟢🔴 BOLT, 🟢 Bifrost🟢 Taproot, 🟢 MuSig
Algorithme Tapret #4 : n'importe quel nœud + preuve🟢🟠🟠🟢🔴 BOLT, 🟢 Bifrost🟢 Taproot, 🟢 MuSig
Schéma d’engagement déterministeStandardCoût on-chainTaille de la preuve côté client
Keytweak (P2C déterministe)LNPBP-1, 20 bytes33 bytes (clé non tweakée)
Sigtweak (S2C déterministe)WIP (LNPBP-39)0 bytes0 bytes
Opret (OP_RETURN)-36 (v)bytes (TxOut additionnel)0 bytes
Algorithme Tapret : nœud haut-gaucheLNPBP-632 bytes dans le témoin (8 vbytes) sur n’importe quel multisig n-of-m et dépenses par chemin de script0 bytes sur les scriptless scripts taproot ~270 bytes dans un cas de script unique, ~128 bytes si plus d’un script
Algorithme Tapret #4 : n’importe quel nœud + preuve d’unicitéLNPBP-632 bytes dans le témoin (8 vbytes) pour les cas de script unique, 0 bytes dans le témoin dans la plupart des autres cas0 bytes sur les scriptless scripts taproot, 65 bytes jusqu’à ce que le Taptree ait une douzaine de scripts
LayerCoût on-chain (bytes/vbytes)Coût on-chain (bytes/vbytes)Coût on-chain (bytes/vbytes)Coût on-chain (bytes/vbytes)Coût on-chain (bytes/vbytes)Coût côté client (bytes)Coût côté client (bytes)Coût côté client (bytes)Coût côté client (bytes)Coût côté client (bytes)
TypeTapretTapret #4KeytweakSigtweakOpretTapretTapret #4KeytweakSigtweakOpret
Single-sig00003200320?0
MuSig (n-of-n)0000320032? > 00
Multi-sig 2-of-332/832/8 ou 00n/a32~2706532n/a0
Multi-sig 3-of-532/832/8 ou 00n/a32~3406532n/a0
Multi-sig 2-of-3 with timeouts32/800n/a32646532n/a0
LayerCoût on-chain (vbytes)Coût on-chain (vbytes)Coût on-chain (vbytes)Coût côté client (bytes)Coût côté client (bytes)
TypeBaseTapret #2Tapret #4Tapret #2Tapret #4
MuSig (n-of-n)16.50000
FROST (n-of-m)?0000
Multi_a (n-of-m)1+16n+8m8833 * m65
Branche MuSig / Multi_a (n-of-m)1+16n+8n+8xlog(n)806465
Avec timeouts (n-of-m)1+16n+8n+8xlog(n)806465
MéthodeConfidentialité et évolutivitéInteropérabilitéCompatibilitéPortabilitéComplexité
Keytweak (P2C déterministe)🟢🔴🔴🟡🟡
Sigtweak (S2C déterministe)🟢🔴🔴🟢🔴
Opret (OP_RETURN)🔴🟠🔴🟢🟢
Algo Tapret : nœud haut-gauche🟠🟢🟢🔴🟠
Algo Tapret #4 : Nœud quelconque + preuve🟢🟢🟢🟠🔴

Au fil de l’étude, il est apparu qu’aucun des schémas de commitments n’était pleinement compatible avec le standard Lightning actuel (qui n’emploie pas Taproot, ni muSig2, ni la prise en compte d’un commitment supplémentaire). Des efforts sont en cours pour modifier la construction de canaux Lightning (BiFrost) et permettre d’insérer les engagements RGB. C’est un autre chantier où l’on doit revoir la structure de la transaction, les clés, et la façon dont sont signées les mises à jour de canaux.

L’analyse a montré qu’en effet, d’autres méthodes (key tweak, sig tweak, witness tweak, etc.) présentaient d’autres formes de complication :

Ainsi, pour RGB, deux des méthodes sortent particulièrement du lot : Opret et Tapret, toutes deux classées en “Transaction Output”, et compatibles avec le mode TxO2 utilisé par le protocole.

Multi Protocol Commitments - MPC

Dans cette section, nous abordons la manière dont RGB gère l’agrégation de plusieurs contrats (ou plus précisément leurs transition bundles) au sein d’un unique engagement (commitment) enregistré dans une transaction Bitcoin via un schéma déterministe (selon Opret ou Tapret). Pour y parvenir, l'ordre de Merkelisation des différents contrats s’opère dans une structure nommée MPC Tree (Multi Protocol Commitment Tree). Dans cette section, nous allons étudier la construction de ce MPC Tree, l’obtention de sa racine, ainsi que la façon dont plusieurs contrats peuvent ainsi partager la même transaction en toute confidentialité et sans ambiguïté.

Le Multi Protocol Commitment (MPC) vise à répondre à deux besoins :

Concrètement, chacun des transition bundles appartient à un contrat particulier. Toutes ces informations sont insérées dans un MPC Tree dont la racine (mpc::Root) est ensuite hachée de nouveau pour donner le mpc::Commitment. C’est ce dernier hash qui est placé dans la transaction Bitcoin (witness transaction), selon la méthode déterministe choisie.

RGB-Bitcoin

MPC Root Hash

La valeur effectivement inscrite on-chain (dans Opret ou Tapret) se nomme mpc::Commitment. Celle-ci est calculée en suivant la forme du BIP-341, selon la formule :

mpc::Commitment = SHA-256(SHA-256(mpc_tag) || SHA-256(mpc_tag) || depth || cofactor || mpc::Root )

où :

RGB-Bitcoin

Construction du MPC Tree

Pour construire ce MPC Tree, il faut assurer qu’à chaque contrat corresponde une position de feuille unique. Supposons qu’on ait :

On va alors construire un arbre de largeur w et de profondeur d telle que 2^d = w, avec w > C, de sorte que chaque contrat puisse être placé dans une leaf distincte. La position pos(c_i) de chaque contrat dans l’arbre est déterminée par :

pos(c_i) = c_i mod (w - cofactor)

cofactor est un entier qui augmente les probabilités d’obtenir des positions distinctes pour chaque contrat. Dans la pratique, la construction suit un processus itératif :

Le but est d’éviter les arbres trop grands tout en maintenant un risque de collision minimal. Notons que le phénomène de collisions suit une logique de distribution aléatoire, liée au Paradoxe des anniversaires.

Les feuilles habitées

Une fois C positions distinctes pos(c_i) obtenues pour les contrats i = {0,1,..,C-1}, on renseigne chaque feuille via une fonction de hachage (tagged hash) :

tH_MPC_LEAF(c_i) = SHA-256(SHA-256(merkle_tag) || SHA-256(merkle_tag) || 0x10 || c_i || BundleId(c_i))

où :

Les feuilles inhabitées

Les feuilles restantes, non affectées à un contrat (c’est-à-dire w - C feuilles), sont remplies par une valeur dite "dummy" (entropy leaf) :

tH_MPC_LEAF(j) = SHA-256(SHA-256(merkle_tag) || SHA-256(merkle_tag) || 0x11 || entropy || j )

où :

Les nœuds MPC

Après avoir généré les w feuilles (habitées ou non), on procède à la merkelisation. Tout nœud interne est haché comme suit :

tH_MPC_BRANCH(tH1 || tH2) = SHA-256(SHA-256(merkle_tag) || SHA-256(merkle_tag) || b || d || w || tH1 || tH2)

où :

En progressant ainsi, on obtient la racine mpc::Root. On peut ensuite calculer mpc::Commitment (comme expliqué précédemment) et l’insérer on-chain.

Pour illustrer cela, imaginons un exemple où C=3 (trois contrats). On suppose que leurs positions sont pos(c_0)=7, pos(c_1)=4, pos(c_2)=2. Les autres feuilles (positions 0, 1, 3, 5, 6) sont des entropy leaves. Le schéma ci-dessous montre comment s’enchaînent les hachages jusqu’à la racine avec :

Le résultat final est le mpc::Root, puis le mpc::Commitment.

RGB-Bitcoin

Vérification de l'arbre MPC

Lorsqu’un vérificateur souhaite s’assurer qu’un contrat c_i​ (et son BundleId) est bien inclus dans l’engagement final mpc::Commitment, il reçoit simplement une preuve de Merkle. Cette preuve indique les nœuds nécessaires pour remonter des feuilles (ici, la contract leaf de c_i​) jusqu’à la racine. Inutile de divulguer l’intégralité du MPC Tree : cela protège la confidentialité des autres contrats.

Dans l’exemple, un vérificateur de c_2 n’a besoin que d'un hachage intermédiaire (tH_MPC_LEAF(D)), de deux tH_MPC_BRANCH(...), de la preuve de la position pos(c_2) et de la valeur cofactor. Il peut alors reconstruire localement la racine, puis recalculer le mpc::Commitment et le comparer à celui inscrit dans la transaction Bitcoin (au sein d’Opret ou de Tapret).

RGB-Bitcoin

Ce mécanisme garantit ainsi que :

Résumé de la structure MPC

Le Multi Protocol Commitment (MPC) est donc le principe qui permet à RGB d’agréger plusieurs contrats dans une seule transaction Bitcoin, tout en maintenant l’unicité des engagements et la confidentialité vis-à-vis des autres participants. Grâce à la construction déterministe de l’arbre, chaque contrat se voit attribuer une position unique, et la présence de feuilles “dummy” (Entropy Leaves) masque partiellement le nombre total de contrats participant à l’opération.

Sur le client, on ne stocke jamais l’ensemble de l'arbre de Merkle. On se contente de générer, à l’instant T, un Merkle path pour chaque contrat concerné, à transmettre au destinataire (qui pourra ainsi valider l’engagement). Dans certains cas, vous possédez plusieurs actifs passés par le même UTXO. Vous pouvez alors fusionner plusieurs Merkle paths dans ce qu’on appelle un multi-protocol commitment block, afin d'éviter de dupliquer trop de données.

Chaque Merkle proof est donc légère, d’autant plus que la profondeur de l’arbre n’excédera pas 32 dans RGB. Il existe également une notion de "Merkle block", qui conserve plus d’informations (la cross-section, l’entropie, etc.), utile pour combiner ou séparer plusieurs branches.

Voilà pourquoi la finalisation de RGB a demandé du temps. On avait la vision globale dès 2019 : tout mettre en client-side, faire circuler les tokens off-chain. Mais pour les détails comme le sharding pour plusieurs contrats, la structure de l'arbre de Merkle, la manière de gérer les collisions et la fusion de preuves… tout cela a exigé des itérations.

Les anchors : un assemblage global

Dans la continuité de la construction de nos engagements (Opret ou Tapret) et de notre MPC (Multi Protocol Commitment), nous devons aborder la notion d’Anchor dans le protocole RGB. Un Anchor est une structure validée côté client qui rassemble les éléments nécessaires pour vérifier qu’un engagement Bitcoin renferme bien une information contractuelle précise. Autrement dit, un Anchor résume toutes les données utiles à la validation des commitments décrits précédemment.

Un Anchor se compose de trois champs ordonnés :

Chacun de ces champs intervient dans la procédure de validation, qu’il s’agisse de reconstituer la transaction Bitcoin sous-jacente ou de prouver l’existence d’un engagement caché (notamment dans le cas de Tapret).

TxId

Le champ Txid correspond à l’identifiant de 32 octets de la transaction Bitcoin qui contient l’engagement Opret ou Tapret.

En théorie, il serait envisageable de retrouver ce Txid en retraçant la chaîne de transitions d'états qui pointent elles-mêmes vers chaque witness transaction, en suivant la logique des Single-use Seals. Cependant, pour faciliter et accélérer la vérification, ce Txid est tout simplement inclus dans l’Anchor, ce qui évite ainsi au validateur d’avoir à remonter tout l’historique off-chain.

MPC Proof

Le second champ, la MPC Proof, se rapporte à la preuve que ce contrat précis (par exemple c_i) est bien inclus dans le Multi Protocol Commitment. Il s’agit d’une combinaison de :

Ce mécanisme a été décrit dans la section précédente consacrée à la construction du MPC Tree, où chaque contrat obtient une feuille unique grâce à l’opération :

pos(c_i) = c_i mod (w - cofactor)

Puis, on utilise un schéma de merkelisation déterministe pour agréger toutes les feuilles (contrats + entropie). La MPC Proof permet, au final, de reconstituer localement la racine et de la comparer au mpc::Commitment inclus on-chain.

Extra Transaction Proof – ETP

Le troisième champ, l’ETP, dépend du type d’engagement utilisé. Si l’engagement est de type Opret, aucune preuve supplémentaire n’est requise. Le validateur inspecte la première sortie OP_RETURN de la transaction et y retrouve directement le mpc::Commitment.

Si l’engagement est de type Tapret, il faut fournir une preuve additionnelle appelée Extra Transaction Proof – ETP. Elle contient :

Cette preuve supplémentaire est indispensable, car, contrairement à Opret, l’engagement Tapret s’intègre dans la structure d’un script taproot, ce qui exige de révéler une partie de l’arbre taproot afin de valider correctement l’emplacement du commitment.

RGB-Bitcoin

Les Anchors encapsulent donc l’ensemble des informations nécessaires pour valider un engagement Bitcoin dans le contexte de RGB. Ils indiquent à la fois la transaction pertinente (Txid) et les preuves de positionnement du contrat (MPC Proof), tout en gérant la preuve supplémentaire (ETP) dans le cas de Tapret. Ainsi, un Anchor protège l’intégrité et l’unicité de l’état off-chain en assurant qu’une même transaction ne puisse être réinterprétée pour d’autres données contractuelles.

Conclusion

Dans ce chapitre, nous avons couvert :

En pratique, la mise en œuvre technique est répartie entre plusieurs crates Rust dédiés (dans client_side_validation, commit-verify, bp_core, etc.). Les notions fondamentales sont là :

RGB-Bitcoin

Dans le chapitre suivant, nous étudierons la composante purement off-chain de RGB, à savoir la logique des contrats. Nous verrons comment les contrats RGB, organisés sous forme de finite state machine partiellement répliquée, atteignent une expressivité bien plus élevée que celle autorisée par les scripts Bitcoin, tout en préservant la confidentialité de leurs données.

Introduction aux contrats intelligents et à leurs états

Dans ce chapitre et le prochain, nous allons aborder la notion de smart contract au sein de l’environnement RGB et nous allons étudier les différentes manières dont ces contrats peuvent définir et faire évoluer leur état (state). Nous verrons pourquoi l’architecture RGB, en utilisant la séquence ordonnée de Single-use Seals, permet d’exécuter divers types de Contract Operations de manière scalable et sans passer par un registre centralisé. Nous verrons également le rôle fondamental de la Business Logic pour encadrer l’évolution de l’état contractuel.

Contrats intelligents et droits au porteur numériques

L’objectif de RGB est de proposer une infrastructure où l’on peut mettre en œuvre des smart contracts sur Bitcoin. Par "smart contract", on entend un accord entre plusieurs parties qui est automatiquement et informatiquement appliqué, sans intervention humaine pour faire respecter les clauses. En d’autres termes, la loi du contrat est exécutée par le logiciel, et non par un tiers de confiance.

Cette automatisation soulève la question de la décentralisation : comment s’affranchir d’un registre centralisé (par exemple une plateforme ou une base de données centrale) pour gérer la propriété et l’exécution des contrats ? L’idée d’origine, reprise par RGB, consiste à renouer avec un mode de possession dit "au porteur" (bearer instruments). Dans la tradition historique, certains titres (obligations, actions, etc.) étaient émis au porteur, permettant à quiconque possédait physiquement le document de faire valoir ses droits.

RGB-Bitcoin

RGB applique ce concept au monde numérique : les droits (et obligations) sont enfermés dans des données manipulées off-chain, et l’état de ces données est validé par les participants eux-mêmes. Cela permet, à priori, un degré de confidentialité et d’indépendance beaucoup plus grand que celui qu’offrent d’autres approches basées sur des registres publics.

Introduction à l’État d’un Smart Contract RGB

Un smart contract dans RGB peut être vu comme une machine à états, définie par :

RGB-Bitcoin

Il est important de comprendre que ces contrats ne sont pas limités aux simples transferts de tokens. Ils peuvent incarner une grande variété d’applications : des actifs traditionnels (jetons, actions, obligations) jusqu’à des mécaniques plus complexes (droits d’usage, conditions commerciales, etc.). Contrairement à d’autres blockchains où le code du contrat est accessible et exécutable par tous, l’approche de RGB cloisonne l’accès et la connaissance du contrat aux participants ("contract participants"). Il existe ainsi plusieurs rôles :

Cette séparation des rôles contribue à la résistance à la censure, en permettant que seules les personnes autorisées puissent interagir avec l’état contractuel. Cela confère également à RGB la capacité de s’étendre de manière horizontale : la majorité des validations a lieu en dehors de la blockchain, et seules des ancrages cryptographiques (les commitments) sont inscrits sur Bitcoin.

État et Business Logic dans RGB

D’un point de vue pratique, la Business Logic du contrat se présente sous forme de règles et de scripts, définis dans ce que RGB appelle un Schema. Le Schema encode :

En parallèle, l’État (Contract State) se décompose souvent en deux volets :

Comme nous le verrons dans les chapitres suivants, toute mise à jour d’état (Contract Operation) doit s’arrimer à un commitment Bitcoin (via Opret ou Tapret) et se conformer aux scripts de la Business Logic pour être considérée comme valide.

Contract Operations : création et évolution de l’État

Dans l’univers RGB, on appelle Contract Operation tout événement qui fait passer le contrat d’un ancien état (old state) à un nouvel état (new state). Ces opérations suivent la logique suivante :

RGB-Bitcoin

Le résultat final est un contrat mis à jour, dont l’État est désormais différent. Cette transition ne nécessite pas que l’ensemble du réseau Bitcoin s’intéresse aux détails, puisque seule une petite empreinte cryptographique (le commitment) est enregistrée dans la blockchain. La séquence des Single-use Seals prévient toute double-dépense ou double-utilisation de l’État.

Chaîne d’opérations : de la Genesis au Terminal State

Pour remettre en perspective, un smart contract RGB démarre par une Genesis, le tout premier état. Par la suite, diverses Contract Operations se succèdent, formant un DAG (Directed Acyclic Graph) d’opérations :

RGB-Bitcoin

Cette topologie en DAG (au lieu d’une simple chaîne linéaire) reflète la possibilité que différentes parties du contrat puissent évoluer en parallèle, tant qu’elles ne se contredisent pas. RGB se charge alors d’éviter toute incohérence via la vérification client-side de chaque participant concerné.

Synthèse

Les smart contracts dans RGB introduisent un modèle d’instruments au porteur numériques, décentralisés, mais ancrés dans Bitcoin pour l’horodatage et la garantie de l’ordre des opérations. L’exécution automatisée de ces contrats repose sur :

Dans le chapitre suivant, nous entrerons plus en détail dans la représentation concrète de ces states et des state transitions au niveau off-chain, ainsi que dans la manière dont ils se lient aux UTXOs et aux Single-use Seals ancrés dans Bitcoin. Ce sera l’occasion de voir comment la mécanique interne de RGB, fondée sur une validation client-side, parvient à maintenir la cohérence des smart contracts tout en préservant la confidentialité des données.

Opérations des contrats RGB

Dans ce chapitre, nous allons étudier le fonctionnement des opérations dans les contrats intelligents et des transitions d'état, toujours au sein du protocole RGB. Le but sera également de comprendre comment plusieurs participants coopèrent pour transférer la propriété d’un actif.

Les transitions d'état et leurs mécaniques

Le principe général est toujours celui de la Client-side Validation, où les données de l’état sont conservées chez le propriétaire et validées par le destinataire. Toutefois, la spécificité ici avec RGB réside dans le fait que Bob, en tant que destinataire, demande à Alice d’incorporer certaines informations dans les données du contrat afin d’avoir un véritable contrôle sur l’actif reçu, via une référence cachée à l’un de ses UTXOs.

Pour illustrer le processus d’une State Transition (qui est l’une des Contract Operations fondamentales dans RGB), suivons pas à pas l’exemple d’un transfert d’actif entre Alice et Bob :

Situation initiale : Alice dispose d’un stash RGB de données validées en local (client-side). Ce stash fait référence à l’un de ses UTXOs sur Bitcoin. Cela signifie qu’une définition de sceau (seal definition) pointe, dans ces données, vers un UTXO qui appartient à Alice. L’idée est de lui permettre de transférer à Bob certains droits numériques liés à un actif (par exemple des jetons RGB).

RGB-Bitcoin

Bob possède également des UTXOs : Bob, de son côté, détient au moins un UTXO qui lui est propre, sans lien direct avec celui d’Alice. Dans le cas où Bob ne posséderait pas d'UTXO, il reste tout de même envisageable de procéder au transfert à son bénéfice en utilisant la transaction témoin (witness transaction) elle-même : l’output de cette transaction inclura alors l’engagement (commitment) et associera implicitement la propriété du nouveau contrat à Bob.

RGB-Bitcoin

Construction de la nouvelle propriété (New State) : Bob envoie à Alice des informations encodées sous forme d’invoice (nous détaillerons dans les prochains chapitres la construction de l'invoice) lui demandant de créer un nouvel état conforme aux règles du contrat. Cet état inclura une nouvelle seal definition pointant vers l’un des UTXOs de Bob. Ainsi, Bob se voit attribuer la propriété sur les actifs définis dans ce nouvel état, par exemple un certain montant de jetons RGB.

RGB-Bitcoin

Préparation de la transaction témoin : Alice crée ensuite une transaction Bitcoin dépensant l'UTXO référencé dans le sceau précédent (celui qui la légitimait comme détentrice). Dans la sortie de cette transaction, un commitment (via Opret ou Tapret) est inséré pour ancrer le nouvel état RGB. Les engagements Opret ou Tapret sont dérivés d’un MPC tree (comme vu dans les chapitres précédents), qui peut agréger plusieurs transitions de différents contrats.

Transmission du Consignment à Bob : Avant de diffuser la transaction, Alice envoie à Bob un Consignment contenant l’intégralité des données client-side nécessaires (son stash) ainsi que les informations du nouvel état en faveur de Bob. À ce stade, Bob applique les règles de consensus RGB :

Finalisation de la transition : Si Bob est satisfait, il peut éventuellement donner son approbation (par exemple en signant le consignment). Alice peut alors diffuser la transaction témoin préparée. Une fois confirmée, celle-ci clos le sceau précédemment détenu par Alice et officialise la propriété par Bob. La sécurité anti double-dépense se base alors sur le même mécanisme que dans Bitcoin : l’UTXO est dépensé, ce qui prouve qu’Alice ne peut plus le réutiliser.

RGB-Bitcoin Le nouvel état référence désormais l'UTXO de Bob, ce qui confère à celui-ci la propriété que détenait précédemment Alice. La sortie Bitcoin où sont ancrées les données RGB devient la preuve irrévocable du transfert de propriété.

Un exemple de DAG (Directed Acyclic Graph) minimal comprenant deux opérations de contrat (une Genesis puis un State Transition) peut illustrer comment l’état RGB (couche client-side, en rouge) se relie à la blockchain Bitcoin (couche Commitment, en orange).

RGB-Bitcoin

On y voit qu’une Genesis définit un sceau (seal definition), puis qu’une State Transition vient clore ce sceau pour en créer un nouveau dans un autre UTXO.

Dans ce contexte, voici quelques rappels de terminologie :

Les State Transitions, décrites dans le chapitre précédent, constituent la forme principale d’opérations de contrat. Elles se réfèrent à un ou plusieurs états antérieurs (issus de la Genesis ou d’une autre State Transition) et les mettent à jour vers un nouvel état.

RGB-Bitcoin

Ce schéma montre comment, dans une State Transition Bundle, on peut clore plusieurs sceaux en une seule transaction témoin, en ouvrant simultanément de nouveaux sceaux. En effet, une caractéristique intéressante du protocole RGB est sa possibilité de passage à l'échelle : plusieurs transitions peuvent être agrégées dans un Transition Bundle, chaque agrégation étant associée à une feuille distincte du MPC tree (un identifiant de bundle unique). Grâce au mécanisme de Deterministic Bitcoin Commitment (DBC), l’ensemble du message est inséré dans une sortie Tapret ou Opret, tout en fermant les sceaux précédents et en définissant éventuellement de nouveaux sceaux. L’Anchor sert de lien direct entre l’engagement stocké dans la blockchain et la structure de validation côté client (client-side).

Nous étudierons dans les chapitre suivants tous les composants et les processus liés à la construction et à la validation d’une State Transition. La plupart de ces éléments relèvent du consensus RGB, implémenté dans la "RGB Core Library".

Transition Bundle

Sur RGB, il est possible de regrouper différentes State Transitions appartenant au même contrat (c’est-à-dire partageant le même ContractId, dérivé du OpId de la Genesis). Dans le cas le plus simple, comme entre Alice et Bob dans l’exemple ci-dessus, un Transition Bundle ne contient qu’une seule transition. Mais la prise en charge des opérations multi-payer (comme par exemple des coinjoins, des ouvertures de canaux Lightning, etc.) permet à plusieurs utilisateurs de combiner leurs State Transitions en un seul bundle.

Une fois rassemblées, ces transitions sont ancrées (par le mécanisme MPC + DBC) dans une unique transaction Bitcoin :

Sur le plan technique, le BundleId inséré dans la feuille MPC est obtenu à partir d’un tagged hash appliqué à la sérialisation stricte du champ InputMap du bundle :

BundleId = SHA256( SHA256(bundle_tag) || SHA256(bundle_tag) || InputMap )

Dans lequel bundle_tag = urn:lnp-bp:rgb:bundle#2024-02-03 par exemple.

L’InputMap est une structure de données qui répertorie, pour chaque entrée i de la transaction témoin, la référence à l’OpId de la State Transition correspondante. Par exemple :

InputMap =
         N               input_0    OpId(input_0)    input_1    OpId(input_1)   ...    input_N-1  OpId(input_N-1)    
|____________________| |_________||______________| |_________||______________|       |__________||_______________|
 16-bit Little Endian   32-bit LE   32-byte hash                                         
                       |_________________________| |_________________________|  ...  |___________________________|
                               MapElement1                MapElement2                       MapElementN 

En référençant chaque entrée une seule fois et de manière ordonnée, on empêche la double-dépense d’un même sceau dans deux State Transitions simultanées.

State Generation et Active State

Les State Transitions permettent donc de transférer la propriété d’un actif d’une personne à une autre. Cependant, ce ne sont pas les seules opérations possibles dans le protocole RGB. Le protocole définit trois Contract Operations :

Parmi celles-ci, Genesis et State Extension sont parfois appelées "State Generation operations", car elles créent de nouveaux états sans pour autant en refermer immédiatement. C’est d'ailleurs un point très important : Genesis et State Extension n’impliquent pas la fermeture d’un sceau. Elles définissent plutôt un nouveau sceau, qui devra ensuite être dépensé par une State Transition ultérieure pour être réellement validé dans l’historique de la blockchain.

RGB-Bitcoin

On définit souvent l’Active State d’un contrat comme l’ensemble des derniers états résultant de l’historique (le DAG) des opérations, en commençant par la Genesis et en suivant tous les ancrages dans la blockchain Bitcoin. Tous les anciens états déjà obsolètes (c’est-à-dire attachés à des UTXOs dépensés) ne sont plus considérés comme actifs, mais restent indispensables pour vérifier la cohérence de l’historique.

Genesis

La Genesis est le point de départ de tout contrat RGB. Elle est créée par l’émetteur du contrat et définit les paramètres initiaux, conformément au Schema. Dans le cas d’un token RGB, la Genesis peut spécifier, par exemple :

Étant la première opération du contrat, la Genesis ne référence aucun état antérieur, ni ne ferme aucun sceau. Toutefois, pour apparaître dans l’historique et être validée, la Genesis doit être consommée (refermée) par une première State Transition (souvent une transaction de balayage / auto-spend vers l’émetteur lui-même ou bien la distribution initiale aux utilisateurs).

State Extension

Les State Extensions offrent une fonctionnalité originale pour des smart contracts. Elles permettent de racheter certains droits numériques (Valencies) prévus dans la définition du contrat, sans fermer immédiatement le sceau. Le plus souvent, cela concerne :

Sur le plan technique, une State Extension référence un Redeem (un type particulier d’input RGB) qui correspond à une Valency définie précédemment (par exemple dans la Genesis ou dans une autre State Transition). Elle définit un nouveau sceau, à la disposition de la personne ou de la condition qui en bénéficie. Pour que ce sceau soit rendu effectif, il faudra qu’une State Transition ultérieure vienne le dépenser.

RGB-Bitcoin

Par exemple : la Genesis crée un droit d’émission (Valency). Celui-ci peut être exercé par un acteur autorisé, qui construit alors une State Extension :

Composants d’une Contract Operation

Maintenant, je vous propose d'examiner de manière détaillée chacun des éléments constitutifs d’une Contract Operation dans RGB. Une Contract Operation est l’action qui permet de modifier l’état d’un contrat, et dont la validation se fait côté client, de manière déterministe, par le destinataire légitime. Nous allons notamment voir comment la Contract Operation prend en compte, d’un côté, l’ancien état (Old State) du contrat, et de l’autre côté, la définition d’un nouvel état (New State).

               +---------------------------------------------------------------------------------------------------------------------+
               |  Contract Operation                                                                                                 |
               |                                                                                                                     |
               |  +-----+     +-----------------------+      +--------------------------------+      +---------+     +------------+  |                             
               |  | Ffv |     | ContractId | SchemaId |      | TransitionType | ExtensionType |      | Testnet |     | AltLayers1 |  |                               
               |  +-----+     +-----------------------+      +--------------------------------+      +---------+     +------------+  |     
               |                                                                                                                     |
               |  +-----------------------------------------------+  +------------------------------------------------------------+  |
               |  | Metadata                                      |  | Global State                                               |  |
               |  |                                               |  | +----------------------------------+                       |  |
               |  | +-------------------------------------+       |  | | +-------------------+ +--------+ |                       |  |
               |  | |          Structured Data            |       |  | | |  GlobalStateType  | |  Data  | |     ...     ...       |  |
               |  | +-------------------------------------+       |  | | +-------------------+ +--------+ |                       |  |
               |  |                                               |  | +----------------------------------+                       |  |
               |  +-----------------------------------------------+  +------------------------------------------------------------+  |         +------+
               |                                                                                                                     +---------> OpId |
               |  +-----------------------------------------------+  +------------------------------------------------------------+  |         +------+
               |  | Inputs                                        |  | Assignments                                                |  |
               |  |                                               |  |                                                            |  |
               |  | +-------------------------------------------+ |  | +--------------------------------------------------------+ |  |
               |  | | Input #1                                  | |  | | Assignment #1                                          | |  |
+------+       |  | | +----------+ +----------------+ +-------+ | |  | | +----------------+ +-------------+ +-----------------+ | |  |       +--------------+
| OpId +--------------> PrevOpId | | AssignmentType | | Index | | |  | | | AssignmentType | | Owned State | | Seal Definition +--------------> Bitcoin UTXO |
+------+       |  | | +----------+ + ---------------+ +-------+ | |  | | +----------------+ +-------------+ +-----------------+ | |  |       +--------------+
               |  | +-------------------------------------------+ |  | +--------------------------------------------------------+ |  |         
               |  |                                               |  |                                                            |  |         
               |  | +-------------------------------------------+ |  | +--------------------------------------------------------+ |  |         
               |  | | Input #2                                  | |  | | Assignment #2                                          | |  |         
+------+       |  | | +----------+ +----------------+ +-------+ | |  | | +----------------+ +-------------+ +-----------------+ | |  |       +--------------+
| OpId +--------------> PrevOpId | | AssignmentType | | Index | | |  | | | AssignmentType | | Owned State | | Seal Definition +--------------> Bitcoin UTXO |
+------+       |  | | +----------+ +----------------+ +-------+ | |  | | +----------------+ +-------------+ +-----------------+ | |  |       +--------------+
               |  | +-------------------------------------------+ |  | +--------------------------------------------------------+ |  |
               |  |                                               |  |                                                            |  |
               |  |       ...           ...          ...          |  |     ...          ...             ...                       |  |
               |  |                                               |  |                                                            |  |
               |  +-----------------------------------------------+  +------------------------------------------------------------+  |
               |                                                                                                                     |
               |  +-----------------------------------------------+  +------------------------------------------------------------+  |
               |  | Redeems                                       |  | Valencies                                                  |  |
               |  |                                               |  |                                                            |  |            
               |  | +------------------------------+              |  |                                                            |  |   
+------+       |  | | +----------+ +-------------+ |              |  |  +-------------+  +-------------+                          |  |            
| OpId +--------------> PrevOpId | | ValencyType | |  ...   ...   |  |  | ValencyType |  | ValencyType |         ...              |  |    
+------+       |  | | +----------+ +-------------+ |              |  |  +-------------+  +-------------+                          |  |    
               |  | +------------------------------+              |  |                                                            |  |   
               |  |                                               |  |                                                            |  |      
               |  +-----------------------------------------------+  +------------------------------------------------------------+  |    
               |                                                                                                                     |    
               +---------------------------------------------------------------------------------------------------------------------+

En observant le schéma ci-dessus, on note qu’une Contract Operation comporte des éléments se rapportant au New State et d’autres qui font référence à l’Old State mis à jour.

Les éléments du New State sont :

L’Old State est référencé via :

Par ailleurs, une Contract Operation inclut des champs plus généraux, propres à l’opération :

Enfin, tous ces champs sont condensés par un procédé de hachage personnalisé, afin de produire une empreinte unique, l’OpId. Cet OpId est ensuite intégré au Transition Bundle, ce qui permettra de l’authentifier et de le valider au sein du protocole.

Chaque Contract Operation est donc identifiée par un hash de 32 octets nommé OpId. Ce hash est calculé par un hachage SHA256 de l’ensemble des éléments composant l’opération. Autrement dit, chaque Contract Operation dispose de son propre engagement cryptographique, qui inclut toutes les données permettant de vérifier l’authenticité et la cohérence de l’opération.

Un contrat RGB est ensuite identifié par un ContractId, dérivé de l’OpId de la Genesis (puisqu’il n’y a pas d’opération antérieure à la Genesis). Concrètement, on prend l’OpId de la Genesis, on en inverse l’ordre des octets et on applique un encodage Base58. Cet encodage rend le ContractId plus facilement manipulable et reconnaissable.

Méthodes et règles de mise à jour de l’état

Le Contract State représente l’ensemble des informations que le protocole RGB doit suivre pour un contrat donné. Il se compose :

RGB-Bitcoin

Le Global State est directement inclus dans la Contract Operation sous la forme d’un bloc unique. Les Owned States sont, eux, définis dans chaque Assignment, à côté de la Seal Definition.

Une particularité majeure de RGB est la manière dont on modifie le Global State et les Owned States. On distingue généralement deux comportements :

Si, dans le contrat, un élément d’état n’est pas défini comme mutable ou cumulatif, cet élément restera vide pour les opérations ultérieures (autrement dit, il n’y a aucune nouvelle version pour ce champ). C'est le Schema du contrat (c’est-à-dire la logique métier codée) qui détermine si un état (Global ou Owned) est mutable, cumulatif ou fixe. Une fois la Genesis définie, ces propriétés ne peuvent être modifiées que si le contrat lui-même l’autorise, par exemple via une State Extension spécifique.

Le tableau ci-dessous illustre comment chaque type de Contract Operation peut manipuler (ou non) le Global State et l’Owned State :

GenesisState ExtensionState Transition
Ajout de Global State+-+
Mutation de Global Staten/a-+
Ajout de Owned State+-+
Mutation de Owned Staten/aNon+
Ajout de Valencies+++

+ : action possible si le Schema du contrat le permet. - : l’opération doit être confirmée par une State Transition ultérieure (la State Extension, seule, ne ferme pas le Single-use Seal).

Par ailleurs, on peut distinguer la portée temporelle et les droits de mise à jour de chaque type de données dans le tableau suivant :

MetadataGlobal StateOwned State
PortéeDéfini pour une seule Contract OperationDéfini globalement pour le contratDéfini pour chaque sceau (Assignment)
Qui peut le mettre à jour ?Non réactualisable (données éphémères)Opération émise par les acteurs (issuer, etc.)Dépend du détenteur légitime qui possède le sceau (celui qui peut le dépenser dans une transaction suivante)
Portée temporelleJuste pour l’opération en coursL’état est établi à l’issue de l’opérationL’état est défini avant l’opération (par la Seal Definition de l’opération précédente)

Global State

Le Global State se décrit souvent par la formule : "personne ne possède, tout le monde sait". Il contient des informations générales sur le contrat, visibles publiquement. Par exemple, dans un contrat d’émission de jetons, on y retrouve potentiellement :

Ce Global State peut être placé sur des ressources publiques (sites web, IPFS, Nostr, Torrent, etc.) et diffusé auprès de la communauté. Aussi, l’incitation économique (le besoin de détenir et de transférer ces tokens, etc.) pousse naturellement les utilisateurs du contrat à maintenir eux-mêmes et à propager ces données.

Assignments

L’Assignment est la structure de base permettant de définir :

On peut voir un Assignment comme l’analogue d’une sortie de transaction Bitcoin, mais avec plus de flexibilité. C’est ici que réside la logique de transfert de propriété : l’Assignment associe un type particulier d’actif ou de droit (AssignmentType) à un sceau. Quiconque possède la clé privée de l’UTXO lié à ce sceau (ou qui peut dépenser cet UTXO) est considéré comme le propriétaire de cet Owned State.

Une des grandes forces de RGB réside dans la possibilité de révéler (reveal) ou de cacher (conceal) à volonté les champs du Seal Definition et de l’Owned State. Cela offre une combinaison poussée de confidentialité et de sélectivité. Par exemple, on peut prouver qu’une transition est valide sans divulguer la totalité des données, dès lors qu’on fournit la version révélée à celui qui doit la valider, tandis que les tiers ne voient que la version cachée (un hash). Dans la pratique, l’OpId d’une transition est toujours calculé à partir des données cachées (concealed).

RGB-Bitcoin

Seal Definition

La Seal Definition, dans sa forme révélée, comporte quatre champs de base : txptr, vout, blinding et method :

La forme cachée (concealed) de la Seal Definition est un hash SHA256 (tagged) de la concaténation de ces 4 champs, avec un tag spécifique à RGB.

RGB-Bitcoin

Owned States

Le second composant de l’Assignment est l’Owned State. Contrairement au Global State, il peut exister sous forme publique ou privée :

RGB définit quatre types d’état (StateTypes) possibles pour un Owned State :

SHA-256(SHA-256(tag_data) || SHA-256(tag_data) || blob)

Avec par exemple :

tag_data = urn:lnp-bp:rgb:state-data#2024-02-12
SHA-256(SHA-256(tag_attachment) || SHA-256(tag_attachment) || file_hash || media_type || salt)

Avec par exemple :

tag_attachment = urn:rgb:state-attach#2024-02-12

Pour résumer, voici les 4 types d'états possibles dans la forme publique et cachée :

  State                      Concealed form                              Revealed form

+---------------------------------------------------------------------------------------------------------

                     +--------------------------------------------------------------------------------+
                     |                                                                                |
  Declarative        |                              < void >                                          |
                     |                                                                                |
                     +--------------------------------------------------------------------------------+

+---------------------------------------------------------------------------------------------------------

                     +--------------------------+             +---------------------------------------+
                     | +----------------------+ |             |         +--------+ +----------+       |
  Fungible           | | Pedersen Commitement | | <========== |         | Amount | | Blinding |       |
                     | +----------------------+ |             |         +--------+ +----------+       |
                     +--------------------------+             +---------------------------------------+

+---------------------------------------------------------------------------------------------------------

                     +--------------------------+             +---------------------------------------+
                     | +----------------------+ |             |         +--------------------+        |
  Structured         | |     Tagged Hash      | | <========== |         |     Data Blob      |        |
                     | +----------------------+ |             |         +--------------------+        |
                     +--------------------------+             +---------------------------------------+

+---------------------------------------------------------------------------------------------------------

                     +--------------------------+             +---------------------------------------+
                     | +----------------------+ |             | +-----------+ +------------+ +------+ |
  Attachments        | |     Tagged Hash      | | <========== | | File Hash | | Media Type | | Salt | |
                     | +----------------------+ |             | +-----------+ +------------+ +------+ |
                     +--------------------------+             +---------------------------------------+

ÉlémentDéclaratifFongibleStructuréPièces jointes
DonnéesAucuneEntier signé ou non signé de 64 bitsTout type de données strictesTout fichier
Type d'infoAucuneSigné ou non signéTypes strictsType MIME
ConfidentialitéNon requisePedersen commitmentHachage avec blindingIdentifiant de fichier haché
Limites de tailleN/A256 octetsJusqu’à 64 KoJusqu’à ~500 Go

Inputs

Les Inputs d’une Contract Operation font référence aux Assignments qui sont en train d’être dépensés dans cette nouvelle opération. Un Input indique :

Les Inputs n’apparaissent jamais dans la Genesis, puisqu’il n’y a pas d’Assignments antérieurs. Ils n’apparaissent pas non plus dans les State Extensions (car ces dernières ne ferment pas de sceau ; elles redéfinissent plutôt de nouveaux sceaux en se basant sur des Valencies).

Lorsqu’on a des Owned States de type Fungible, la logique de validation (via l’AluVM script prévu dans le Schema) vérifie la cohérence des sommes : la somme de jetons entrants (Inputs) doit être égale à la somme de jetons sortants (dans les nouveaux Assignments).

Metadata

Le champ Metadata peut aller jusqu’à 64 KiB et sert à inclure des données temporaires utiles à la validation, mais sans être intégrées dans l’état permanent du contrat. Par exemple, on peut y stocker des variables de calcul intermédiaires pour des scripts complexes. Cet espace n’est pas destiné à être conservé dans l’historique global, ce qui explique pourquoi il se trouve hors du périmètre des Owned States ou du Global State.

Valencies

Les Valencies sont un mécanisme original du protocole RGB. On peut les rencontrer dans la Genesis, les State Transitions ou les State Extensions. Elles représentent des droits numériques activables par une State Extension (via des Redeems), puis finalisés par une Transition ultérieure. Chaque Valency est identifiée par un ValencyType (16 bits). Sa sémantique (droit de réémission, swap de jetons, droit de burn, etc.) est définie dans le Schema.

Concrètement, on peut imaginer qu’une Genesis définisse une valency "droit de réémission". Une State Extension viendra la consommer (Redeem) si certaines conditions sont remplies, afin d’introduire une nouvelle quantité de jetons. Puis, une State Transition émanant du détenteur du sceau ainsi créé pourra transférer ces nouveaux jetons.

Redeems

Les Redeems sont l’équivalent, pour les Valencies, de ce que sont les Inputs pour les Assignments. Ils n’apparaissent que dans les State Extensions, car c’est là qu’on active une Valency préalablement définie. Un Redeem comporte deux champs :

Exemple : un Redeem peut correspondre à une exécution de CoinSwap, suivant ce qui était codé dans la Valency.

Caractéristiques de l'état RGB

Nous allons maintenant étudier plusieurs caractéristiques fondamentales de l’état dans RGB. Nous allons notamment voir ce que sont :

Comme à chaque fois, gardez à l’esprit que tout ce qui concerne l’état du contrat est validé côté client selon des règles de consensus énoncées dans le protocole, et dont l’ultime référence cryptographique est ancrée dans des transactions Bitcoin.

Strict Type System

RGB utilise un Strict Type System et un mode de sérialisation déterministe (Strict Encoding). Cette organisation est conçue pour garantir une reproductibilité et une précision parfaite dans la définition, la manipulation et la validation des données du contrat.

Dans de nombreux environnements de programmation (JSON, YAML…), la structure des données peut être flexible, voire trop permissive. Dans RGB, au contraire, la Structure et les Types de chaque champ sont définis avec des contraintes explicites. Ainsi :

Grâce à ce protocole d’encodage strict :

En pratique, la structure (Schema) et le code qui en découle (Interface et logique associée) sont compilés. Il existe un langage descriptif qui précise la définition du contrat (types, champs, règles) et génère un format binaire strict. À la compilation, on obtient :

Le système de types stricts permet aussi de faire un suivi précis des évolutions : toute modification de la structure (même un changement de nom de champ) est détectable et peut entraîner un changement de l’empreinte globale.

Enfin, chaque compilation produit une empreinte, un identifiant cryptographique qui atteste de la version exacte du code (données, règles, validation). Par exemple un identifiant de la forme :

BEiLYE-am9WhTW1-oK8cpvw4-FEMtzMrf-mKocuGZn-qWK6YF#ginger-parking-nirvana

Cela permet de gérer les mises à jour de consensus ou d’implémentation, tout en assurant une traçabilité fine des versions employées dans le réseau.

Pour éviter que l’état d’un contrat RGB ne devienne trop lourd à valider côté client, une règle de consensus impose une taille maximale de 2^16 octets (64 Kio) pour toute donnée impliquée dans les calculs de validation. Cela concerne chaque variable ou structure : pas plus de 65536 octets, ou l’équivalent en nombres (32768 entiers de 16 bits, etc.). Cela concerne également les collections (listes, sets, maps), qui ne peuvent dépasser 2^16 éléments.

Cette limite garantit :

Le paradigme Validation != Ownership

L’une des innovations majeures de RGB est la séparation stricte entre deux concepts :

La Validation se fait au niveau de la pile logicielle RGB (les bibliothèques, le protocole de commitments, etc.). Son rôle est de s’assurer que les règles internes au contrat (montants, permissions, etc.) sont bien respectées. Les observateurs ou les autres participants peuvent aussi valider l’historique des données.

L’Ownership, elle, repose totalement sur la sécurité de Bitcoin. Posséder la clé privée d’un UTXO, c’est contrôler la capacité de lancer une nouvelle transition (fermer le Single-use Seal). Ainsi, même si quelqu’un peut voir ou valider les données, il ne peut pas modifier l’état s’il ne détient pas l’UTXO concerné.

RGB-Bitcoin

Cette approche limite les vulnérabilités classiques rencontrées dans les blockchains plus complexes (où tout le code d’un smart contract est public et modifiable par n’importe qui, ce qui a parfois mené à des hacks). Sur RGB, un attaquant ne peut pas simplement interagir avec l’état on-chain, car le droit d’agir sur l’état (ownership) est protégé par la couche Bitcoin.

De plus, ce découplage permet à RGB de s’intégrer naturellement avec le Lightning Network. Les canaux Lightning peuvent servir à engager et à déplacer les actifs RGB sans engager à chaque fois les commitments on-chain. Nous étudierons plus précisément cette intégration de RGB sur Lightning dans les prochains chapitres de la formation.

Évolutions de consensus dans RGB

Outre le versioning sémantique du code, RGB inclut un système permettant de faire évoluer ou de mettre à jour les règles de consensus d’un contrat au fil du temps. On distingue deux grandes formes d’évolution :

Un fast-forward survient lorsqu’une règle auparavant non valide devient valide. Par exemple, si le contrat évolue pour autoriser un nouveau type d’AssignmentType ou un nouveau champ :

Un push-back signifie qu’une règle jusque-là valide devient invalide. C'est donc un "durcissement" des règles, mais ce n’est pas à proprement parler un softfork :

Dans ce chapitre consacré aux opérations des contrats RGB, nous avons exploré les principes fondamentaux qui sous-tendent ce protocole. Comme vous l'avez remarqué, la complexité inhérente du protocole RGB implique l'usage de nombreux termes techniques. Ainsi, dans le prochain chapitre, je vous propose un glossaire qui récapitulera tous les concepts abordés dans cette première partie théorique, avec les définitions de tous les termes techniques relatifs à RGB. Ensuite, dans la partie suivante, nous aborderons de manière pratique la définition et l'implémentation des contrats RGB.

Glossaire RGB

En cas de besoin dans la formation, vous pouvez revenir sur ce petit glossaire des termes techniques importants employés dans l'univers RGB (classés par ordre alphabétique). Ce chapitre n'est donc pas indispensable si vous avez déjà bien compris tout ce que nous avons vu dans la première section.

AluVM

L’abréviation AluVM désigne "Algorithmic logic unit Virtual Machine", une machine virtuelle à registres, conçue pour la validation de smart contracts et le calcul distribué. Elle est utilisée (sans y être exclusivement réservée) dans le cadre de la validation des contrats RGB. Les scripts ou les opérations inscrites dans un contrat RGB peuvent ainsi être exécutés dans l’environnement AluVM.
Pour plus d’informations : Site officiel d’AluVM

Anchor

Un Anchor représente un ensemble de données côté client permettant de prouver l’inclusion d’un commitment unique dans une transaction. Dans le protocole RGB, un Anchor est constitué des éléments suivants :

Un Anchor sert donc à établir un lien vérifiable entre une transaction Bitcoin précise et des données privées validées par le protocole RGB. Il garantit que ces données sont bel et bien incluses dans la blockchain, sans pour autant que leur contenu exact soit exposé publiquement.

Assignment

Dans la logique de RGB, un Assignment est l’équivalent d’une sortie de transaction (output) qui modifie, met à jour ou crée certaines propriétés au sein de l’état d’un contract. Un Assignment comporte deux éléments :

Un Assignment indique donc qu’une portion de l’état (par exemple, un actif) est désormais allouée à un détenteur particulier, identifié via un Single-use Seal lié à un UTXO.

Business Logic

La Business Logic regroupe l’ensemble des règles et des opérations internes d’un contrat, décrites par son schéma (c’est-à-dire la structure même du contrat). Elle dicte la manière dont l’état du contrat peut évoluer et sous quelles conditions.

Client-side Validation

La Client-side Validation renvoie au processus par lequel chaque partie (client) vérifie un ensemble de données échangées en privé, selon les règles d’un protocole. Dans le cas de RGB, ces données échangées sont regroupées dans ce qu’on appelle des consignments. Contrairement au protocole Bitcoin qui exige que toutes les transactions soient publiées on-chain, RGB permet de ne stocker en public que des commitments (ancrés dans Bitcoin), tandis que l’essentiel des informations de contrat (transitions, attestations, preuves) reste off-chain, partagé seulement entre les utilisateurs concernés.

Commitment

Un Commitment (au sens cryptographique) est un objet mathématique, noté C, dérivé de façon déterministe à partir d’une opération sur une donnée structurée m (le message) et d’une valeur aléatoire r. On écrit :

C = \text{commit}(m, r)

Ce mécanisme comprend deux opérations principales :

Un commitment doit respecter deux propriétés :

m' : \, | \, : m' \neq m \quad \text{and} \quad r' : \, | \, : r' \neq r \quad 

Tels que :

\text{verify}(m, r, C) = \text{verify}(m', r', C) \rightarrow \text{True}

Dans le protocole RGB, un commitment est inclus dans une transaction Bitcoin afin de prouver l’existence d’une certaine information à un instant donné, sans dévoiler cette information elle-même.

Consignment

Un Consignment regroupe les données échangées entre les parties, soumises à la Client-side Validation dans RGB. Il existe deux grandes catégories de consignments :

Ces consignments ne sont pas enregistrés publiquement dans la blockchain ; ils sont échangés directement entre les parties concernées sur le canal de communication de leur choix.

Contract

Un Contract désigne un ensemble de droits exécutés numériquement entre plusieurs acteurs via le protocole RGB. Il possède un état actif et une logique d’affaires, définie par un Schema, qui précise quelles opérations sont autorisées (transferts, extensions, etc.). L’état d’un contrat, ainsi que les règles de validité, s’expriment dans le Schema. À tout moment, le contrat n’évolue que conformément à ce qui est permis par ce Schema et par les scripts de validation (exécutés, par exemple, dans AluVM).

Contract Operation

Une Contract Operation est une mise à jour de l’état du contrat effectuée selon les règles du Schema. Les opérations suivantes existent dans RGB :

Chaque opération modifie l’état en y ajoutant ou en y remplaçant certaines données (Global State, Owned State…).

Contract Participant

Un Contract Participant est un acteur prenant part aux opérations relatives au contrat. Dans RGB, on distingue :

Contract Rights

Les Contract Rights désignent les différents droits que peuvent exercer les acteurs d’un contrat RGB. Ils se classent en plusieurs catégories :

Contract State

Le Contract State correspond à l’état courant d’un contrat à un instant donné. Il peut être constitué de données à la fois publiques et privées, qui reflète la situation du contrat. Dans RGB, on distingue :

Deterministic Bitcoin Commitment - DBC

Le Deterministic Bitcoin Commitment (DBC) est l’ensemble de règles permettant d’inscrire de manière prouvable et unique un commitment dans une transaction Bitcoin. Dans le protocole RGB, il existe deux formes principales de DBC :

Ces mécanismes définissent précisément comment le commitment est encodé dans les sorties ou dans la structure d’une transaction Bitcoin, afin de s’assurer que cet engagement est repérable et vérifiable de façon déterministe.

Directed Acyclic Graph - DAG

Un DAG (ou Graphe Orienté Acyclique) est un graphe sans cycle, permettant un ordonnancement topologique. Les blockchains, tout comme les shards de contrats RGB, peuvent être représentés par des DAGs.

Pour plus d’informations : Directed Acyclic Graph

Engraving

Un Engraving est une chaîne de données optionnelle que les détenteurs successifs d’un contrat peuvent inscrire dans l’historique du contrat. Cette fonctionnalité existe, par exemple, dans l’interface RGB21 et permet d’ajouter des informations commémoratives ou descriptives dans l’historique du contrat.

Extra Transaction Proof - ETP

L’ETP (Extra Transaction Proof) est la partie de l’Anchor qui renferme les données supplémentaires nécessaires à la validation d’un commitment de type Tapret (dans le contexte de taproot). Elle comprend, entre autres, la clé publique interne du script taproot (internal PubKey) et les informations spécifiques au Script Path Spend.

Genesis

La Genesis désigne l’ensemble des données, régies par un Schema, qui forment l’état initial de tout contrat dans RGB. On peut la rapprocher du concept de Genesis Block (le bloc originel) sur Bitcoin, ou bien au concept de transaction Coinbase, mais ici au niveau client-side et des jetons RGB.

Global State

Le Global State est l’ensemble des propriétés publiques contenues dans l’état d’un contrat (Contract State). Il est défini lors de la Genesis et peut être, selon les règles du contrat, mis à jour par des transitions autorisées. Contrairement aux Owned States, le Global State n’appartient pas à une entité particulière ; il est plus proche d’un registre public dans le cadre du contrat.

Interface

L’Interface est l’ensemble des instructions qui permettent de décoder les données binaires compilées dans un Schema ou dans des opérations de contrat et leurs états, afin de les rendre lisibles pour l’utilisateur ou son wallet. Elle agit comme une couche d’interprétation.

Interface Implementation

L’Interface Implementation est l’ensemble des déclarations qui relient une Interface à un Schema. Elle rend possible la traduction sémantique opérée par l’Interface elle-même, afin que les données brutes d’un contrat soient compréhensibles par l’utilisateur ou les logiciels impliqués (les wallets).

Invoice

Une Invoice prend la forme d’une URL encodée en base58, qui embarque les données nécessaires à la construction d’une State Transition (par le payeur). En d’autres termes, c’est une facture permettant à la contrepartie (payer) de créer la transition correspondante pour transférer l’actif ou mettre à jour l’état du contrat.

Lightning Network

Le Lightning Network est un réseau décentralisé de canaux de paiements (ou state channels) sur Bitcoin, constitué de portefeuilles multi-signatures 2/2. Il permet de faire des transactions off-chain rapides et peu coûteuses, tout en s’appuyant sur la couche 1 de Bitcoin pour l’arbitrage (ou la fermeture) lorsque nécessaire.

Pour plus d’informations sur le fonctionnement de Lightning, je vous conseille de suivre cette autre formation :

https://planb.network/courses/34bd43ef-6683-4a5c-b239-7cb1e40a4aeb

Multi Protocol Commitment - MPC

Le Multi Protocol Commitment (MPC) désigne la structure d'arbre de Merkle utilisée dans RGB pour inclure, au sein d’une unique transaction Bitcoin, plusieurs Transition Bundles issus de contrats différents. L’idée est de regrouper plusieurs engagements (correspondant potentiellement à différents contrats ou différents actifs) dans un seul point d’ancrage afin d’optimiser l’occupation de l’espace de bloc.

Owned State

Un Owned State est la partie de l’état d’un contrat (Contract State) qui est enfermée dans un Assignment et associée à un détenteur particulier (via un Single-use Seal pointant vers un UTXO). Cela représente, par exemple, un actif numérique ou un droit contractuel spécifique attribué à cette personne.

Ownership

Le terme Ownership renvoie à la capacité de contrôler et de dépenser un UTXO référencé par une Seal Definition. Lorsqu’un Owned State est lié à un UTXO, le propriétaire de cet UTXO a le droit, potentiellement, de transférer ou de faire évoluer l’état associé, selon les règles du contrat.

Partially Signed Bitcoin Transaction - PSBT

Une PSBT (Partially Signed Bitcoin Transaction) est une transaction Bitcoin qui n’est pas encore complètement signée. Elle peut être partagée entre plusieurs entités, chacune pouvant y ajouter ou y vérifier certains éléments (signatures, scripts…), jusqu’à ce que la transaction soit jugée prête pour la diffusion on-chain.

Pour plus d’informations : BIP-0174

Pedersen commitment

Un Pedersen commitment est un type d'engagement cryptographique présentant la propriété d’être homomorphique vis-à-vis de l’opération d’addition. Cela signifie qu’il est possible de valider la somme de deux engagements sans dévoiler les valeurs individuelles.

Formellement, si :

C1=\text{commit}(m1,r1) \quad C2=\text{commit}(m2,r2)

alors :

C3=C1⋅C2=\text{commit}(m1+m2, r1+r2)

Cette propriété devient utile, par exemple, pour dissimuler les montants de tokens échangés tout en pouvant vérifier les totaux.

Pour plus d’informations : Pedersen commitment

Redeem

Dans une State Extension, un Redeem fait référence à l’action de récupérer (ou d’exploiter) une Valency précédemment déclarée. Une Valency étant un droit public, le Redeem permet à un participant autorisé de réclamer une extension spécifique de l’état du contrat.

Schema

Un Schema dans RGB est un morceau de code déclaratif décrivant l’ensemble des variables, règles et logiques d’affaires (Business Logic) qui régissent le fonctionnement d’un contrat. Le Schema définit la structure de l’état, les types de transitions autorisées et les conditions de validation.

Seal Definition

La Seal Definition est la partie d’un Assignment qui associe le commitment à un UTXO possédé par le nouveau détenteur. Elle indique, en d’autres termes, où se trouve l’état (dans quel UTXO) et permet d’établir la propriété d’un actif ou d’un droit.

Shard

Un Shard représente une branche dans le DAG de l’historique des State Transitions d’un contrat RGB. Autrement dit, c’est un sous-ensemble cohérent de l’historique global du contrat, correspondant par exemple à la séquence de transitions nécessaires pour prouver la validité d’un actif donné depuis la Genesis.

Single-use Seal

Un Single-use Seal est une promesse d'engagement cryptographique sur un message encore inconnu, qui sera révélé une seule fois à l’avenir et qui doit être connu de tous les membres d’une audience spécifique. L’objectif est d’empêcher la création de multiples engagements concurrents pour le même sceau.

Stash

Le Stash est l’ensemble des données côté client qu’un utilisateur stocke pour un ou plusieurs contrats RGB, afin de procéder à la validation (Client-side Validation). Cela inclut l’historique des transitions, les consignments, les preuves de validité, etc. Chaque détenteur ne conserve que les parties de l’historique dont il a besoin (shards).

State Extension

Une State Extension est une opération de contrat permettant de redéclencher des mises à jour de l’état via la rédemption de Valencies préalablement déclarées. Pour être effective, une State Extension doit ensuite être refermée par une State Transition (qui actualise l’état final du contrat).

State Transition

La State Transition est l’opération qui fait évoluer l’état d’un contrat RGB vers un nouvel état. Elle peut modifier les données du Global State et/ou des Owned States. Dans la pratique, chaque transition est vérifiée par les règles du Schema et ancrée dans la blockchain Bitcoin via un commitment.

Taproot

Fait référence au format de transactions Segwit v1 de Bitcoin, introduit par le BIP341 et le BIP342. Taproot permet d’améliorer la confidentialité et la flexibilité des scripts, notamment en rendant les transactions plus compactes et plus difficiles à distinguer les unes des autres.

Terminal Consignment - Consignment Endpoint

Le Terminal Consignment (ou Consignment Endpoint) est un transfer consignment comprenant l’état final du contrat qui intègre la State Transition créée à partir de l’Invoice du destinataire (payee). Il s’agit donc du point d’aboutissement d’un transfert, avec les données nécessaires pour prouver que la propriété ou l’état a bien été transmis.

Transition Bundle

Un Transition Bundle est un ensemble de State Transitions RGB (appartenant au même contrat) qui sont tous engagés dans la même witness transaction Bitcoin. Cela permet de regrouper plusieurs mises à jour ou transferts en un seul ancrage on-chain.

UTXO

Un UTXO (Unspent Transaction Output) Bitcoin est défini par le hachage d’une transaction et l’index de la sortie (vout). On l’appelle aussi parfois un outpoint. Dans le protocole RGB, la référence à un UTXO (via une Seal Definition) permet de localiser l’Owned State, c’est-à-dire la propriété détenue sur la blockchain.

Valency

Une Valency est un droit public ne nécessitant pas de stockage d’état en tant que tel, mais qui peut être racheté via une State Extension. Il s’agit donc d’une forme de possibilité ouverte à tous (ou à certains acteurs), déclarée dans la logique du contrat, afin d’effectuer ultérieurement une extension particulière.

Witness Transaction

La Witness Transaction est la transaction Bitcoin qui ferme le Single-use Seal autour d’un message contenant un Multi Protocol Commitment (MPC). Cette transaction dépense un UTXO ou en crée un, de façon à sceller l’engagement lié au protocole RGB. Elle fait office de preuve on-chain que l’état a été fixé à un instant précis.

Programmation sur RGB

Implémentation des contrats RGB

Dans ce chapitre, nous allons aborder concrètement la manière dont un contrat RGB est défini et mis en place. Nous allons voir quels sont les composants d'un contrat RGB, quels sont leurs rôles et comment ils sont construits.

Les composants d'un contrat RGB

Jusqu’ici, nous avons déjà discuté de la Genesis, qui représente le point de départ d’un contrat, et nous avons vu comment elle s’inscrit dans la logique d’une Contract Operation et de l’état du protocole. La définition complète d’un contrat RGB ne se limite cependant pas à la seule Genesis : elle implique trois composants complémentaires qui, ensemble, forment le cœur de l’implémentation.

Le premier composant est appelé le Schema. Il s’agit d’un fichier décrivant la structure fondamentale et la logique métier (business logic) du contrat. En son sein, on précise les types de données utilisés, les règles de validation, les opérations permises (par exemple : l'émission initiale de tokens, le transferts, des conditions particulières...), bref, l’ossature générale qui dicte le fonctionnement du contrat.

Le deuxième composant est l'Interface. Il se focalise sur la manière dont les utilisateurs (et par extension, les logiciels de portefeuilles) vont interagir avec ce contrat. On y décrit la sémantique, c’est-à-dire la représentation lisible des différents champs et actions. Ainsi, alors que le Schema définit comment le contrat fonctionne techniquement, l’Interface définit comment présenter et exposer ces fonctionnalités : noms des méthodes, affichage des données, etc.

Le troisième composant est l'Interface Implementation, qui vient compléter les deux précédents en étant une sorte de pont entre le Schema et l’Interface. Autrement dit, il associe la sémantique énoncée par l’Interface aux règles sous-jacentes définies dans le Schema. C’est cette implémentation qui va gérer, par exemple, la conversion entre un paramètre saisi dans le wallet et la structure binaire imposée par le protocole, ou encore la compilation des règles de validation en langage machine.

Cette modularité est une caractéristique intéressante dans RGB, car elle permet à différents groupes de développeurs de travailler séparément sur ces aspects (Schema, Interface, Implementation), tant qu’ils suivent les règles de consensus du protocole.

Pour résumer, chaque contrat se compose donc de :

RGB-Bitcoin

Il est important de noter que pour qu’un portefeuille puisse gérer un actif RGB (que ce soit un jeton fongible ou un droit quelconque), il doit disposer de tous ces éléments compilés : Schema, Interface, Interface Implementation et Genesis. Cela se transmet via un contract consignment, c’est-à-dire un paquet de données contenant tout le nécessaire pour valider le contrat côté client.

Afin de mieux clarifier ces notions, voici un tableau récapitulatif comparant les composants d’un contrat RGB à des concepts déjà connus soit en programmation orientée objet (OOP), soit dans l’écosystème Ethereum :

Composant de contrat RGBSignificationÉquivalent OOPÉquivalent Ethereum
GenesisÉtat initial du contratClass constructorContract constructor
SchemaLogique métier du contratClassContract
InterfaceSémantique du contratInterface (Java) / trait (Rust) / protocol (Swift)ERC Standard
Interface ImplementationMapping de la sémantique et la logiqueImpl (Rust) / Implements (Java)Application Binary Interface (ABI)

Dans la colonne de gauche, on retrouve les éléments propres au protocole RGB. Dans la colonne du milieu, on voit la fonction concrète de chaque composant. Puis, dans la colonne "Équivalent OOP", on trouve le terme équivalent dans la programmation orientée objet :

Dans le cadre d’Ethereum, la Genesis se rapproche du contract constructor, le Schema de la définition du contrat, l’Interface d’un standard type ERC-20 ou ERC-721, et l’Interface Implementation de l’ABI (Application Binary Interface), qui spécifie le format des interactions avec le contrat.

L’avantage de la modularité de RGB tient aussi au fait que des parties prenantes différentes peuvent écrire, par exemple, leur propre Interface Implementation, tant qu’elles respectent la logique du Schema et la sémantique de l’Interface. Ainsi, un émetteur pourrait développer un nouveau front-end (Interface) plus convivial, sans modifier la logique du contrat, ou inversement, on pourrait étendre le Schema pour ajouter une fonctionnalité, et fournir une nouvelle version de l’Interface Implementation adaptée, tandis que les anciennes implémentations resteraient valables pour les fonctionnalités de base.

Lorsqu’on compile un nouveau contrat, on génère une Genesis (première étape d’émission ou de distribution de l’actif), ainsi que ses composants (Schema, Interface, Interface Implementation). Après cela, le contrat est pleinement opérationnel et peut être propagé aux wallets et aux utilisateurs. Cette méthode, où la Genesis se combine à ces trois composants, garantit à la fois un haut degré de personnalisation (chaque contrat peut avoir sa propre logique), de décentralisation (chacun peut contribuer à un composant donné), et de sécurité (la validation demeure strictement définie par le protocole, sans dépendre d’un code arbitraire on-chain comme c’est souvent le cas sur d’autres blockchains).

Maintenant, je vous propose de découvrir plus en détail chacun de ces composants : le Schema, l’Interface et l’Interface Implementation.

Schema

Dans la section précédente, nous avons vu que dans l’écosystème RGB, un contrat est composé de plusieurs éléments : la Genesis, qui instaure l’état initial, et plusieurs autres composants complémentaires. Le but du Schema est de décrire de manière déclarative toute la logique métier (business logic) du contrat, c’est-à-dire la structure des données, les types utilisés, les opérations permises et leurs conditions. C'est donc un élément très important pour rendre un contrat opérationnel côté client, puisque chaque participant (un wallet, par exemple) doit vérifier que les transitions d’état qu’il reçoit sont conformes à la logique définie dans le Schema.

Le Schema peut être assimilé à une "classe" dans la programmation orientée objet (OOP). De manière générale, il sert de modèle définissant les composants d’un contrat, tels que :

RGB-Bitcoin

Lorsque l’issuer d’un actif sur RGB publie un contrat, il fournit la Genesis et le Schema qui lui est associé. Les utilisateurs ou wallets qui souhaitent interagir avec l’actif récupèrent ce Schema pour comprendre la logique qui sous-tend le contrat et pouvoir vérifier par la suite que les transitions auxquelles ils participeront sont légitimes.

La première étape, pour quiconque reçoit des informations sur un actif RGB (par exemple un transfert de tokens), est de valider ces informations par rapport au Schema. Cela implique d’utiliser la compilation du Schema pour :

Dans la pratique, le Schema n’est pas un code exécutable comme on peut le voir dans des blockchains qui stockent le code on-chain (EVM sur Ethereum). Au contraire, RGB sépare la logique métier (déclarative) du code d’exécution sur la blockchain (qui se limite à des ancrages cryptographiques). Ainsi, le Schema détermine les règles, mais l’application de ces règles se fait hors de la blockchain, chez chaque participant, selon le principe de la Client-side Validation.

Un Schema doit être compilé pour être utilisable par les applications RGB. Cette compilation produit un fichier binaire (par exemple .rgb) ou binaire chiffré (.rgba). Lorsque le wallet importe ce fichier, il sait alors :

Comme expliqué dans les chapitres précédents, le strict type system nous donne un format d’encodage stable et déterministe : toutes les variables, qu’il s’agisse d'Owned States, de Global State ou de Valencies, sont décrites avec précision (taille, borne inférieure et supérieure si nécessaire, type signé ou non signé, etc.). Il est également possible de définir des structures imbriquées, par exemple pour prendre en charge des cas d’usage complexes.

En option, le Schema peut référencer un SchemaId racine qui facilite la réutilisation d’une structure de base existante (un template). On peut ainsi faire évoluer un contrat ou créer des variations (par exemple un nouveau type de token) à partir d’un canevas déjà éprouvé. Cette modularité vise à éviter la recréation de contrats entiers et encourage la standardisation des bonnes pratiques.

Un autre point important est que la logique d’évolution de l’état (transferts, mises à jour, etc.) se trouve décrite dans le Schema sous forme de scripts, de règles et de conditions. Ainsi, si le concepteur du contrat souhaite autoriser une réémission ou imposer un mécanisme de burn (destruction de tokens), il peut spécifier les scripts correspondants pour AluVM dans la partie validation du Schema.

Différence avec les blockchains programmables on-chain

Contrairement à des systèmes comme Ethereum, où le code du smart contract (exécutable) est inscrit dans la blockchain elle-même, RGB stocke le contrat (sa logique) off-chain, sous forme de document déclaratif compilé. Cela implique que :

Utilisation par l’émetteur et par les utilisateurs

Lorsqu’un issuer crée un actif (par exemple un jeton fongible non inflationniste), il prépare :

Ensuite, il met à disposition des utilisateurs le Schema compilé (un fichier .rgb), afin que toute personne recevant un transfert de ce token puisse vérifier localement la cohérence de l’opération. Sans ce Schema, un utilisateur ne saurait pas interpréter les données d’état ou vérifier qu’elles respectent les règles du contrat.

Aussi, lorsqu’un nouveau wallet souhaite prendre en charge un actif, il lui suffit d’intégrer le Schema pertinent. Ce mécanisme permet d’ajouter la compatibilité à de nouveaux types d’actifs RGB, sans changer la base logicielle du wallet de manière invasive : il suffit d’importer le binaire du Schema et de comprendre sa structure.

Le Schema définit donc la logique métier dans RGB. Il liste les règles d’évolution d’un contrat, la structure de ses données (Owned States, Global State, Valencies) et les scripts de validation associés (exécutables par AluVM). Grâce à ce document déclaratif, on sépare clairement la définition d’un contrat (fichier compilé) de l’exécution même des règles (côté client). Ce découplage confère une grande souplesse à RGB, en permettant une large gamme de cas d’usage (jetons fongibles, NFT, contrats plus sophistiqués) tout en évitant la complexité et les failles typiques des blockchains programmables on-chain.

Exemple de Schema

Examinons ensemble un exemple concret de Schema pour un contrat RGB. Il s’agit d’un extrait en Rust issu du fichier nia.rs (initiales de "Non-Inflatable Assets"), qui définit un modèle de jetons fongibles ne pouvant pas être réémis au-delà de leur supply initiale (un actif non inflationniste). On peut considérer ce type de jeton comme l’équivalent, dans l’univers RGB, des ERC20 sur Ethereum, c’est-à-dire des tokens fongibles qui respectent certaines règles de base (par exemple sur les transferts, l’initialisation de la supply, etc.).

Avant de plonger dans le code, il est utile de rappeler la structure générale d’un Schema RGB. On y trouve une série de déclarations encadrant :

RGB-Bitcoin

Le code ci-dessous montre la définition complète du Schema Rust. Nous allons le commenter partie par partie, en suivant les annotations (1) à (9) ci-dessous :

// ===== PART 1: Function Header and SubSchema =====
fn nia_schema() -> SubSchema {
    
    // definitions of libraries and variables

    // ===== PART 2: General Properties (ffv, subset_of, type_system) =====
    Schema {
        ffv: zero!(),
        subset_of: None,
        type_system: types.type_system(),

        // ===== PART 3: Global States =====
        global_types: tiny_bmap! {
            GS_NOMINAL => GlobalStateSchema::once(types.get("RGBContract.DivisibleAssetSpec")),
            GS_DATA => GlobalStateSchema::once(types.get("RGBContract.ContractData")),
            GS_TIMESTAMP => GlobalStateSchema::once(types.get("RGBContract.Timestamp")),
            GS_ISSUED_SUPPLY => GlobalStateSchema::once(types.get("RGBContract.Amount")),
        },

        // ===== PART 4: Owned Types =====
        owned_types: tiny_bmap! {
            OS_ASSET => StateSchema::Fungible(FungibleType::Unsigned64Bit),
        },

        // ===== PART 5: Valencies =====
        valency_types: none!(),

        // ===== PART 6: Genesis: Initial Operations =====
        genesis: GenesisSchema {
            metadata: Ty::<SemId>::UNIT.id(None),
            globals: tiny_bmap! {
                GS_NOMINAL => Occurrences::Once,
                GS_DATA => Occurrences::Once,
                GS_TIMESTAMP => Occurrences::Once,
                GS_ISSUED_SUPPLY => Occurrences::Once,
            },
            assignments: tiny_bmap! {
                OS_ASSET => Occurrences::OnceOrMore,
            },
            valencies: none!(),
        },

        // ===== PART 7: Extensions =====
        extensions: none!(),

        // ===== PART 8: Transitions: TS_TRANSFER =====
        transitions: tiny_bmap! {
            TS_TRANSFER => TransitionSchema {
                metadata: Ty::<SemId>::UNIT.id(None),
                globals: none!(),
                inputs: tiny_bmap! {
                    OS_ASSET => Occurrences::OnceOrMore,
                },
                assignments: tiny_bmap! {
                    OS_ASSET => Occurrences::OnceOrMore,
                },
                valencies: none!(),
            }
        },

        // ===== PART 9: Script AluVM and Entry Points =====
        script: Script::AluVM(AluScript {
            libs: confined_bmap! { alu_id => alu_lib },
            entry_points: confined_bmap! {
                EntryPoint::ValidateGenesis => LibSite::with(FN_GENESIS_OFFSET, alu_id),
                EntryPoint::ValidateTransition(TS_TRANSFER) => LibSite::with(FN_TRANSFER_OFFSET, alu_id),
            },
        }),
    }
}

La fonction nia_schema() renvoie un SubSchema, ce qui indique que ce Schema peut hériter partiellement d’un schéma plus générique. Dans l’écosystème RGB, cette souplesse permet de réutiliser certains éléments standard d’un schema master, puis de définir des règles spécifiques au contrat en question. Ici, on choisit de ne pas activer d’héritage puisque subset_of sera None.

La propriété ffv correspond à la version fast-forward du contrat. Une valeur zero!() indique ici qu’on est à la version 0 ou initiale de ce schéma. Si plus tard on souhaite ajouter de nouvelles fonctionnalités (nouveau type d’opération, etc.), on peut incrémenter cette version pour indiquer un changement de consensus.

La propriété subset_of: None confirme l’absence d’héritage. Le champ type_system fait référence au strict type system déjà défini dans la bibliothèque types. Grâce à cette ligne, on indique que toutes les données employées par le contrat utilisent l’implémentation de sérialisation stricte fournie par la librairie mentionnée.

Dans le bloc global_types, on déclare quatre éléments. On utilise la clé, comme GS_NOMINAL ou GS_ISSUED_SUPPLY, pour les référencer par la suite :

Le mot-clé once(...) signifie que chacun de ces champs ne peut apparaître qu’une seule fois.

Dans owned_types, on déclare OS_ASSET, qui décrit un état fongible. On utilise StateSchema::Fungible(FungibleType::Unsigned64Bit), indiquant que la quantité d’actifs (tokens) est stockée en tant qu’entier non signé de 64 bits. Ainsi, toute transaction enverra un certain montant d’unités de ce jeton, qui sera validé selon cette structure numérique strictement typée.

On indique valency_types: none!(), ce qui signifie qu’il n’y a pas de Valencies dans ce schéma, autrement dit aucun droit spécial ou extra (comme la réémission, un burn conditionnel, etc.). Si un schéma en prévoyait, on les déclarerait dans cette section.

On entre ici dans la partie qui déclare les Contract Operations. La Genesis est décrite par :

C’est ainsi qu’on limite la définition d’émission initiale du jeton : on doit déclarer la supply émise (GS_ISSUED_SUPPLY), plus au moins un détenteur (une Owned State de type OS_ASSET).

Le champ extensions: none!() indique qu’aucune State Extension n’est prévue dans ce contrat. Cela signifie qu’il n’y a pas d’opération servant à racheter un droit numérique (Valency) ou à effectuer une extension de l’état avant une Transition. Tout se fait via la Genesis ou les State Transitions.

Dans transitions, on définit le type d’opération TS_TRANSFER. On explique que :

Cela modélise le comportement d’un transfert basique, qui consomme des tokens sur un UTXO, puis crée de nouveaux Owned States en faveur des destinataires, et donc conserve l’égalité du montant total entre les inputs et les outputs.

Enfin, on déclare un script AluVM (Script::AluVM(AluScript { ... })). Ce script contient :

Ce code de validation est responsable d’appliquer la logique métier. Par exemple, il vérifiera :

En cas de non-respect de ces règles, la transition sera considérée comme invalide.

Cet exemple de Schema de "Non Inflatable Fungible Asset" nous permet de mieux comprendre la structure d’un contrat de token fongible simple sous RGB. On voit clairement la séparation entre la description des données (Global et Owned States), la déclaration des opérations (Genesis, Transitions, Extensions) et l’implémentation de la validation (scripts AluVM). Grâce à ce modèle, un token se comporte comme un jeton fongible classique, mais reste validé côté client et ne dépend pas de l’infrastructure on-chain pour exécuter son code. Seuls des engagements cryptographiques sont ancrés dans la blockchain Bitcoin.

Interface

L'interface est la couche destinée à rendre un contrat lisible et manipulable, tant par des utilisateurs (lecture humaine) que par les portefeuilles (lecture logicielle). L’Interface joue donc un rôle comparable à celui d’une interface dans un langage de programmation orientée objet (Java, Rust trait, etc.), en ce sens qu’elle expose et clarifie la structure fonctionnelle d’un contrat, sans nécessairement dévoiler les détails internes de la logique métier.

Contrairement au Schema, qui est purement déclaratif et compilé en un fichier binaire difficilement exploitable tel quel, l’Interface fournit les clés de lecture nécessaires pour :

RGB-Bitcoin

Grâce à l’Interface, on peut par exemple écrire un code dans un wallet qui, au lieu de manipuler des champs, manipule directement des libellés comme "nombre de tokens", "nom de l’actif", etc. De cette manière, la gestion d’un contrat devient plus intuitive.

Fonctionnement général

Cette méthode dispose de nombreux avantages :

Un même type de contrat peut être pris en charge par une Interface standard, partagée entre plusieurs implémentations de wallets. Cela facilite la compatibilité et la réutilisation du code.

Dans la conception de RGB, le Schema (logique métier) et l’Interface (présentation et manipulation) sont deux entités indépendantes. Les développeurs qui écrivent la logique du contrat peuvent se concentrer sur le Schema, sans se soucier de l’ergonomie ou de la représentation des données, tandis qu’une autre équipe (ou la même, mais sur un autre temps) peut développer l’Interface.

L’Interface peut être modifiée ou complétée après l’émission de l’actif, sans avoir à changer le contrat lui-même. C’est une différence majeure avec certains systèmes de smart contracts on-chain où l’Interface (souvent mêlée au code d’exécution) est figée dans la blockchain.

Un même contrat pourrait être exposé par différentes Interfaces adaptées à des besoins distincts : une Interface simple pour l’utilisateur final, une autre plus avancée pour l’issuer qui doit gérer des opérations complexes de configuration. Le wallet pourra alors choisir quelle Interface importer, selon son usage.

RGB-Bitcoin

En pratique, lorsque le wallet récupère un contrat RGB (via un fichier .rgb ou .rgba), il importe également l’Interface associée, elle aussi compilée. À l’exécution, le wallet peut par exemple :

Différence avec Ethereum et les autres systèmes

Sur Ethereum, l’Interface (décrite via l’ABI, Application Binary Interface) est généralement dérivée d’un code stocké on-chain (le smart contract). Il peut être coûteux ou compliqué de modifier une partie spécifique de l’interface sans toucher au contrat lui-même. Or, RGB repose sur une logique entièrement off-chain, avec des données ancrées en commitments sur Bitcoin. Cette conception rend possible la modification de l’Interface (ou de son implémentation) sans impact sur la sécurité fondamentale du contrat, car la validation des règles métier reste dans le Schema et le code AluVM référencé.

Compilation de l’Interface

Comme pour le Schema, l’Interface est définie dans un code source (souvent en Rust) et compilée en un fichier .rgb ou .rgba. Ce fichier binaire regroupe toutes les informations nécessaires au wallet pour :

Une fois l’Interface importée, le wallet peut donc afficher correctement le contrat et proposer des interactions à l'utilisateur.

Interfaces standardisées par l'association LNP/BP

Dans l’écosystème RGB, une Interface sert donc à donner un sens lisible et manipulable aux données et opérations d’un contrat. L’Interface est ainsi un complément du Schema, qui décrit plutôt la logique métier en interne (types stricts, scripts de validation, etc.). Dans cette section, nous allons découvrir les Interfaces standards développées par l'association LNP/BP pour des types de contrats fréquents (tokens fongibles, NFT...).

Pour rappel, l’idée est que chaque Interface décrit la façon d’afficher et de manipuler un contrat du côté du wallet, en nommant clairement les champs (comme spec, ticker, issuedSupply...) et en définissant les opérations possibles (comme Transfer, Burn, Rename...). Plusieurs Interfaces sont déjà opérationnelles, mais il y en aura de plus en plus à l'avenir.

Quelques interface prêtes à l'emploi

RGB20 est l’Interface destinée aux actifs fongibles, que l’on peut comparer au standard ERC20 d’Ethereum. Elle va cependant plus loin en offrant des fonctionnalités plus étendues :

À titre d’exemple, on peut lier l’Interface RGB20 au Schema Non-Inflatable Asset (NIA), qui impose une supply initiale non inflationniste, ou à d’autres schémas plus évolués selon les besoins.

RGB21 concerne les contrats de type NFT ou plus largement, tout contenu numérique unique, comme la représentation de médias numériques (images, musiques, etc.). En plus de décrire l’émission et le transfert d’un actif unique, elle inclut des fonctionnalités comme :

RGB25 est un standard hybride combinant des aspects fongibles et non-fongibles. Il est destiné aux actifs partiellement fongibles, par exemple de la tokenisation immobilière, où l’on veut fractionner une propriété tout en conservant un lien avec un actif racine unique (autrement dit, on a des morceaux de maison fongibles, liés à une maison qui, elle, n'est pas fongible). Techniquement, on peut relier cette interface au Schema Collectible Fungible Asset (CFA), qui prend en compte la notion de fractionnement tout en traçant l’actif original.

Interfaces en cours de développement

D’autres Interfaces sont envisagées pour des usages plus spécialisés, mais ne sont pas encore disponibles à l’heure actuelle :

Évidemment, selon la date à laquelle vous consultez cette formation, il est possible que ces interfaces soient déjà opérationnelles et accessibles.

Exemple d'Interface

Dans cet extrait de code Rust, on peut voir une Interface RGB20 (actif fongible). Ce code est tiré du fichier rgb20.rs dans la bibliothèque standard RGB. Nous allons l’examiner pour comprendre la structure d’une Interface et la façon dont elle fournit un pont entre, d’un côté, la logique métier (définie dans le Schema) et de l’autre, les fonctionnalités exposées aux wallets et aux utilisateurs.

// ...
fn rgb20() -> Iface {
    let types = StandardTypes::with(rgb20_stl());

    Iface {
        version: VerNo::V1,
        name: tn!("RGB20"),
        global_state: tiny_bmap! {
            fname!("spec") => GlobalIface::required(types.get("RGBContract.DivisibleAssetSpec")),
            fname!("data") => GlobalIface::required(types.get("RGBContract.ContractData")),
            fname!("created") => GlobalIface::required(types.get("RGBContract.Timestamp")),
            fname!("issuedSupply") => GlobalIface::one_or_many(types.get("RGBContract.Amount")),
            fname!("burnedSupply") => GlobalIface::none_or_many(types.get("RGBContract.Amount")),
            fname!("replacedSupply") => GlobalIface::none_or_many(types.get("RGBContract.Amount")),
        },
        assignments: tiny_bmap! {
            fname!("inflationAllowance") => AssignIface::public(OwnedIface::Amount, Req::NoneOrMore),
            fname!("updateRight") => AssignIface::public(OwnedIface::Rights, Req::Optional),
            fname!("burnEpoch") => AssignIface::public(OwnedIface::Rights, Req::Optional),
            fname!("burnRight") => AssignIface::public(OwnedIface::Rights, Req::NoneOrMore),
            fname!("assetOwner") => AssignIface::private(OwnedIface::Amount, Req::NoneOrMore),
        },
        valencies: none!(),
        genesis: GenesisIface {
            metadata: Some(types.get("RGBContract.IssueMeta")),
            global: tiny_bmap! {
                fname!("spec") => ArgSpec::required(),
                fname!("data") => ArgSpec::required(),
                fname!("created") => ArgSpec::required(),
                fname!("issuedSupply") => ArgSpec::required(),
            },
            assignments: tiny_bmap! {
                fname!("assetOwner") => ArgSpec::many(),
                fname!("inflationAllowance") => ArgSpec::many(),
                fname!("updateRight") => ArgSpec::optional(),
                fname!("burnEpoch") => ArgSpec::optional(),
            },
            valencies: none!(),
            errors: tiny_bset! {
                SUPPLY_MISMATCH,
                INVALID_PROOF,
                INSUFFICIENT_RESERVES
            },
        },
        transitions: tiny_bmap! {
            tn!("Transfer") => TransitionIface {
                optional: false,
                metadata: None,
                globals: none!(),
                inputs: tiny_bmap! {
                    fname!("previous") => ArgSpec::from_non_empty("assetOwner"),
                },
                assignments: tiny_bmap! {
                    fname!("beneficiary") => ArgSpec::from_non_empty("assetOwner"),
                },
                valencies: none!(),
                errors: tiny_bset! {
                    NON_EQUAL_AMOUNTS
                },
                default_assignment: Some(fname!("beneficiary")),
            },
            tn!("Issue") => TransitionIface {
                optional: true,
                metadata: Some(types.get("RGBContract.IssueMeta")),
                globals: tiny_bmap! {
                    fname!("issuedSupply") => ArgSpec::required(),
                },
                inputs: tiny_bmap! {
                    fname!("used") => ArgSpec::from_non_empty("inflationAllowance"),
                },
                assignments: tiny_bmap! {
                    fname!("beneficiary") => ArgSpec::from_many("assetOwner"),
                    fname!("future") => ArgSpec::from_many("inflationAllowance"),
                },
                valencies: none!(),
                errors: tiny_bset! {
                    SUPPLY_MISMATCH,
                    INVALID_PROOF,
                    ISSUE_EXCEEDS_ALLOWANCE,
                    INSUFFICIENT_RESERVES
                },
                default_assignment: Some(fname!("beneficiary")),
            },
            tn!("OpenEpoch") => TransitionIface {
                optional: true,
                metadata: None,
                globals: none!(),
                inputs: tiny_bmap! {
                    fname!("used") => ArgSpec::from_required("burnEpoch"),
                },
                assignments: tiny_bmap! {
                    fname!("next") => ArgSpec::from_optional("burnEpoch"),
                    fname!("burnRight") => ArgSpec::required()
                },
                valencies: none!(),
                errors: none!(),
                default_assignment: Some(fname!("burnRight")),
            },
            tn!("Burn") => TransitionIface {
                optional: true,
                metadata: Some(types.get("RGBContract.BurnMeta")),
                globals: tiny_bmap! {
                    fname!("burnedSupply") => ArgSpec::required(),
                },
                inputs: tiny_bmap! {
                    fname!("used") => ArgSpec::from_required("burnRight"),
                },
                assignments: tiny_bmap! {
                    fname!("future") => ArgSpec::from_optional("burnRight"),
                },
                valencies: none!(),
                errors: tiny_bset! {
                    SUPPLY_MISMATCH,
                    INVALID_PROOF,
                    INSUFFICIENT_COVERAGE
                },
                default_assignment: None,
            },
            tn!("Replace") => TransitionIface {
                optional: true,
                metadata: Some(types.get("RGBContract.BurnMeta")),
                globals: tiny_bmap! {
                    fname!("replacedSupply") => ArgSpec::required(),
                },
                inputs: tiny_bmap! {
                    fname!("used") => ArgSpec::from_required("burnRight"),
                },
                assignments: tiny_bmap! {
                    fname!("beneficiary") => ArgSpec::from_many("assetOwner"),
                    fname!("future") => ArgSpec::from_optional("burnRight"),
                },
                valencies: none!(),
                errors: tiny_bset! {
                    NON_EQUAL_AMOUNTS,
                    SUPPLY_MISMATCH,
                    INVALID_PROOF,
                    INSUFFICIENT_COVERAGE
                },
                default_assignment: Some(fname!("beneficiary")),
            },
            tn!("Rename") => TransitionIface {
                optional: true,
                metadata: None,
                globals: tiny_bmap! {
                    fname!("new") => ArgSpec::from_required("spec"),
                },
                inputs: tiny_bmap! {
                    fname!("used") => ArgSpec::from_required("updateRight"),
                },
                assignments: tiny_bmap! {
                    fname!("future") => ArgSpec::from_optional("updateRight"),
                },
                valencies: none!(),
                errors: none!(),
                default_assignment: Some(fname!("future")),
            },
        },
        extensions: none!(),
        error_type: types.get("RGB20.Error"),
        default_operation: Some(tn!("Transfer")),
        type_system: types.type_system(),
    }
}

Dans cette interface, on remarque des similarités avec la structure du Schema : on retrouve une déclaration de Global State, de Owned States, de Contract Operations (Genesis et Transitions), ainsi que de la gestion d’erreurs. Mais l’Interface se concentre sur la présentation et la manipulation de ces éléments pour un wallet ou toute autre application.

La différence avec le Schema réside dans la nature des types. Dans le Schema, on utilise des types stricts (comme FungibleType::Unsigned64Bit) et des identifiants plus techniques. Dans l’Interface, on utilise plutôt des noms de champs, des macros (fname!(), tn!()), et des références à des classes d’arguments (ArgSpec, OwnedIface::Rights...). Il s’agit ici de faciliter la compréhension fonctionnelle et l’organisation des éléments pour le wallet.

De plus, l’Interface peut introduire des fonctionnalités supplémentaires par rapport au schéma de base (par exemple, la gestion d’un droit de burnEpoch), tant que celles-ci restent cohérentes avec la logique finale validée côté client. La section "script" AluVM dans le Schema assurera la validité cryptographique, tandis que l’Interface décrit comment l’utilisateur (ou le wallet) interagit avec ces états et transitions.

Global State et Assignments

Dans la section global_state, on retrouve des champs tels que spec (description de l’actif), data, created, issuedSupply, burnedSupply, replacedSupply. Ce sont des champs que le wallet peut lire et présenter. Par exemple :

Dans la section assignments, on définit différents rôles ou droits. Par exemple :

Le mot-clé public ou private (par exemple AssignIface::public(...)) indique si ces états sont visibles (public) ou confidentiels (private). Quant à Req::NoneOrMore, Req::Optional, ils indiquent l’occurrence attendue.

Genesis et transitions

La partie genesis décrit la manière dont est initialisé l’actif :

Ensuite, pour chaque Transition (Transfer, Issue, Burn…), l’Interface définit quels champs l’opération attend en entrée, quels champs l’opération va produire en sortie, et les éventuelles erreurs qui peuvent survenir. Par exemple :

Transition Transfer :

Transition Issue :

Transition Burn :

Chaque opération est donc décrite d’une façon lisible pour un wallet. Cela permet d’afficher une interface graphique où l’utilisateur voit clairement : "Vous avez un droit de burn. Souhaitez-vous brûler un certain montant ?". Le code sait qu’il faut renseigner un champ burnedSupply et vérifier que le burnRight est valide.

Pour résumer, il faut garder à l’esprit qu’une Interface, aussi complète soit-elle, ne définit pas à elle seule la logique interne du contrat. Le cœur du travail est fait par le Schema, qui comprend les types stricts, la structure de la Genesis, les transitions, etc. L’Interface se contente en quelque sorte d’exposer ces éléments de manière plus intuitive et nommée, pour un usage dans une application.

Grâce à la modularité de RGB, on peut ainsi faire évoluer l’Interface (par exemple, ajouter une transition Rename, corriger l’affichage d’un champ...) sans devoir réécrire tout le contrat. Les utilisateurs de cette Interface peuvent alors bénéficier immédiatement de ces améliorations, dès qu’ils mettent à jour le fichier .rgb ou .rgba.

Mais après avoir déclaré une Interface, il faut la relier au Schema correspondant. Cette correspondance s’effectue via l’Interface Implementation, qui indique comment mapper chaque champ nommé (comme par exemple fname!("assetOwner")) à l’ID strict (comme par exemple OS_ASSET) défini dans le Schema. Cela permet par exemple de s’assurer que, lorsqu’un wallet manipule un champ burnRight, il s’agit bien de l’état qui, dans le Schema, décrit la capacité de brûler des tokens.

Interface Implementation

Dans l’architecture RGB, nous avons vu que chaque composant (Schema, Interface, etc.) peut être développé et compilé indépendamment. Il reste toutefois un élément indispensable pour relier ces différentes briques : l’Interface Implementation. C’est elle qui fait le mappage explicite entre les identifiants ou les champs définis dans le Schema (côté logique métier) et les noms déclarés dans l’Interface (côté présentation et interaction utilisateur). Ainsi, lorsqu’un wallet charge un contrat, il peut comprendre précisément quel champ correspond à quoi, et comment une opération nommée dans l’Interface se rattache à la logique du Schema.

Un point important est que l’Interface Implementation n’a pas forcément vocation à exposer toutes les fonctionnalités d’un Schema, ni tous les champs d’une Interface : elle peut se limiter à un sous-ensemble. En pratique, cela permet de brider ou filtrer certains aspects du Schema. Par exemple, on pourrait avoir un Schema avec quatre types d’opérations, mais une Interface Implementation qui n’en mappe que deux dans un contexte donné. Inversement, si une Interface propose des endpoints supplémentaires, on peut choisir de ne pas les implémenter ici.

Voici un exemple classique d’Interface Implementation, où l’on associe un Schema Non-Inflatable Asset (NIA) à l’Interface RGB20 :

fn nia_rgb20() -> IfaceImpl {
    let schema = nia_schema();
    let iface = Rgb20::iface();

    IfaceImpl {
        version: VerNo::V1,
        schema_id: schema.schema_id(),
        iface_id: iface.iface_id(),
        script: none!(),
        global_state: tiny_bset! {
            NamedField::with(GS_NOMINAL, fname!("spec")),
            NamedField::with(GS_DATA, fname!("data")),
            NamedField::with(GS_TIMESTAMP, fname!("created")),
            NamedField::with(GS_ISSUED_SUPPLY, fname!("issuedSupply")),
        },
        assignments: tiny_bset! {
            NamedField::with(OS_ASSET, fname!("assetOwner")),
        },
        valencies: none!(),
        transitions: tiny_bset! {
            NamedType::with(TS_TRANSFER, tn!("Transfer")),
        },
        extensions: none!(),
    }
}

Dans cette Interface Implementation :

Le résultat après compilation est un fichier .rgb ou .rgba séparé, destiné à être importé dans le wallet en complément du Schema et de l’Interface. Ainsi, le logiciel sait comment connecter concrètement ce contrat NIA (dont la logique est décrite par son Schema) à l’Interface "RGB20" (qui fournit des noms humains et un mode d’interaction pour des jetons fongibles), en appliquant cette Interface Implementation comme passerelle entre les deux.

Pourquoi séparer l’Interface Implementation ?

La séparation renforce la flexibilité. Un même Schema pourrait avoir plusieurs Interface Implementations distinctes, chacune mappant un ensemble différent de fonctionnalités. Par ailleurs, l’Interface Implementation elle-même peut évoluer ou être réécrite sans nécessiter un changement dans le Schema ni dans l’Interface. On garde donc le principe de modularité de RGB : chaque composant (Schema, Interface, Interface Implementation) peut être versionné et mis à jour de manière indépendante, tant qu’on respecte les règles de compatibilité imposées par le protocole (mêmes identifiants, cohérence des types, etc.).

Dans le cadre d’une utilisation concrète, lorsque le wallet charge un contrat, il doit :

Cette architecture modulaire rend possible des scénarios d’usage tels que :

Dans le chapitre suivant, nous allons étudier comment fonctionne le transfert d'un contrat, et comment sont générées les invoices RGB.

Les transferts de contrat

Dans ce chapitre, nous allons analyser le déroulement d'un transfert de contrat dans l’écosystème RGB. Pour l’illustrer, nous retrouvons Alice et Bob, nos protagonistes habituels, qui désirent échanger un actif RGB. Nous allons également montrer des extraits de commandes issus de l’outil en ligne de commande rgb, afin de voir comment cela fonctionne en pratique.

Comprendre le transfert de contrat RGB

Prenons donc un exemple de transfert entre Alice et Bob. Dans cet exemple, on suppose que Bob commence tout juste à utiliser RGB, tandis qu’Alice détient déjà des actifs RGB dans son wallet. Nous verrons comment Bob installe son environnement, importe le contrat concerné, puis demande à Alice un transfert, et enfin comment Alice réalise la transaction effective sur la blockchain Bitcoin.

1) Installation du wallet RGB

Avant toute chose, Bob doit installer un wallet RGB, c’est-à-dire un logiciel compatible avec le protocole. Celui-ci ne contient au départ aucun contrat. Bob aura également besoin :

Pour rappel, les Owned States dans RGB font référence à des UTXOs Bitcoin. On doit donc toujours être en mesure de gérer et de dépenser des UTXOs dans une transaction Bitcoin qui intègre les engagements cryptographiques (Tapret ou Opret) pointant vers les données RGB.

2) Acquisition des informations sur le contrat

Bob doit ensuite récupérer les données du contrat qui l’intéresse. Ces données peuvent circuler par n’importe quel canal : site web, e-mail, application de messagerie... En pratique, elles sont groupées dans un consignment, c’est-à-dire un petit paquet de données contenant :

RGB-Bitcoin

La taille totale est souvent de l’ordre de quelques kilo-octets, car chaque composant pèse généralement moins de 200 octets. Il peut également être possible de diffuser ce consignment en Base58, via des canaux résistants à la censure (comme Nostr ou via le Lightning Network par exemple), ou sous forme de QR code.

3) Import du contrat et validation

Une fois que Bob a reçu le consignment, il l’importe dans son wallet RGB. Celui-ci va alors :

Bob peut maintenant voir l’actif dans son wallet (même s’il n’en détient pas encore) et comprendre quels sont les champs disponibles, les opérations possibles... Il doit ensuite contacter une personne qui possède effectivement l’actif à transférer. Dans notre exemple, c’est Alice.

Le processus de découverte de qui détient un certain actif RGB s’apparente à la découverte d’un payeur en bitcoins. Les détails de cette mise en relation dépendent des usages (places de marché, canaux de discussion privés, facturation, vente de biens et services, salaire...).

4) Émission d'une invoice

Pour lancer le transfert d’un actif RGB, Bob doit commencer par émettre une invoice. Cette invoice sert à :

Bob utilise l’outil rgb en ligne de commande. Supposons qu’il souhaite 100 unités d’un jeton dont le ContractId est connu, qu’il veut s’appuyer sur Tapret, et qu’il spécifie son UTXO (456e3..dfe1:0) :

bob$ rgb invoice RGB20 100 <ContractId> tapret1st:456e3..dfe1:0

Nous étudierons plus précisément la structure des invoices RGB à la fin de ce chapitre.

5) Transmission de l’invoice

L’invoice générée (par exemple sous forme d'URL : rgb:2WBcas9.../RGB20/100+utxob:...) contient toutes les informations nécessaires pour qu’Alice puisse préparer le transfert. Comme pour le consignment, elle peut être encodée de manière compacte (Base58 ou un autre format) et envoyée via une application de messagerie, e-mail, Nostr...

RGB-Bitcoin

6) Préparation de la transaction côté Alice

Alice reçoit l’invoice de Bob. Elle dispose dans son wallet RGB d’un stash où figure l’actif à transférer. Pour dépenser l’UTXO où se situe l’actif, elle doit commencer par générer une PSBT (Partially Signed Bitcoin Transaction), c’est-à-dire une transaction Bitcoin incomplète, utilisant l’UTXO qu’elle possède :

alice$ wallet construct tx.psbt

Cette transaction de base (non signée pour le moment) servira de support pour ancrer l’engagement cryptographique lié au transfert vers Bob. L’UTXO d’Alice sera ainsi dépensé, et dans la sortie, on placera le commitment Tapret ou Opret pour Bob.

7) Génération du consignment de transfert

Ensuite, Alice construit le terminal consignment (parfois appelé "consignment de transfert") via la commande :

alice$ rgb transfer tx.psbt <invoice> consignment.rgb

Ce nouveau fichier consignment.rgb contient :

À ce stade, la transaction n’est pas encore diffusée sur le réseau Bitcoin. Le consignment est plus volumineux qu’un consignment de base, car il inclut tout l’historique (proof chain) pour prouver la légitimité de l’actif.

8) Bob vérifie et accepte le consignment

Alice transmet ce terminal consignment à Bob. Bob va alors :

bob$ rgb accept consignment.rgb
sig:DbwzvSu4BZU81jEpE9FVZ3xjcyuTKWWy2gmdnaxtACrS
RGB-Bitcoin

9) Option : Bob renvoie une confirmation à Alice (payslip)

Si Bob le souhaite, il peut renvoyer cette signature à Alice. Cela indique :

Ce n’est pas obligatoire, mais cela peut constituer une assurance pour Alice qu’il n’y aura pas de litige par la suite sur ce transfert.

10) Alice signe et publie la transaction

Alice peut alors :

alice$ rgb check <sig>
alice$ wallet sign —publish tx.psbt
RGB-Bitcoin

Une fois confirmée, cette transaction marque la conclusion du transfert. Bob devient le nouveau détenteur de l’actif : il possède désormais un Owned State pointant vers l’UTXO qu’il contrôle, prouvé par la présence de l’engagement dans la transaction.

Pour résumer, voici le processus de transfert complet :

RGB-Bitcoin

Avantages des transferts RGB

Seuls Alice et Bob possèdent la totalité des données de la State Transition. Ils échangent ces informations en dehors de la blockchain, via des consignments. Les engagements cryptographiques dans la transaction Bitcoin ne révèlent pas le type d’actif ni le montant, ce qui garantie une confidentialité bien supérieure à celle des autres systèmes de tokens on-chain.

Bob peut vérifier seul la cohérence du transfert en confrontant le consignment aux anchors dans la blockchain Bitcoin. Il n’a pas besoin d’une validation par un tiers. Alice n’a pas à publier l’historique complet sur la blockchain, ce qui allège la charge sur protocole de base et améliore la confidentialité.

Les échanges complexes (atomic swaps entre BTC et un actif RGB, par exemple) peuvent être réalisés au sein d’une même transaction, ce qui évite l’utilisation de scripts HTLC ou PTLC. Si l’accord n’est pas diffusé, chacun peut réutiliser ses UTXOs autrement.

Schéma récapitulatif d'un transfert

Avant d'étudier plus en détail les invoices, voici un schéma récapitulatif de flux global d’un transfert RGB :

RGB-Bitcoin Le transfert illustre toute la puissance et la souplesse du protocole RGB : un échange privé, validé côté client, ancré de façon minimale et discrète sur la blockchain Bitcoin, et conservant le meilleur de la sécurité du protocole (pas de risque de double dépense). C’est ce qui fait de RGB un écosystème prometteur pour des transferts de valeur plus confidentiels et plus modulables que sur des blockchains programmables on-chain.

Invoices RGB

Dans cette section, nous allons expliquer en détail la façon dont les invoices fonctionnent dans l’écosystème RGB et comment elles permettent de réaliser des opérations (en particulier des transferts) avec un contrat. Nous allons aborder d’abord la question des identifiants utilisés, puis la manière dont ils sont encodés, et enfin la structure d’une invoice exprimée sous forme d’URL (un format assez pratique pour un usage dans les wallets).

Identifiants et encodage

Pour chacun des éléments suivants, on définit un identifiant unique :

Cette unicité est très importante, car chaque composant du système doit pouvoir être distingué. Par exemple, un contrat X ne doit pas être confondu avec un autre contrat Y, et deux interfaces différentes (RGB20 vs. RGB21 par exemple) doivent avoir des identifiants distincts.

Pour que ces identifiants soient à la fois efficaces (peu volumineux) et lisibles, on utilise :

Par exemple, un ContractId pourra être représenté par quelque chose du type :

rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX

Le préfixe rgb: confirme qu’il s’agit d’un identifiant RGB, et non pas d’un lien HTTP ou d’un autre protocole. Grâce à ce préfixe, les wallets savent interpréter correctement la chaîne.

Segmentation de l'identifiant

Les identifiants RGB sont souvent assez longs, car la sécurité sous-jacente (cryptographique) peut nécessiter des champs de 256 bits ou plus. Pour faciliter la lecture et la vérification humaine, on segmente (chunk) ces chaînes en plusieurs blocs séparés par un tiret (-). Ainsi, au lieu d’avoir une longue suite ininterrompue de caractères, on la divise en blocs plus courts. Cette pratique est courante pour les numéros de cartes bancaires ou de téléphones, et elle s’applique également ici pour la facilité de vérification. Ainsi, on peut par exemple annoncer à un utilisateur ou un partenaire : "Vérifie s’il te plaît que le troisième bloc est 9GEgnyMj7", plutôt que de devoir comparer la totalité d’un seul coup. Le dernier bloc sert souvent de checksum, afin d'avoir un système de détection d’erreurs ou de typos.

À titre d’exemple, un ContractId en base58 encodé et segmenté pourrait être :

2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX

Chacun des tirets vient couper la chaîne en sections. Cela n’affecte pas la sémantique du code, seulement sa présentation.

Utilisation des URLs pour les Invoices

Une invoice RGB se présente comme une URL. Cela signifie qu’elle peut être cliquée ou scannée (sous forme de QR code), et qu’un wallet pourra directement l’interpréter pour effectuer une opération. Cette simplicité d’interaction diffère de certains autres systèmes où l’on doit copier-coller divers morceaux de données dans différents champs du logiciel.

Une invoice pour un token fongible (par exemple un jeton RGB20) peut ressembler à ceci :

rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb

Analysons cette URL :

Le fait que tout tienne dans une seule URL facilite la vie de l’utilisateur : un simple clic ou un scan dans le wallet, et l’opération est prête à être exécutée.

On pourrait imaginer des systèmes où l’on emploie un simple ticker (ex. USDT) à la place du ContractId. Cela poserait néanmoins de gros problèmes de confiance et de sécurité : un ticker n’est pas une référence unique (plusieurs contrats peuvent prétendre s’appeler "USDT"). Sur RGB, on veut un identifiant cryptographique unique, sans ambiguïté. D’où l’adoption de la chaîne de 256 bits, encodée en base58 et segmentée. L’utilisateur sait qu’il manipule précisément le contrat dont l’ID est 2WBcas9-yjz... et pas un autre.

Paramètres supplémentaires dans l’URL

On peut également ajouter des paramètres supplémentaires à l’URL, de la même façon qu’avec HTTP, comme par exemple :

rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/100+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb?sig=6kzbKKffP6xftkxn9UP8gWqiC41W16wYKE5CYaVhmEve

Prenons maintenant le cas d’un NFT via l’interface RGB21. On pourrait par exemple avoir :

rgb:7BKsac8-beMNMWA8r-3GEprtFh7-bjzEvGufY-aNLuU4nSN-MRsLOIK/RGB21/DbwzvSu-4BZU81jEp-E9FVZ3xj-cyuTKWWy-2gmdnaxt-ACrS+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb

Ici, on voit :

L’idée est la même : transmettre un lien unique que le wallet sait interpréter, en identifiant clairement l’actif unique à transférer.

Autres opérations via URL

Les URLs RGB ne servent pas uniquement à demander un transfert. Elles peuvent aussi encoder des opérations plus avancées, comme l’émission de nouveaux tokens (issuance). Par exemple :

rgb:2WBcas9-yjzEvGufY-9GEgnyMj7-beMNMWA8r-sPHtV1nPU-TMsGMQX/RGB20/issue/100000+utxob:egXsFnw-5Eud7WKYn-7DVQvcPbc-rR69YmgmG-veacwmUFo-uMFKFb

Ici, on retrouve :

Ainsi, le wallet pourra comprendre : "On me demande d’effectuer une opération issue depuis l’interface RGB20, sur tel contrat, pour 100000 unités, au profit de tel Single-use Seal."

Maintenant que nous avons étudié les principaux éléments liés à la programmation sur RGB, je vous propose, dans le chapitre suivant, de rédiger un contrat RGB.

Rédaction de contrats intelligents

Dans ce chapitre, nous allons suivre pas à pas la rédaction d'un contrat, en utilisant l’outil en ligne de commande rgb. L’objectif est de montrer comment installer et manipuler la CLI, compiler un Schema, importer l’Interface et l’Interface Implementation, puis émettre (issue) un actif. Nous verrons également la logique sous-jacente, avec la compilation et la validation de l’état. À l’issue de ce chapitre, vous devriez être en mesure de reproduire la démarche et de créer vos propres contrats RGB.

Pour rappel, la logique interne de RGB repose sur des bibliothèques Rust que vous, en tant que développeurs, pouvez importer dans vos projets pour gérer la partie Client-side Validation. En complément, l’équipe de l'Association LNP/BP travaille à proposer des bindings pour d’autres langages, mais ce n’est pas encore finalisé. Par ailleurs, d’autres entités comme Bitfinex développent leurs propres stacks d’intégration (nous en parlerons dans les 2 derniers chapitres de la formation). La CLI rgb constitue donc pour l’instant la référence officielle, même si elle reste relativement brute de décoffrage.

Installation et présentation de l’outil rgb

La commande principale se nomme simplement rgb. Elle est conçue de façon à rappeler l’usage de git, avec un ensemble de sous-commandes pour manipuler les contrats, les invoquer, émettre des assets, etc. Actuellement, la partie Bitcoin Wallet n’y est pas intégrée, mais va l’être dans une version imminente (0.11). Cette prochaine version permettra de créer et gérer ses wallets (via des descriptors) directement depuis rgb, y compris la génération de PSBT, la compatibilité avec du matériel externe (par exemple un hardware wallet) pour la signature, ou encore l’interopérabilité avec des logiciels comme Sparrow. Le scénario complet d’émission et de transfert d’actif deviendra ainsi plus simple.

Installation via Cargo

On installe l’outil en Rust avec :

cargo install rgb-contracts --all-features

(Remarque : le crate s’appelle rgb-contracts, et la commande installée sera nommée rgb. S’il existait déjà un crate nommé rgb, il aurait pu y avoir collision, d’où cette dénomination.)

L’installation compile un grand nombre de dépendances (par exemple le parsing de la commande, l’intégration avec Electrum, la gestion des zero-knowledge proofs, etc.).

Une fois l’installation terminée, on dispose de la commande :

rgb

L’exécution de rgb (sans argument) affiche la liste des sous-commandes disponibles, comme interfaces, schema, import, export, issue, invoice, transfer, etc. Il est possible de modifier le répertoire de stockage local (un stash qui conserve les consignments, schémas et implémentations), de choisir le réseau (testnet, mainnet) ou de configurer son Electrum server.

RGB-Bitcoin

Premier aperçu des commandes

Lorsqu’on exécute la commande suivante, on voit qu’une interface RGB20 est déjà intégrée par défaut :

rgb interfaces

Si jamais cette interface n'est pas intégrée, clonez le dépôt des interfaces :

git clone https://github.com/RGB-WG/rgb-interfaces

Compilez-le :

cargo run

Puis importez l'interface de votre choix :

rgb import interfaces/RGB20.rgb
RGB-Bitcoin

En revanche, on nous indique qu’aucun schema n’est encore importé dans le logiciel. Il n’y a pas non plus de contrat dans le stash. Pour le voir, exécutez la commande :

rgb schemata

Vous pouvez alors cloner le dépôt pour récupérer certains schémas :

git clone https://github.com/RGB-WG/rgb-schemata
RGB-Bitcoin

Ce dépôt contient, dans son répertoire src/, plusieurs fichiers Rust (par exemple nia.rs) qui définissent des schémas (NIA pour "Non Inflatable Asset", UDA pour "Unique Digital Asset", etc.). On peut alors exécuter pour compiler :

cd rgb-schemata
cargo run

Cela génère plusieurs fichiers .rgb et .rgba qui correspondent aux schémas compilés. Par exemple, on peut y trouver NonInflatableAsset.rgb.

Import du Schema et de l’Interface Implementation

Vous pouvez maintenant importer le schéma dans rgb :

rgb import schemata/NonInflatableAssets.rgb
RGB-Bitcoin

Cela permet de l'ajouter dans le stash local. Si on exécute la commande suivante, on constate que le schéma apparaît désormais :

rgb schemata

Création d’un contrat (issuing)

Pour créer un nouvel actif, il y a deux approches :

Vous pouvez retrouver des exemples en Rust dans le dossier examples qui illustrent comment on construit un ContractBuilder, on renseigne le global state (nom de l’actif, ticker, supply, date, etc.), on définit l’Owned State (à quel UTXO il est assigné), puis on compile tout cela en un contract consignment qu’on peut exporter, valider et importer dans un stash.

L'autre manière est donc d'éditer manuellement un fichier YAML pour personnaliser le ticker, le name, la supply, etc. Supposons que le fichier s’appelle RGB20-demo.yaml. On peut y spécifier :

Voici un exemple de fichier YAML à créer :

interface: RGB20Fixed

globals:
  spec:
    ticker: PBN
    name: Plan B Network
    details: "Pay attention: the asset has no value"
    precision: 2
  terms:
    text: >
      SUBJECT TO, AND WITHOUT IN ANY WAY LIMITING, THE REPRESENTATIONS AND WARRANTIES OF ANY SELLER. PROPERTY IS BEING SOLD “AS IS”...
    media: ~
  issuedSupply: 100000000

assignments:
  assetOwner:
    seal: tapret1st:b449f7eaa3f98c145b27ad0eeb7b5679ceb567faef7a52479bc995792b65f804:1
    amount: 100000000 # this is 1 million (we have two digits for cents)
RGB-Bitcoin

Ensuite, il suffit d'exécuter la commande :

rgb issue '<SchemaID>' ssi:<Issuer> rgb20-demo.yaml
RGB-Bitcoin

Dans mon cas, l'identifiant unique du schéma (à mettre entre guillemets simples) est RDYhMTR!9gv8Y2GLv9UNBEK1hcrCmdLDFk9Qd5fnO8k et je n'ai mis aucun issuer. Ma commande est donc :

rgb issue 'RDYhMTR!9gv8Y2GLv9UNBEK1hcrCmdLDFk9Qd5fnO8k' ssi:anonymous rgb20-demo.yaml

Si vous ne connaissez pas l'ID du schéma, exécutez la commande :

rgb schemata

La CLI répond qu’un nouveau contrat a été émis et ajouté au stash. Si on tape la commande suivante, on voit qu’il y a désormais un contrat supplémentaire, correspondant à celui qu’on vient d’émettre :

rgb contracts
RGB-Bitcoin

Puis, la commande suivante permet d'afficher les global states (le nom, le ticker, la supply...) et la liste des Owned States, c’est-à-dire les allocations (par exemple, 1 million de token PBN définis dans l'UTXO b449f7eaa3f98c145b27ad0eeb7b5679ceb567faef7a52479bc995792b65f804:1).

rgb state '<ContractId>'
RGB-Bitcoin

Export, import et validation

Pour partager ce contrat avec d’autres utilisateurs, on peut l’exporter depuis le stash vers un fichier :

rgb export '<ContractId>' myContractPBN.rgb
RGB-Bitcoin

Le fichier myContractPBN.rgb peut être transmis à un autre utilisateur, lequel pourra l’ajouter dans son stash avec la commande :

rgb import myContractPBN.rgb

À l’import, si c’est un simple contract consignment, on aura un message "Importing consignment rgb". S’il s’agit d’un state transition consignment plus volumineux, la commande sera différente (rgb accept).

Pour s’assurer de la validité, on peut aussi utiliser la fonctionnalité de validation en local. On pourrait exécuter par exemple :

rgb validate myContract.rgb

Utilisation du stash, vérification et affichage

Pour rappel, le stash est un inventaire en local qui conserve à la fois les schémas, les interfaces, les implémentations et les contrats (Genesis + transitions). Chaque fois qu’on exécute "import", on ajoute un élément au stash. On peut visualiser ce stash en détail avec la commande :

rgb dump
RGB-Bitcoin

Cela va générer un dossier avec le détail de tout le stash.

Transfert et PSBT

Pour réaliser un transfert, il va falloir manipuler un wallet Bitcoin en local pour gérer les engagements Tapret ou Opret.

Générer une invoice

Dans la plupart des cas, l’interaction entre les participants d’un contrat (par exemple Alice et Bob) se fait via la génération d’une invoice. Si Alice souhaite que Bob exécute quelque chose (un transfert de tokens, une réémission, une action dans une DAO, etc.), Alice crée une invoice qui détaille ses instructions à Bob. On a donc :

Contrairement à d’autres écosystèmes, une invoice RGB ne se limite pas à la notion de paiement. Elle peut embarquer n’importe quelle requête liée au contrat : révoquer une clé, voter, créer une gravure (engraving) sur un NFT, etc. L’opération correspondante peut être décrite dans l’interface du contrat.

La commande suivante vous permet de générer une invoice RGB :

$ rgb invoice $CONTRACT -i $INTERFACE $ACTION $STATE $SEAL

Avec :

Par exemple, avec les commandes suivantes :

alice$ CONTRACT='iZgIN9EL-2H21UgQ-x!A3uJc-WwXhCSm-$9Lwcc1-v!mUkKY'

alice$ MY_UTXO=4960acc21c175c551af84114541eace09c14d3a1bb184809f7b80916f57f9ef8:1

alice$ rgb invoice $CONTRACT -i RGB20 --amount 100 $MY_UTXO

La CLI va générer une invoice du style :

rgb:iZgIN9EL-2H21UgQ-x!A3uJc-WwXhCSm-$9Lwcc1-v!mUkKY/RGB20/100+utxob:zlVS28Rb-...

Elle peut être transmise à Bob par n’importe quel canal (texte, QR code, etc.).

Effectuer un transfert

Pour réaliser un transfert à partir de cette invoice :

bob$ rgb transfer tx.psbt $INVOICE consignment.rgb
alice$ rgb accept consignment.rgb
bob$ rgb check <sig> && wallet sign --publish tx.psbt

Dans le prochain chapitre, nous aborderons plus en détail l'intégration de RGB au Lightning Network.

RGB sur le Lightning Network

Dans ce chapitre, je vous propose d'examiner comment RGB peut être utilisé au sein du Lightning Network, afin d’intégrer et de déplacer des actifs RGB (tokens, NFT, etc.) via des canaux de paiement off-chain.

L’idée fondamentale est que la transition d’état RGB (State Transition) peut être engagée dans une transaction Bitcoin qui, elle-même, peut rester off-chain tant que le canal Lightning n’est pas fermé. Ainsi, à chaque mise à jour de canal, on peut incorporer une nouvelle transition d’état RGB dans la nouvelle transaction d'engagement, qui invalide alors l’ancienne transition. De cette façon, des canaux Lightning peuvent servir à transférer des actifs RGB, et on peut les router comme on le ferait pour des paiements Lightning classiques.

Création d'un canal et funding

Pour créer un canal Lightning qui transporte des actifs RGB, on a besoin de deux éléments :

Sur le plan de Bitcoin, la transaction de funding doit exister pour définir l’UTXO de référence, même si elle ne contient qu’une petite quantité de sats (il faut simplement que chaque sortie dans les futures transactions d'engagement restent au-dessus du dust limit tout de même). Par exemple, Alice peut décider de fournir 10k sats et 500 USDT (émis sous forme d’un actif RGB). Sur la transaction de funding, on ajoute un engagement (Opret ou Tapret) qui ancre la transition d’état RGB.

RGB-Bitcoin

Une fois la transaction de funding préparée (mais pas encore diffusée), on crée les transactions d'engagement pour que chaque partie puisse, à tout moment, fermer le canal unilatéralement. Ces transactions ressemblent aux transactions d'engagement classiques de Lightning, à la différence qu’on y ajoute une sortie supplémentaire contenant l’ancre RGB (OP_RETURN ou Taproot) liée à la nouvelle transition d’état.

La transition d’état RGB déplace alors les actifs depuis le multisig 2/2 du funding vers les sorties de la transaction d'engagement. L’avantage ce ce processus est que la sécurité de l’état RGB se cale exactement sur la mécanique punitive de Lightning : si Bob diffuse un ancien état du canal, Alice peut le punir et dépenser la sortie, afin de récupérer à la fois les sats et les tokens RGB. L’incitation est donc plus forte encore que dans un canal Lightning sans actif RGB, puisqu’un attaquant peut perdre non seulement des sats, mais aussi les actifs RGB du canal.

Une transaction d'engagement signée par Alice et envoyée à Bob ressemblera donc à cela :

RGB-Bitcoin

Et la transaction d'engagement qui va de paire, signée par Bob et envoyée à Alice ressemblera à cela :

RGB-Bitcoin

Mise à jour du canal

Lorsqu’un paiement se produit entre deux participants du canal (ou qu’ils souhaitent modifier la répartition des actifs), ils créent une nouvelle paire de transactions d'engagement. Le montant en sats sur chaque sortie peut rester inchangé ou non, selon l’implémentation, car son rôle principal est de permettre la construction d’UTXOs valides. En revanche, la sortie OP_RETURN (ou Taproot) doit être modifiée pour contenir le nouvel ancrage RGB, représentant la nouvelle répartition des actifs dans le canal.

Par exemple, si Alice transfère 30 USDT à Bob dans le canal, la nouvelle transition d’état va refléter un solde de 400 USDT pour Alice et 100 USDT pour Bob. La transaction d'engagement se voit ajouter (ou modifier) l’ancrage OP_RETURN/Taproot pour inclure cette transition. À noter que, du point de vue de RGB, l’input dans la transition reste le multisig initial (où sont réellement alloués les assets on-chain jusqu’à la fermeture du canal). Seules les sorties RGB (allocations) changent, selon la redistribution décidée.

La transaction d'engagement signée par Alice, prête à être diffusée par Bob :

RGB-Bitcoin

La transaction d'engagement signée par Bob, prête à être diffusée par Alice :

RGB-Bitcoin

Gestion des HTLCs

Dans la réalité, le Lightning Network permet le routage de paiements via des canaux multiples, en utilisant des HTLCs (Hashed Time-Locked Contracts). C’est identique avec RGB : pour tout paiement en transit dans le canal, on ajoute une sortie HTLC à la transaction d'engagement et une allocation RGB liée à cet HTLC. Ainsi, celui qui dépense la sortie HTLC (grâce au secret ou après expiration du timelock) récupère à la fois les sats et les actifs RGB associés. En revanche, il faut évidemment avoir sur la route suffisamment de liquidités à la fois en sats et en actif RGB.

RGB-Bitcoin

Le fonctionnement de RGB sur Lightning doit donc être considéré en parallèle avec celui du Lightning Network lui-même. Si vous désirez approfondir ce sujet, je vous recommande vivement de consulter cette autre formation complète :

https://planb.network/courses/34bd43ef-6683-4a5c-b239-7cb1e40a4aeb

Plan du code de RGB

Pour terminer et avant de passer à la section suivante, je voudrais vous faire un récapitulatif du code utilisé sur RGB. Le protocole repose sur un ensemble de librairies Rust et de spécifications open source. Voici un panorama des principaux dépôts et crates :

RGB-Bitcoin

Client-side Validation

Gestion de la validation off-chain et de la logique des Single-use Seals.

Deterministic Bitcoin Commitments (DBC)

Gestion de l’ancrage déterministe dans les transactions Bitcoin (Tapret, OP_RETURN, etc.).

Multi Protocol Commitment (MPC)

Combinaisons d’engagements multiples et intégration avec différents protocoles.

Strict Types & Strict Encoding

Le système de typage strict et la sérialisation déterministe utilisés pour la validation côté client.

RGB Core

Cœur du protocole, qui englobe la logique principale de la validation RGB.

RGB Standard Library & Wallet

Implémentations standard, gestion du stash et wallet.

RGB CLI

La CLI rgb et le crate wallet, qui permettent de manipuler les contrats en ligne de commande.

RGB Schema

Contient des exemples de schémas (NIA, UDA, etc.) et leurs implémentations.

ALuVM

Machine virtuelle registry-based utilisée pour exécuter les scripts de validation.

Bitcoin Protocol - BP

Compléments pour la prise en charge du protocole Bitcoin (transactions, dérivations, etc.).

Ubiquitous Deterministic Computing - UBIDECO

Écosystème lié aux développements déterministes open-source.

Construire sur RGB

DIBA et le projet Bitmask

Cette dernière section de la formation provient de présentations effectuées par différents intervenants lors du bootcamp sur RGB. Elle comprend des témoignages et des réflexions concernant RGB et son écosystème, ainsi que des présentations d'outils et de projets basés sur le protocole. Ce premier chapitre est animé par Hunter Beast et les deux suivants le seront par Frederico Tenga.

De JavaScript à Rust, et l’entrée dans l’écosystème Bitcoin

Au début, Hunter Beast travaillait principalement en JavaScript. Puis il a découvert Rust, dont la syntaxe lui semblait peu attrayante et frustrante au départ. Cependant, il en est venu à apprécier la puissance offerte par ce langage, le contrôle sur la mémoire (heap et stack), ainsi que la sécurité et les performances qui en découlent. Il souligne que Rust constitue une excellente formation pour comprendre en profondeur comment fonctionne un ordinateur.

Hunter Beast raconte son passé dans divers projets de l’écosystème des altcoins, comme Ethereum (avec du Solidity, du TypeScript, etc.), et plus tard Filecoin. Il explique avoir été d’abord impressionné par certains protocoles, mais avoir fini par se sentir désillusionné par la plupart, notamment à cause de leur tokenomics. Il dénonce les incitations financières douteuses, la création inflationniste des tokens qui dilue les investisseurs, et l’aspect possiblement exploitative de ces projets. Il finit donc par adopter une posture de Bitcoin maximaliste, notamment parce que certaines personnes lui ont ouvert les yeux sur les mécanismes économiques plus sains de Bitcoin, et sur la robustesse de ce système.

L’attrait pour RGB et la construction sur des layers

Ce qui l’a définitivement convaincu de la pertinence de Bitcoin, selon ses dires, est la découverte de RGB et du concept de layers. Il considère que les fonctionnalités existantes sur d’autres blockchains pourrait être reproduites sur des couches supérieures, au-dessus de Bitcoin, sans altérer le protocole de base.

En février 2022, il rejoint DIBA pour travailler précisément sur RGB, et en particulier sur le wallet Bitmask. À l’époque, Bitmask en était encore à la version 0.01 et exploitait RGB en version 0.4, uniquement pour la gestion de tokens simples. Il note que c’était moins tourné vers la self-custody qu’aujourd’hui, car la logique reposait en partie sur un serveur. Depuis, l’architecture a évolué vers ce modèle apprécié par les bitcoiners.

Les fondements du protocole RGB

Le protocole RGB est la concrétisation la plus récente et la plus poussée du concept de colored coins, déjà exploré vers 2012-2013. À l’époque, plusieurs équipes cherchaient à associer de la valeur différente du bitcoin sur des UTXOs, ce qui a mené à de multiples implémentations dispersées. Ce manque de standardisation et la faible demande de l’époque ont empêché ces solutions de s’imposer durablement.

RGB se distingue aujourd’hui par sa solidité conceptuelle et ses spécifications unifiées via l’association LNP/BP. Le principe repose sur une validation en dehors de la blockchain (Client-side Validation). La blockchain Bitcoin ne stocke que des engagements cryptographiques (commitments, via Taproot ou OP_RETURN), tandis que la majorité des données (définition des contrats, historiques de transferts, etc.) se conserve chez les utilisateurs concernés. Ainsi, la charge de stockage est répartie et la confidentialité des échanges est renforcée, sans alourdir la blockchain. Cette approche permet la création d’actifs fongibles (standard RGB20) ou d’actifs uniques (standard RGB21), dans un cadre modulaire et évolutif.

La fonction de tokens (RGB20) et d’actifs uniques (RGB21)

Avec RGB20, on définit un jeton fongible sur Bitcoin. L’émetteur choisit un supply, une precision, et crée un contrat dans lequel il peut ensuite effectuer des transferts. Chaque transfert fait référence à un UTXO Bitcoin, qui agit comme un Single-use Seal. Cette logique garantit que l’utilisateur ne pourra pas dépenser le même actif deux fois, puisque seule la personne capable de dépenser l’UTXO détient effectivement la clé permettant d’actualiser l’état du contrat côté client.

RGB21 cible quant à lui les actifs uniques (ou "NFT"). L’actif a une supply de 1, et on peut y associer des métadonnées (fichier image, audio, etc.) décrites via un champ spécifique. Contrairement aux NFT sur des blockchains publiques, les données et leurs identifiants MIME peuvent rester privées, diffusées de pair à pair selon la volonté du propriétaire.

La solution Bitmask : un wallet pour RGB

Pour exploiter concrètement les capacités de RGB, le projet DIBA a conçu un wallet baptisé Bitmask. L’idée est de fournir un outil non custodial, basé sur Taproot, accessible comme une application web ou une extension de navigateur. Bitmask gère à la fois des actifs RGB20 et RGB21, et intègre divers mécanismes de sécurisation :

Grâce à cette architecture, toutes les opérations sur les actifs se font côté client. La transaction Bitcoin n’est, de l’extérieur, qu’une transaction de dépense Taproot classique, dont personne ne peut soupçonner qu’elle véhicule aussi un transfert de tokens fongibles ou de NFT. L’absence de surcharge on-chain (pas de métadonnées stockées publiquement) garantit une certaine discrétion et permet de mieux résister aux éventuelles tentatives de censure.

Sécurité et architecture distribuée

Dans la mesure où le protocole RGB exige que chaque participant conserve son historique de transactions (pour prouver la validité des transferts qu’il reçoit), la question du stockage se pose. Bitmask propose de sérialiser ce stash localement, puis de l’envoyer vers plusieurs serveurs ou clouds (optionnellement). Les données restent chiffrées par l’utilisateur via Carbonado ; ainsi, un serveur ne peut pas lire ces informations. En cas de corruption partielle, la couche de correction d’erreurs peut reconstituer le contenu.

L’usage du CRDT (Conflict-free replicated data type) permet de fusionner différentes versions du stash si jamais elles divergent. Chacun est libre d’héberger ces données où il le souhaite, car aucun full node unique ne porte la totalité des informations liées à l’actif. Cela reflète exactement la philosophie Client-side Validation, où chaque propriétaire est responsable du stockage des preuves de la validité de son actif RGB.

Vers un écosystème plus large : marketplace, interopérabilité et nouvelles fonctionnalités

La société derrière Bitmask ne se limite pas au simple développement d'un wallet. DIBA entend développer :

En parallèle, certains développements portent sur WebBTC ou WebLN (des standards permettant à des sites web de demander au wallet de signer des opérations Bitcoin ou Lightning), ainsi que sur la capacité de "télébrûler" des inscriptions Ordinals (si on veut rapatrier l’Ordinals vers un format RGB plus discret et plus flexible).

Conclusion

L’ensemble de cette démarche montre comment l’écosystème RGB peut être déployé et rendu accessible à des utilisateurs finaux grâce à des solutions techniques robustes. La transition d’une perspective altcoin vers une vision plus centrée sur Bitcoin, couplée à la découverte de la Client-side Validation, illustre un cheminement assez logique : on comprend qu’il est possible d’implémenter des fonctionnalités variées (tokens fongibles, NFT, smart contracts…) sans forker la blockchain, simplement en profitant d’engagements cryptographiques sur des transactions Taproot ou des OP_RETURN.

Le wallet Bitmask s'inscrit dans cette démarche : côté blockchain, on ne voit qu’une transaction Bitcoin banale ; côté utilisateur, on manipule une interface web où l’on crée, échange, et stocke toutes sortes d’actifs off-chain. Ce modèle dissocie clairement l’infrastructure monétaire (Bitcoin) de la logique d’émission et de transferts (RGB), tout en assurant une grande confidentialité et un meilleur passage à l’échelle.

Les travaux de Bitfinex sur RGB

Dans ce chapitre établi sur la présentation de Frederico Tenga, nous étudions un ensemble d’outils et de projets créés par l'équipe de Bitfinex dédiée à RGB, dans l’optique de favoriser l’émergence d’un écosystème riche et diversifié autour de ce protocole. L’équipe n’a pas, au départ, l’objectif de sortir un produit commercial précis ; elle s’emploie plutôt à mettre à disposition des briques logicielles, à contribuer au protocole RGB lui-même, et à proposer des références de mise en œuvre concrètes comme un wallet mobile (Iris Wallet) ou un nœud Lightning compatible RGB.

Contexte et objectifs

Depuis environ 2022, l’équipe Bitfinex spécialisée dans RGB se concentre sur le développement de la pile technologique permettant d’exploiter et de tester efficacement RGB. Plusieurs contributions ont été réalisées :

Cette approche vise à couvrir toute la chaîne de besoins : de la librairie de bas niveau (RGBlib), permettant l’implémentation d’un wallet, jusqu’à l’aspect mise en production (un nœud Lightning, un wallet pour Android, etc.).

La librairie RGBlib : simplifier le développement d’applications RGB

Un point important pour démocratiser la création de wallets et d’applications RGB réside dans la mise à disposition d’une abstraction suffisamment simple pour que les développeurs n’aient pas à tout apprendre de la logique interne du protocole. C’est précisément l’objectif de RGBlib, écrite en Rust.

RGBlib joue le rôle de passerelle entre les exigences très flexibles (mais parfois complexes) de RGB que nous avons pu étudier dans les chapitres précédents, et les besoins concrets d’un développeur d’application. En d’autres termes, un wallet (ou un service) qui souhaite gérer des transferts de tokens, des émissions d’assets, des vérifications, etc., peut s’appuyer sur RGBlib sans connaître chaque détail cryptographique ou chaque paramètre customisable de RGB.

La librairie propose :

RGBlib repose donc sur des notions complexes propres à RGB (Client-side Validation, ancrages Tapret/Opret), mais les encapsule pour que l’application finale n’ait pas à tout reprogrammer ni à prendre de décisions hasardeuses. De plus, RGBlib est déjà bindée dans plusieurs langages (Kotlin et Python), ce qui ouvre la porte à des usages plus larges qu’un simple univers en Rust.

Iris Wallet : un exemple de wallet RGB sur Android

Pour prouver l’efficacité de RGBlib, l’équipe de Bitfinex a développé Iris Wallet, exclusivement sur Android à ce stade. Il s’agit d’un wallet mobile permettant d’illustrer un parcours utilisateur proche d’un wallet Bitcoin ordinaire : on peut y émettre un asset, l’envoyer, le recevoir, et voir son historique, tout en restant sur un modèle de self-custody.

Iris dispose de certaines caractéristiques intéressantes :

Utilisation d’un serveur Electrum :

Comme tout wallet, Iris doit connaître les confirmations de transactions sur la blockchain. Plutôt que d’embarquer un nœud complet, Iris s’appuie par défaut sur un serveur Electrum maintenu par l’équipe de Bitfinex. L’utilisateur, s’il le souhaite, peut cependant configurer son propre serveur ou un autre service tiers. Ainsi, la validation des transactions Bitcoin et la récupération d’informations (indexation) se fait de façon modulable.

Le serveur proxy RGB :

Contrairement à Bitcoin, RGB exige l’échange de métadonnées off-chain (consignments) entre l’expéditeur et le receveur. Pour simplifier ce procesus, Iris propose une solution où la communication s’effectue via un serveur proxy. Le wallet destinataire génère une invoice mentionnant notamment où l’expéditeur doit envoyer les données client-side. Par défaut, l’URL pointe vers un proxy hébergé par l’équipe de Bitfinex, mais il demeure possible de changer ce proxy (ou d’en héberger un soi-même). L’idée est de retrouver une expérience utilisateur familière où le destinataire affiche un QR code, et l’expéditeur scanne ce code pour la transaction, sans manipulations supplémentaires complexes.

Sauvegarde en continu :

Dans un contexte strictement Bitcoin, sauvegarder sa seed suffit généralement (même si de nos jours on conseille plutôt de sauvegarder la seed et les descriptors). Avec RGB, c’est insuffisant : on doit aussi conserver l’historique local (les consignments) prouvant qu’on possède vraiment un actif RGB. À chaque réception, l’appareil stocke de nouvelles données indispensables à la dépense ultérieure. Iris gère automatiquement un backup chiffré dans le Google Drive de l’utilisateur. Cela n’exige aucune confiance particulière en Google, car la sauvegarde est chiffrée et il est prévu, dans l’avenir, des options plus robustes (comme un serveur personnel) pour éviter tout risque de censure ou de suppression par un opérateur tiers.

Autres fonctionnalités :

Au final, Iris affiche donc une expérience utilisateur proche d’un wallet Bitcoin classique, en masquant la complexité supplémentaire (gestion du stash, historique de consignments, etc.) grâce à RGBlib et l'utilisation d'un serveur proxy.

Serveur proxy et expérience utilisateur

Le serveur proxy introduit ci-dessus mérite d’être détaillé, car il constitue la clef d’une expérience fluide pour l'utilisateur. Au lieu que l’expéditeur doive manuellement transmettre les consignments au destinataire, la transaction RGB s’effectue en arrière-plan via un schéma de requêtes :

Ainsi, le wallet se comporte presque comme un wallet normal. L’utilisateur n’a pas conscience de toutes les étapes intermédiaires. Certes, le proxy actuel n’est pas chiffré ni authentifié (ce qui laisse des inquiétudes quant à la confidentialité et l’intégrité), mais ces améliorations sont possibles dans des versions ultérieures. Le concept de proxy demeure extrêmement utile pour recréer l’expérience : "j’envoie un QR code, tu scannes pour payer".

Intégration de RGB sur le Lightning Network

Un autre axe essentiel du travail mené par l’équipe de Bitfinex consiste à rendre le Lightning Network compatible avec des assets RGB. L’objectif est de permettre des canaux Lightning en USDT (ou tout autre jeton), et de bénéficier des mêmes avantages que pour le bitcoin sur Lightning (transactions quasi instantanées, routage, etc.). Concrètement, il s’agit de créer un nœud Lightning modifié pour :

Cette solution, baptisée "RGB Lightning Node", utilise notamment LDK (Lightning Dev Kit) comme base et ajoute les mécanismes nécessaires pour injecter des tokens RGB dans les canaux. Les engagements Lightning conservent la structure classique (sorties punissables, timelock...), et en plus on y ancre une transition d'état RGB (via Opret ou Tapret). Pour l’utilisateur, cela ouvre la voie à des canaux Lightning en stablecoins ou en tout autre actif émis via RGB.

Potentiel DEX et impacts sur Bitcoin

Une fois plusieurs actifs gérés via Lightning, il devient possible d’imaginer un échange atomique sur un unique chemin de routage Lightning, en utilisant la même logique de secrets et timelocks. Par exemple, un utilisateur A détient du bitcoin sur un canal Lightning, et un utilisateur B détient de l’USDT RGB sur un autre canal Lightning. Ils peuvent construire un chemin reliant leurs deux canaux et échanger simultanément BTC contre USDT, sans besoin de confiance. Ce n’est rien d’autre qu’un atomic swap se déroulant en plusieurs sauts, rendant les participants extérieurs quasi inconscients du fait qu’ils réalisent un trade, pas juste un routage. Cette approche offre :

On peut alors imaginer un écosystème où des nœuds Lightning proposent des prix de swap (en fournissant de la liquidité). Chaque nœud, s’il le souhaite, peut jouer ce rôle de market maker, en achetant et en vendant divers actifs sur Lightning. Cette perspective d’un DEX layer-2 renforce l’idée qu’il n’est pas nécessaire de fork ou d’utiliser des blockchains tierces pour obtenir des échanges d’actifs décentralisés.

L’impact pour Bitcoin peut être positif : l’infrastructure de Lightning (nœuds, canaux et services) serait davantage utilisée grâce aux volumes générés par ces stablecoins, dérivés et autres tokens. Les commerçants intéressés par les paiements en USDT sur Lightning, découvriraient mécaniquement les paiements en BTC sur Lightning (gérés par la même stack). L’entretien et le financement de l’infrastructure du Lightning Network pourraient aussi profiter de la multiplication de ces flux non-BTC, ce qui, indirectement, profiterait aux utilisateurs de Bitcoin.

Conclusion et ressources

L’équipe de Bitfinex dédiée à RGB illustre, via ses travaux, la diversité de ce qu’on peut faire au-dessus du protocole. D’un côté, on a RGBlib, une librairie qui facilite la conception de wallets et d’applications. D’un autre côté, on a Iris Wallet, une démonstration pratique sur Android d’une interface soignée pour l’utilisateur final. Enfin, l’intégration de RGB à Lightning montre que des canaux en stablecoin sont rendus possibles, et ouvre la voie à un potentiel DEX décentralisé sur Lightning.

Cette démarche reste largement expérimentale et continue d’évoluer : la librairie RGBlib se peaufine au fur et à mesure, Iris Wallet reçoit des améliorations régulières et le nœud Lightning dédié n’est pas encore un client Lightning mainstream.

Pour ceux qui souhaitent en savoir plus ou contribuer, plusieurs ressources sont disponibles, notamment :

Dans le prochain chapitre, nous allons voir concrètement commet on peut lancer un nœud RGB Lightning.

RLN - RGB Lightning Node

Dans ce dernier chapitre, Frederico Tenga vous guide étape par étape dans la mise en place d'un nœud Lightning RGB sur un environnement en Regtest, et vous montre comment y créer des tokens RGB. En lançant deux nœuds séparés, vous découvrirez également comment ouvrir un canal Lightning entre eux et y échanger des actifs RGB.

Cette vidéo sert de tutoriel, similaire à ce que nous avons abordé dans un chapitre précédent, mais spécifiquement axée sur Lightning cette fois-ci !

La principale ressource de cette vidéo est le dépôt Github RGB Lightning Node, qui vous facilite le lancement de cette configuration en Regtest.

Déploiement d’un nœud Lightning compatible RGB

Le processus reprend et met en pratique toutes les notions abordées dans les chapitres précédents :

Présentation de rgb-lightning-node

Le projet rgb-lightning-node est un daemon en Rust qui s’appuie sur un fork de rust-lightning (LDK) modifié pour prendre en compte l’existence d’assets RGB dans un canal. Lors de l’ouverture d’un canal, on peut ainsi préciser la présence d’assets, et lors de chaque mise à jour de l’état du canal, une transition RGB est créée, qui reflète la répartition de l’asset dans les outputs Lightning. Cela permet :

Le code est encore à un stade alpha : il est conseillé de l’utiliser en regtest ou sur le testnet uniquement.

Installation du nœud

Pour compiler et installer le binaire rgb-lightning-node, on commence par cloner le dépôt et ses sous-modules, puis on va lancer la compilation :

git clone https://github.com/RGB-Tools/rgb-lightning-node --recurse-submodules --shallow-submodules
RGB-Bitcoin

Depuis la racine du projet, exécutez la commande suivante pour compiler et installer le binaire :

cargo install --locked --debug --path .
RGB-Bitcoin

Au terme de cette commande, un exécutable rgb-lightning-node sera disponible dans votre $CARGO_HOME/bin/. Assurez-vous que ce chemin est dans votre $PATH pour pouvoir invoquer la commande depuis n’importe quel répertoire.

Prérequis d’exécution

Pour fonctionner, le daemon rgb-lightning-node requiert la présence et la configuration de :

Chaque instance RLN aura besoin de communiquer avec bitcoind pour diffuser et surveiller ses transactions on-chain. L’authentification (login/password) et l’URL (host/port) devront être fournis au daemon.

Le daemon doit pouvoir lister et explorer les transactions on-chain, en particulier pour retrouver l’UTXO sur lequel un asset a été ancré. Vous devrez préciser l’URL de votre Electrum server ou Esplora.

Comme vu dans les chapitres précédents, le serveur proxy est un composant (optionnel, mais fortement recommandé) permettant de simplifier l’échange de consignments entre pairs Lightning. Là encore, une URL doit être spécifiée.

Les identifiants et URL sont renseignés au moment où l’on unlock le daemon via l’API. Cela sera détaillé plus loin.

Lancement en Regtest

Pour un usage simple, il y a un script regtest.sh qui démarre automatiquement, via Docker, un ensemble de services : bitcoind, electrs (indexer), rgb-proxy-server.

RGB-Bitcoin

Cela permet de lancer un environnement local, isolé et déjà configuré. Il crée et détruit les conteneurs ainsi que les répertoires de données à chaque redémarrage. Nous allons commencer par démarrer l'environnement :

./regtest.sh start

Ce script va :

RGB-Bitcoin

Puis, nous allons lancer plusieurs nœuds RLN. Dans des shells distincts, exécutez par exemple (pour lancer 3 nœuds RLN) :

# 1st shell
rgb-lightning-node dataldk0/ --daemon-listening-port 3001 \
    --ldk-peer-listening-port 9735 --network regtest

# 2nd shell
rgb-lightning-node dataldk1/ --daemon-listening-port 3002 \
    --ldk-peer-listening-port 9736 --network regtest

# 3rd shell
rgb-lightning-node dataldk2/ --daemon-listening-port 3003 \
    --ldk-peer-listening-port 9737 --network regtest
RGB-Bitcoin

Vous pouvez également exécuter des commandes sur vos nœuds RLN depuis votre navigateur :

https://rgb-tools.github.io/rgb-lightning-node/

Pour qu’un nœud puisse ouvrir un canal, il doit d'abord posséder des bitcoins sur une adresse générée avec la commande suivante (pour le nœud n°1 par exemple) :

curl -X POST http://localhost:3001/address

La réponse vous fournira une adresse.

RGB-Bitcoin

Sur le bitcoind Regtest, on va miner quelques bitcoins. Exécutez :

./regtest.sh mine 101
RGB-Bitcoin

Envoyez des fonds à l'adresse du nœud générée précédemment :

./regtest.sh sendtoaddress <address> <amount>
RGB-Bitcoin

Puis minez un bloc pour confirmer la transaction :

./regtest.sh mine 1
RGB-Bitcoin

Lancement en Testnet (sans Docker)

Si vous souhaitez tester un scénario plus réaliste, vous pouvez lancer 3 nœuds RLN sur le Testnet plutôt qu'en Regtest, pointant vers des services publics :

rgb-lightning-node dataldk0/ --daemon-listening-port 3001 \
    --ldk-peer-listening-port 9735 --network testnet

rgb-lightning-node dataldk1/ --daemon-listening-port 3002 \
    --ldk-peer-listening-port 9736 --network testnet

rgb-lightning-node dataldk2/ --daemon-listening-port 3003 \
    --ldk-peer-listening-port 9737 --network testnet

Par défaut, s’il ne trouve pas de configuration, le daemon tentera d’utiliser les services :

Avec les identifiants :

Vous pouvez aussi personnaliser ces éléments via l’API init/unlock.

Émission d'un token RGB

Pour émettre un token on va commencer par créer des UTXOs "colorables" :

curl -X POST -H "Content-Type: application/json" \
  -d '{
        "up_to": false,
        "num": 4,
        "size": 2000000,
        "fee_rate": 4.2,
        "skip_sync": false
      }' \
  http://localhost:3001/createutxos
RGB-Bitcoin

Vous pouvez évidemment adapter la commande. Pour confirmer la transaction, on mine un bloc :

./regtest.sh mine 1

On peut maintenant créer un asset RGB. La commande va dépendre du type d'asset que vous souhaitez créer et de ses paramètres. Ici je crée un token NIA (Non Inflatable Asset) nommé "PBN" avec une supply de 1000 unités. La precision permet de définir la divisibilité des unités.

curl -X POST -H "Content-Type: application/json" \
  -d '{
        "amounts": [
          1000
        ],
        "ticker": "PBN",
        "name": "Plan B Network",
        "precision": 0
      }' \
  http://localhost:3001/issueassetnia
RGB-Bitcoin

La réponse inclue l’identifiant de l’asset nouvellement créé. Pensez à noter cet identifiant. Dans mon cas, c'est :

rgb:fc7fMj5S-8yz!vIl-260BEhU-Hj1skvM-ZHcjfyz-RTcWc10
RGB-Bitcoin

Vous pourrez ensuite effectuer des transferts on-chain, ou bien l’allouer dans un canal Lightning. C'est justement ce que nous allons faire dans la prochaine section.

Ouverture de canal et transfert d’un actif RGB

Vous devez d'abord connecter votre nœud à un pair du réseau Lightning en utilisant la commande /connectpeer. Dans mon exemple, je contrôle les deux nœuds. Je vais donc récupérer la clé publique de mon second nœud Lightning avec cette commande :

curl -X 'GET' \
  'http://localhost:3002/nodeinfo' \
  -H 'accept: application/json'

La commande me renvoie la clé publique de mon nœud n°2 :

031e81e4c5c6b6a50cbf5d85b15dad720fec92c62e84bafb34088f0488e00a8e94
RGB-Bitcoin

Ensuite, nous allons ouvrir le canal en spécifiant l'asset concerné (PBN). La commande /openchannel vous permet de définir la taille du canal en satoshis et d'opter pour l'inclusion de l'asset RGB. Cela dépend de ce que vous souhaitez créer, mais dans mon cas, la commande est :

curl -X POST -H "Content-Type: application/json" \
  -d '{
        "peer_pubkey_and_opt_addr": "031e81e4c5c6b6a50cbf5d85b15dad720fec92c62e84bafb34088f0488e00a8e94@localhost:9736",
        "capacity_sat": 1000000,
        "push_msat": 10000000,
        "asset_amount": 500,
        "asset_id": "rgb:fc7fMj5S-8yz!vIl-260BEhU-Hj1skvM-ZHcjfyz-RTcWc10",
        "public": true,
        "with_anchors": true,
        "fee_base_msat": 1000,
        "fee_proportional_millionths": 0,
        "temporary_channel_id": "a8b60c8ce3067b5fc881d4831323e24751daec3b64353c8df3205ec5d838f1c5"
      }' \
  http://localhost:3001/openchannel

Dans le détail ici :

RGB-Bitcoin

Pour confirmer la transaction, on mine 6 blocs :

./regtest.sh mine 6
RGB-Bitcoin

Le canal Lightning est désormais ouvert et contient également 500 tokens PBN du côté du nœud n°1. Si le nœud n°2 souhaite recevoir des tokens PBN, il devra générer une invoice. Voici comment procéder :

curl -X POST -H "Content-Type: application/json" \
  -d '{
        "amt_msat": 3000000,
        "expiry_sec": 420,
        "asset_id": "rgb:fc7fMj5S-8yz!vIl-260BEhU-Hj1skvM-ZHcjfyz-RTcWc10",
        "asset_amount": 100
      }' \
  http://localhost:3002/lninvoice

Avec :

En réponse, vous obtiendrez une invoice RGB (comme décris dans les chapitres précédents) :

lnbcrt30u1pncgd4rdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4qv0grex9c6m22r9ltkzmzhddwg87eykx96zt47e5pz8sfz8qp28fgpp5jksvqtleryhvwr299qdz96qxzm24augy5agkdhltudk463lt9dassp5d6n0sqgl0c4gx52fdmutrdtqamt0y4xuz2rcgel4hpjwne08gmls9qyysgqcqpcxqzdylz5wfnkywnxvvmkvnt2x4fj6wre0gshvjtv95ervvzzg4592t2gdgchx6mkf5k45jrrdfn8j73d2f2xx4mrxycq7qzry4v4jan6uxhhacyqa4gn6plggwpq9j74tu74f2zsamtz6ymt600p8su4c4ap9g9d8ku2x3wdh6fuc8fd8pff2yzpjrf24ys3cltca9fgqut6gzj
RGB-Bitcoin

Nous allons maintenant payer cette invoice depuis le premier nœud, qui détient les liquidités nécessaires avec le token PBN :

curl -X POST -H "Content-Type: application/json" \
  -d '{
        "invoice": "lnbcrt30u1pncgd4rdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4qv0grex9c6m22r9ltkzmzhddwg87eykx96zt47e5pz8sfz8qp28fgpp5jksvqtleryhvwr299qdz96qxzm24augy5agkdhltudk463lt9dassp5d6n0sqgl0c4gx52fdmutrdtqamt0y4xuz2rcgel4hpjwne08gmls9qyysgqcqpcxqzdylz5wfnkywnxvvmkvnt2x4fj6wre0gshvjtv95ervvzzg4592t2gdgchx6mkf5k45jrrdfn8j73d2f2xx4mrxycq7qzry4v4jan6uxhhacyqa4gn6plggwpq9j74tu74f2zsamtz6ymt600p8su4c4ap9g9d8ku2x3wdh6fuc8fd8pff2yzpjrf24ys3cltca9fgqut6gzj"
      }' \
  http://localhost:3001/sendpayment
RGB-Bitcoin

Le paiement a bien été effectué. On peut le vérifier en exécutant sur un des deux nœuds la commande :

curl -X 'GET' \
  'http://localhost:3001/listpayments' \
  -H 'accept: application/json'
RGB-Bitcoin

Voici donc comment déployer un nœud Lightning modifié pour transporter des assets RGB. Cette démonstration se base sur :

Grâce à ce processus :

Le projet demeure à un stade alpha. Il est donc fortement recommandé de se limiter à des environnements test (regtest, testnet).

Les opportunités ouvertes par cette compatibilité LN-RGB sont considérables : stablecoins sur Lightning, DEX layer-2, transferts de tokens fongibles ou de NFT à très faible coût… Les chapitres précédents ont exposé l’architecture conceptuelle et la logique de validation. Désormais, vous possédez une vue pratique de la mise en route d’un tel nœud, pour vos futurs développements ou tests.

Section finale

Avis & Notes

0217e8b0-942a-5fee-bd91-9a866551eff3 true

Conclusion

0309536d-c336-56a0-869e-a8395ed8d9ae true