name: The RGB protocol, from theory to practice goal: Acquire the skills needed to understand and use RGB objectives:


Discovering the RGB protocol

Dive into the world of RGB, a protocol designed to implement and enforce digital rights, in the form of contracts and assets, based on the consensus rules and operations of the Bitcoin blockchain. This comprehensive training course guides you through the technical and practical foundations of RGB, from the concepts of "Client-side Validation" and "Single-use Seals", to the implementation of advanced smart contracts.

Through a structured, step-by-step program, you'll discover the mechanisms of client-side validation, deterministic commitments on Bitcoin and interaction patterns between users. Learn how to create, manage and transfer RGB tokens on Bitcoin or the Lightning Network.

Whether you're a developer, Bitcoin enthusiast, or simply curious to learn more about this technology, this training course will provide you with the tools and knowledge you need to master RGB and build innovative solutions on Bitcoin.

The course is based on a live seminar organized by Fulgur'Ventures and taught by three renowned teachers and RGB experts.

Introduction

Course presentation

Hello everyone, and welcome to this training course dedicated to RGB, a client-side validated smart contract system running on Bitcoin and the Lightning Network. The structure of this course is designed to enable in-depth exploration of this complex subject. Here's how the course is organized:

Section 1: Theory

The first section is dedicated to the theoretical concepts needed to understand the fundamentals of client-side validation and RGB. As you'll discover in this course, RGB introduces a host of technical concepts not usually seen in Bitcoin. In this section, you'll also find a glossary providing definitions for all terms specific to the RGB protocol.

Section 2: Practice

The second section will focus on the application of the theoretical concepts seen in section 1. We'll learn how to create and manipulate RGB contracts. We'll also see how to program with these tools. These first two sections are presented by Maxim Orlovsky.

Section 3: Applications

The final section is led by other speakers who present concrete RGB-based applications, to highlight real-life use cases.


This training course originally grew out of a two-week advanced development bootcamp in Viareggio, Tuscany, organized by Fulgur'Ventures. The first week, focused on Rust and SDKs, can be found in this other course:

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

In this course, we focus on the second week of the bootcamp, which focuses on RGB.

Week 1 - LNP402:

RGB-Bitcoin

Week 2 - Current training CSV402:

RGB-Bitcoin

Many thanks to the organizers of these live courses and to the 3 teachers who took part:

The written version of this training course was drafted using 2 main resources:

Ready to dive into the complex and fascinating world of RGB? Let's go!

RGB in theory

Introduction to distributed computing concepts

RGB is a protocol designed to apply and enforce digital rights (in the form of contracts and assets) in a scalable and confidential way, based on the consensus rules and operations of the Bitcoin blockchain. The aim of this first chapter is to present the basic concepts and terminology around the RGB protocol, highlighting in particular its close links with basic distributed computing concepts such as Client-side Validation and Single-use Seals.

In this chapter, we explore the fundamentals of distributed consensus systems and see how RGB fits into this family of technologies. We'll also introduce the main principles that help us understand why RGB aims to be extensible and independent of Bitcoin's own consensus mechanism, while relying on it when necessary.

Introduction

Distributed computing, a specific branch of computer science, studies the protocols used to circulate and process information on a network of nodes. Together, these nodes and the protocol rules constitute what is known as a distributed system. Among the essential properties that characterize such a system , some are:

In particular, the notion of consensus in a distributed system covers two aspects:

The first functional, permission-free implementation of a distributed consensus mechanism was introduced by Satoshi Nakamoto with Bitcoin, thanks to the combined use of a blockchain data structure and a Proof-of-Work (PoW) algorithm. In this system, the credibility of the block history depends on the computing power devoted to it by the nodes (miners). Bitcoin is therefore a major and historic example of a distributed consensus system open to all (permissionless).

In the world of blockchain and distributed computing, we can distinguish two fundamental paradigms: blockchain in the traditional sense, and state channels, the best example of which in production is the Lightning Network. The blockchain is defined as a register of chronologically ordered events, replicated by consensus within an open, permission-free network. State channels, on the other hand, are peer-to-peer channels that enable two (or more) participants to maintain an updated state off-chain, using the blockchain only when opening and closing these channels.

In the context of Bitcoin, you're no doubt familiar with the principles of mining, decentralization and finality of transactions on the blockchain, as well as how payment channels work. With RGB, we're introducing a new paradigm called Client-side Validation, which, unlike blockchain or Lightning, consists in locally (client-side) storing and validating the state transitions of a smart contract. This also differs from other "DeFi" techniques (rollups, plasma, ARK, etc.), where the Client-side Validation relies on the blockchain to prevent double-spending and to have a time-stamping system, while keeping the register of off-chain states and transitions, only with the participants concerned.

RGB-Bitcoin

Later on, we'll also introduce an important term: the notion of "stash", which refers to the set of client-side data required to preserve the state of a contract, as this data is not replicated globally across the network. Finally, we'll look at the rationale behind RGB, a protocol that takes advantage of Client-side Validation, and why it complements existing approaches (blockchain and state channels).

Trilemmas in distributed computing

To understand how Client-side Validation and RGB address problems not solved by blockchain and Lightning, let's discover 3 major "trilemmas" in distributed computing:

1. Scalability, decentralization and confidentiality

Blockchain is highly decentralized, but not very scalable. What's more, since everything is in a global, public register, confidentiality is limited. We can try to improve confidentiality with zero-knowledge technologies (confidential transactions, mimblewimble schemes, etc.), but the public chain cannot hide the transaction graph.

State channels (as with the Lightning Network) are more scalable and more private than blockchain, as transactions take place off-chain. However, the obligation to publicly announce certain elements (funding transactions, network topology) and the monitoring of network traffic can partly compromise confidentiality. Decentralization also suffers: routing is cash-intensive, and major nodes can become centralization points. This is precisely the phenomenon we're beginning to see on Lightning.

This new paradigm is even more scalable and more confidential, because not only can we integrate zero-disclosure proof-of-knowledge techniques, but there is no global graph of transactions, since nobody holds the entire register. On the other hand, it also implies a certain compromise on decentralization: the issuer of a smart contract can have a central role (like a "contract deployer" in Ethereum). However, unlike blockchain, with Client-side Validation, you only store and validate the contracts you're interested in, which improves scalability by avoiding the need to download and verify all existing states.

RGB-Bitcoin

2. CAP Theorem (Consistency, Availability, Partition tolerance)

The CAP theorem emphasizes that it is impossible for a distributed system to simultaneously satisfy Consistency,Availability and Partition tolerance.

The blockchain favors consistency and availability, but doesn't do well with network partitioning: if you can't see a block, you can't act and have the same view as the whole network.

A system of state channels has availability and partitioning tolerance (since two nodes can remain connected to each other even if the network is fragmented), but overall consistency depends on the opening and closing of channels on the blockchain.

A system like RGB offers consistency (each participant validates its data locally, without ambiguity) and partitioning tolerance (you keep your data autonomously), but does not guarantee global availability (everyone has to make sure they have the relevant pieces of history, and some participants may not publish anything or stop sharing certain information).

RGB-Bitcoin

3. CIA trilemma (Confidentiality, Integrity, Availability)

This trilemma reminds us that confidentiality, integrity and availability cannot all be optimized at the same time. Blockchain, Lightning and Client-side Validation fall differently into this balance. The idea is that no single system can provide everything; it is necessary to combine several approaches (blockchain's time-stamping, Lightning's synchronous approach, and local validation with RGB) to obtain a coherent package offering good guarantees in each dimension.

RGB-Bitcoin

The role of blockchain and the notion of sharding

The blockchain (in this case, Bitcoin) serves primarily as a time-stamping mechanism and protection against double spending. Instead of inserting the complete data of a smart contract or decentralized system, we simply include cryptographic commitments to transactions (in the sense of Client-side Validation, which we'll call "state transitions"). Thus:

Sharding is a concept that originated in distributed databases (e.g. MySQL for social networks such as Facebook or Twitter). To solve the problem of data volume and synchronization latencies, the database is segmented into shards (USA, Europe, Asia, etc.). Each segment is locally consistent and only partially synchronized with the others.

For RGB-type smart contracts, we shard according to the contracts themselves. Each contract is an independent shard. For example, if you only hold USDT tokens, you don't have to store or validate the entire history of another token like USDC. On Bitcoin, the blockchain doesn't do sharding: you have a global set of UTXOs. With Client-side Validation, each participant retains only the contract data it holds or uses.

We can therefore imagine the ecosystem as follows:

RGB-Bitcoin

These three elements form a triangular whole, rather than a linear stack of "layer 2", "layer 3" and so on. Lightning can connect directly to Bitcoin, or be associated with Bitcoin transactions that incorporate RGB data. Similarly, a "BiFi" (finance on Bitcoin) can compose with the blockchain, with Lightning and with RGB according to needs for confidentiality, scalability or contract logic.

RGB-Bitcoin

The notion of state transitions

In any distributed system, the aim of the validation mechanism is to be able to determine the validity and chronological order of state changes. The aim is to verify that the protocol rules have been respected, and to prove that these state changes follow one another in a definitive, unassailable order.

To understand how this validation works in the context of Bitcoin and, more generally, to grasp the philosophy behind Client-side Validation, let's first take a look back at the mechanisms of the Bitcoin blockchain, before seeing how Client-side Validation differs from them and what optimizations it makes possible.

RGB-Bitcoin

In the case of the Bitcoin blockchain, transaction validation is based on a simple rule:

RGB-Bitcoin

However, this model has two major drawbacks:

RGB-Bitcoin

In practice, this model works for Bitcoin as a base layer (Layer 1), but may become insufficient for more complex uses that simultaneously require high transaction throughput and a certain degree of confidentiality.

Client-side Validation is based on the opposite idea: rather than requiring the entire network to validate and store all transactions, each participant (client) will validate only the part of the history that concerns him or her:

RGB-Bitcoin

At the same time, so that the rest of the network (or more precisely, the underlying layer, such as Bitcoin) can lock in the final state without seeing the details of this data, Client-side Validation relies on the notion of commitment.

A commitment is a cryptographic commitment, typically a hash (SHA-256 for example) inserted into a Bitcoin transaction, which proves that private data has been included, without revealing this data.

Thanks to these commitments, we can prove:

The exact content, however, is not revealed, thus preserving its confidentiality.

In concrete terms, here's how an RGB state transition works:

RGB-Bitcoin

Client-side Validation offers two major benefits:

The commitments included in the blockchain are small (of the order of a few dozen bytes). This ensures that block space is not saturated, as only the hash needs to be included. It also enables the off-chain protocol to evolve, as each user only has to store his or her history fragment (his or her stash).

Transactions themselves (i.e. their detailed content) are not published on-chain. Only their fingerprints (hash) are. Thus, amounts, addresses and contract logic remain private, and the receiver can verify, locally, the validity of his shard by inspecting all previous transitions. There is no reason for the receiver to make this data public, except in the event of a dispute or where proof is required.

In a system like RGB, multiple state transitions from different contracts (or different assets) can be aggregated into a single Bitcoin transaction via a single commitment. This mechanism establishes a deterministic, time-stamped link between the on-chain transaction and the off-chain data (the client-side validated transitions), and enables multiple shards to be simultaneously recorded in a single anchor point, further reducing the on-chain cost and footprint.

In practice, when this Bitcoin transaction is validated, it permanently "locks" the state of the underlying contracts, since it becomes impossible to modify the hash already inscribed in the blockchain.

RGB-Bitcoin

The stash concept

A stash is the set of client-side data that a participant must absolutely retain to maintain the integrity and history of an RGB smart contract. Unlike a Lightning channel, where certain states can be reconstructed locally from shared information, the stash of an RGB contract is not replicated elsewhere: if you lose it, no one will be able to restore it to you, as you are responsible for your share of the history. This is why you need to adopt a system with reliable backup procedures in RGB.

RGB-Bitcoin

Single-use Seal: origins and operation

When accepting an asset such as a currency, two guarantees are essential:

For physical assets, such as a banknote, physical presence is enough to prove that it has not been duplicated. However, in the digital world, where assets are purely informational, this verification is more complex, as information can easily multiply and be duplicated.

As we saw earlier, the sender's revelation of the history of state transitions enables us to ensure the authenticity of an RGB token. By having access to all transactions since the genesis transaction, we can confirm the token's authenticity. This principle is similar to that of Bitcoin, where the history of coins can be traced back to the original coinbase transaction to verify their validity. However, unlike Bitcoin, this history of state transitions in RGB is private and kept on the client side.

To prevent double-spending of RGB tokens, we use a mechanism called "Single-use Seal". This system ensures that each token, once used, cannot be fraudulently reused a second time.

Single-use Seals are cryptographic primitives, proposed in 2016 by Peter Todd, akin to the concept of physical seals: once a seal has been placed on a container, it becomes impossible to open or modify it without irreversibly breaking the seal.

RGB-Bitcoin

This approach, transposed to the digital world, makes it possible to prove that a sequence of events has indeed taken place, and that it can no longer be altered a posteriori. Single-use Seals thus go beyond the simple logic of hash + timestamp, adding the notion of a seal that can be closed only once.

RGB-Bitcoin

For Single-use Seals to work, you need a publication proof medium capable of proving the existence or absence of a publication, and difficult (if not impossible) to falsify once the information has been disseminated. A blockchain (like Bitcoin) can fill this role, as can a paper newspaper with a public circulation do, as an example. The idea is as follows:

A blockchain lends itself ideally to this role: as soon as a transaction is included in a block, the whole network has the same unfalsifiable proof of its existence and content (at least in part, since the commitment can hide the details while proving the authenticity of the message).

A Single-use Seal can therefore be seen as a formal promise to publish a message (still unknown at this stage) once and only once, in a way that can be verified by all interested parties.

Unlike simple commitments (hash) or timestamps, which attest to a date of existence, a Single-use Seal offers the additional guarantee that no alternative commitment can coexist: you can't close the same seal twice, or attempt to replace the sealed message.

The following comparison helps to understand this principle:

Simple commitment (digest/hash)TimestampsSingle-use seals
Publishing the commitment does not reveal the messageYesYesYes
Proof of the commitment date / existence of the message before a certain dateImpossiblePossiblePossible
Proof that no alternative commitment can existImpossibleImpossiblePossible

Single-use Seals work in three main stages:

Seal Definition:

RGB-Bitcoin

Seal Closing:

RGB-Bitcoin

Seal Verification:

The process can be summarized as follows:

# Defined by Alice, validated or accepted by Bob
seal <- Define()
# Seal is closed by Alice with the message
witness <- Close(seal, message)
# Verification by Bob
bool <- Verify(seal, witness, message)

Client-side validation, however, goes one step further: if the definition of a seal itself remains outside the blockchain, it is possible (in theory) for someone to challenge the existence or legitimacy of the seal in question. To overcome this problem, a chain of interlocking Single-use Seals is used:

This is precisely what the RGB system does:

To sum up:

This uniqueness is important for Client-side Validation: when you validate a state transition, you check that it corresponds to a unique UTXO, not previously spent in a competing commitment. This is what guarantees the absence of double spending in off-chain smart contracts.

Multiple commitments and roots

An RGB smart contract may need to spend several Single-use Seals (several UTXOs) simultaneously. What's more, a single Bitcoin transaction may reference several distinct contracts, each sealing its own state transition. This requires a multi-commitment mechanism to prove, deterministically and uniquely, that none of the commitments exists in duplicate. This is where the notion of anchor comes into play in RGB: a special structure linking a Bitcoin transaction and one or more client-side commitments (state transitions), each potentially belonging to a different contract. We'll take a closer look at this concept in the next chapter.

RGB-Bitcoin

Two of the project's main GitHub repositories (under the LNPBP organization) group together the basic implementations of these concepts studied in the first chapter:

RGB-Bitcoin

Note that these software bricks are Bitcoin agnostic; in theory, they could be applied to any other proof-of-publication medium (another registry, a journal, etc.). In practice, RGB relies on Bitcoin for its robustness and broad consensus.

RGB-Bitcoin

Questions from the public

Towards wider use of Single-use Seals

Peter Todd also created the Open Timestamps protocol, and the Single-use Seal concept is a natural extension of these ideas. Beyond RGB, other use cases can be envisaged, such as the construction of sidechains without resorting to merge mining or drivechain-related proposals like BIP300. Any system requiring a single commitment can, in principle, exploit this cryptographic primitive. Today, RGB is the first major full-scale implementation.

Data availability problems

Since in Client-side Validation, each user stores his or her own part of the history, data availability is not guaranteed globally. If a contract issuer withholds or revokes certain information, you may be unaware of the actual evolution of the offer. In some cases (such as stablecoins), the issuer is expected to maintain public data to prove the volume in circulation, but there is no technical obligation to do so. It is therefore possible to design deliberately opaque contracts with unlimited supply, which raises questions of trust.

Sharding and contract isolation

Each contract represents an isolated shard: USDT and USDC, for example, do not have to share their histories. Atomic swaps are still possible, but this does not involve merging their registers. Everything is done by cryptographic commitment, without disclosing the entire history graph to each participant.

Conclusion

We've seen where the concept of Client-side Validation fits in with blockchain and state channels, how it responds to distributed computing trilemmas, and how it leverages the Bitcoin blockchain uniquely to avoid double-spending and for time-stamping. The idea is based on the notion of Single-use Seal, enabling the creation of unique commitments that you can't re-spend at will. In this way, each participant uploads only the history that is strictly necessary, increasing the scalability and confidentiality of smart contracts while retaining the security of Bitcoin as a backdrop.

The next step will be to explain in more detail how this Single-use Seal mechanism is applied in Bitcoin (via UTXOs), how anchors are created and validated, and then how complete smart contracts are built in RGB. In particular, we'll look at the issue of multiple commitments, the technical challenge of proving that a Bitcoin transaction simultaneously seals multiple state transitions in different contracts, without introducing vulnerabilities or double commitments.

Before diving into the more technical details of the second chapter, feel free to reread the key definitions (Client-side Validation, Single-use Seal, anchors, etc.) and keep in mind the overall logic: we're looking to reconcile the strengths of the Bitcoin blockchain (security, decentralization, time-stamping) with those of off-chain solutions (speed, confidentiality, scalability), and this is precisely what RGB and Client-side Validation are trying to achieve.

The commitment layer

In this chapter, we'll look at the implementation of Client-side Validation and Single-use Seals within the Bitcoin blockchain. We'll present the main principles of RGB's commitment layer (layer 1), with a particular focus on the TxO2 scheme, which RGB uses to define and close a seal in a Bitcoin transaction. Next, we'll discuss two important points that haven't yet been covered in detail:

It is the combination of these concepts that enables us to superimpose several systems or contracts on top of a single UTXO and therefore a single blockchain.

It should be remembered that the cryptographic operations described can be applied, in absolute terms, to other blockchains or publishing media, but Bitcoin's characteristics (in terms of decentralization, resistance to censorship and openness to all) make it the ideal foundation for developing advanced programmability such as that required by RGB.

Commitment schemes in Bitcoin and their use by RGB

As we saw in the first chapter of the course, Single-use Seals are a general concept: we make a promise to include a commitment (commitment) in a specific location of a transaction, and this location acts like a seal that we close on a message. However, on the Bitcoin blockchain, there are several options for choosing where to place this commitment.

To understand the logic, let's recall the basic principle: to close a single-use seal, we spend the sealed area by inserting the commitment on a given message. In Bitcoin, this can be done in a number of ways:

We can decide that a specific public key or address is the single-use seal. As soon as this key or address appears on-chain in a transaction, it means that the seal is closed with a certain message.

This means that a single-use seal is defined as a precise outpoint (a TXID + output number pair). As soon as this outpoint is spent, the seal is closed.

While working on RGB, we identified at least 4 different ways to implement these seals on Bitcoin:

Schema NameSeal DefinitionSeal ClosureAdditional RequirementsMain ApplicationPossible Commitment Schemes
PkOPublic Key ValueTransaction OutputP2(W)PKHNone at the momentKeytweak, taptweak, opret
TxO2Transaction OutputTransaction OutputRequires deterministic commitments on BitcoinRGBv1 (universal)Keytweak, tapret, opret
PkIPublic Key ValueTransaction InputTaproot only & not compatible with legacy walletsBitcoin-based identitiesSigtweak, witweak
TxO1Transaction OutputTransaction InputTaproot only & not compatible with legacy walletsNone at the momentSigtweak, witweak

We won't go into detail about each of these configurations, as in RGB we've chosen to use an outpoint as the definition of the seal, and to place the commitment in the output of the transaction spending this outpoint. We can therefore introduce the following concepts for the sequel:

This scheme has been selected for its compatibility with RGB architecture, but other configurations could be useful for different uses.

The "O2" in "TxO2" reminds us that both definition and closure are based on the expenditure (or creation) of a transaction output.

TxO2 diagram example

As a reminder, defining a single-use seal does not necessarily require publishing an on-chain transaction. It's enough for Alice, for example, to already have an unspent UTXO. She can decide: "This outpoint (already existing) is now my seal". She notes this locally (client-side), and until this UTXO is spent, the seal is considered open.

RGB-Bitcoin

On the day it wants to close the seal (to signal an event, or to anchor a particular message), it spends this UTXO in a new transaction (this transaction is often called the "witness transaction" (unrelated to segwit, it's just the term we give it). This new transaction will contain the commitment to the message.

RGB-Bitcoin

Note that in this example:

To illustrate this TxO2 scheme, we can use a single-use seal as a mechanism for revoking a PGP key. Instead of publishing a revocation certificate on servers, Alice can say: "This Bitcoin output, if spent, means that my PGP key is revoked".

Alice therefore has a specific UTXO, to which a certain state or data (known only to her) is associated locally (on the client side).

Alice informs Bob that if this UTXO is spent, a particular event will be deemed to have occurred. From the outside, all we see is a Bitcoin transaction; but Bob knows that this expenditure has a hidden meaning.

RGB-Bitcoin

As Alice spends this UTXO, she closes the seal on a message indicating her new key, or simply the revocation of the old one. In this way, anyone monitoring on-chain will see that the UTXO is spent, but only those with the full proof will know that it is precisely the revocation of the PGP key.

RGB-Bitcoin

In order for Bob or anyone else involved to check the hidden message, Alice must provide him with off-chain information.

RGB-Bitcoin

Alice must therefore provide Bob with the following:

RGB-Bitcoin

Third parties don't have this information. They only see that a UTXO has been spent. Confidentiality is therefore assured.

To clarify the structure, let's summarize the process in two transactions:

RGB-Bitcoin RGB-Bitcoin

We therefore call the second transaction the "witness transaction".

To illustrate this from another angle, we can represent two layers:

RGB-Bitcoin

But when closing the seal, the question arises as to where the commitment should be inserted.

In the previous section, we briefly mentioned how the Client-side Validation model can be applied to RGB and other systems. Here, we tackle the part about deterministic Bitcoin commitments and how to integrate them into a transaction. The idea is to understand why we are trying to insert a single commitment into the witness transaction, and above all how to ensure that there can be no other undisclosed competing commitments.

Commitment locations in a transaction

When you give someone proof that a certain message is embedded in a transaction, you need to be able to guarantee that there isn't another form of commitment (a second, hidden message) in the same transaction that hasn't been revealed to you. For client-side validation to remain robust, you need a deterministic mechanism for placing a single commitment in the transaction that closes the single-use seal.

The witness transaction spends the famous UTXO (or seal definition) and this expenditure corresponds to the closing of the seal. Technically speaking, we know that each outpoint can only be spent once. This is precisely what underpins Bitcoin's resistance to double spending. But the spending transaction may have several inputs, several outputs, or be composed in a complex way (coinjoins, Lightning channels, etc.). We therefore need to clearly define where to insert the commitment in this structure, unambiguously and uniformly.

Whatever the method (PkO, TxO2, etc.), the commitment can be inserted:

RGB-Bitcoin

Here are the details of each method:

RGB-Bitcoin

Sig tweak (sign-to-contract):

An earlier scheme involved exploiting the random part of a signature (ECDSA or Schnorr) to embed the commitment: this is the technique known as "Sign-to-contract". You replace the randomly generated nonce with a hash containing the data. In this way, the signature implicitly reveals your commitment, without any additional space in the transaction. This approach has a number of advantages:

However, 2 major drawbacks have emerged:

In practice, sig tweak is also not very compatible with existing hardware (hardware wallets) and formats (Lightning, etc.). So this great idea is hard to put into practice.

Key tweak (pay-to-contract):

The key tweak takes up the historical concept of pay-to-contract. We take the public key X and tweak it by adding the value H(message). Specifically, if X = x * G and h = H(message), then the new key will be X' = X + h * G. This tweaked key hides the commitment to the message. The holder of the original private key can, by adding h to his private key x, prove that he has the key to spend the output. In theory, this is elegant, because:

In practice, however, we come up against the following difficulties:

In the context of RGB, this path was envisaged until 2021, but it proved too complicated to make it work with current standards and infrastructure.

Witness tweak:

Another idea, which certain protocols such as inscriptions Ordinals have put into practice, is to place the data directly in the witness section of the transaction (hence the expression "witness tweak"). However, this method:

In addition, witness is designed to be prunable in certain contexts, which can make having robust proofs more complicated.

Open-return (opret):

Very simple in its operation, an OP_RETURN allows you to store a hash or message in a special field of the transaction. But it's immediately detectable: everyone sees that there's a commitment in the transaction, and it can be censored or discarded, as well as adding extra output. Since this increases transparency and size, it's considered less satisfactory from the point of view of a Client-side Validation solution.

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

Tapret

The final option is the use of Taproot (introduced with BIP341) with the Tapret scheme. Tapret is a more complex form of deterministic commitment, which brings improvements in terms of footprint on the blockchain and confidentiality for contract operations. The main idea is to hide the commitment in the Script Path Spend part of a taproot transaction.

RGB-Bitcoin

Before describing how the commitment is inserted into a taproot transaction, let's look at the exact form of the commitment, which must imperatively correspond to a 64-byte string constructed as follows:

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

So the 64-byte Tapret method looks like an Opret to which we've prefixed 29 bytes of OP_RESERVED and added an extra byte as a Nonce.

To maintain flexibility in terms of implementation, confidentiality and scaling, the Tapret scheme takes into account various use cases, depending on requirements:

Let's take a closer look at each of these two scenarios.

Tapret incorporation without existing Script Path

In this first case, we start from a taproot output key (Taproot Output Key) Q which contains only the internal public key P (Internal Key), with no associated script path (Script Path):

RGB-Bitcoin

To include a Tapret commitment, add a Script Path Spend with a unique script, as follows:

RGB-Bitcoin

The proof of inclusion and uniqueness in the taproot tree here boils down to the single internal public key P.

Tapret integration into a pre-existing Script Path

The second scenario concerns a more complex Q taproot output, which already contains several scripts. For example, we have a tree of 3 scripts:

RGB-Bitcoin

To add the Tapret commitment, we need to insert an unspendable script at the first level of the tree, shifting the existing scripts one level down. Visually, the tree becomes:

RGB-Bitcoin

According to taproot rules, each branch/leaf must be combined according to a lexicographical hash order. There are two possible cases:

Visual example for the first case (tHABC < tHT):

RGB-Bitcoin

Example for the second case (tHABC > tHT):

RGB-Bitcoin

Optimization with the nonce

To improve confidentiality, we can "mine" (a more accurate term would be "bruteforcing") the value of the <Nonce> (the last byte of the 64-byte Tapret) in an attempt to obtain a hash tHT such that tHABC < tHT. In this case, the commitment is placed on the right, saving the user from having to divulge the entire contents of existing scripts to prove the Tapret's uniqueness.

In summary, the Tapret offers a discrete and deterministic way of incorporating a commitment into a taproot transaction, while respecting the requirement for uniqueness and unambiguity essential to RGB's Client-side Validation and Single-use Seal logic.

Valid exits

For RGB commitment transactions, the main requirement for a valid Bitcoin commitment scheme is as follows: The transaction (witness transaction) must provably contain a single commitment. This requirement makes it impossible to construct an alternative history for client-side validated data within the same transaction. This means that the message around which the single-use seal closes is unique.

To satisfy this principle, and regardless of the number of outputs in a transaction, we require that one and only one output can contain a commitment. For each of the schemes used (Opret or Tapret), the only valid outputs that can contain an RGB commitment are the following:

Note that it is quite possible for a transaction to contain a single Opret commitment and a single Tapret commitment in two separate outputs. Thanks to the deterministic nature of Seal Definition, these two commitments then correspond to two distinct pieces of data validated on the client side.

Analysis and practical choices in RGB

When we started RGB, we reviewed all these methods to determine where and how to place a commitment in a transaction in a deterministic way. We defined some criteria:

MethodOn-chain trace & sizeClient-side sizeWallet IntegrationHardware CompatibilityLightning CompatibilityTaproot Compatibility
Keytweak (deterministic P2C)πŸŸ’πŸŸ‘πŸ”΄πŸŸ πŸ”΄ BOLT, πŸ”΄ Bifrost🟠 Taproot, 🟒 MuSig
Sigtweak (deterministic S2C)πŸŸ’πŸŸ’πŸŸ πŸ”΄πŸ”΄ BOLT, πŸ”΄ Bifrost🟠 Taproot, πŸ”΄ MuSig
Opret (OP_RETURN)πŸ”΄πŸŸ’πŸŸ’πŸŸ πŸ”΄ BOLT, 🟠 Bifrost-
Tapret Algorithm: top-left nodeπŸŸ πŸ”΄πŸŸ πŸŸ’πŸ”΄ BOLT, 🟒 Bifrost🟒 Taproot, 🟒 MuSig
Tapret Algorithm #4: any node + proofπŸŸ’πŸŸ πŸŸ πŸŸ’πŸ”΄ BOLT, 🟒 Bifrost🟒 Taproot, 🟒 MuSig
Deterministic Commitment SchemeStandardOn-Chain CostProof Size on Client Side
Keytweak (Deterministic P2C)LNPBP-1, 20 bytes33 bytes (non-tweaked key)
Sigtweak (Deterministic S2C)WIP (LNPBP-39)0 bytes0 bytes
Opret (OP_RETURN)-36 (v)bytes (additional TxOut)0 bytes
Tapret Algorithm: top-left nodeLNPBP-632 bytes in the witness (8 vbytes) for any n-of-m multisig and spending through script path0 bytes on scriptless scripts taproot ~270 bytes in a single script case, ~128 bytes if multiple scripts
Tapret Algorithm #4: any node + uniqueness proofLNPBP-632 bytes in the witness (8 vbytes) for single script cases, 0 bytes in the witness in most other cases0 bytes on scriptless scripts taproot, 65 bytes until the Taptree contains a dozen scripts
LayerOn-Chain Cost (Bytes/vbytes)On-Chain Cost (Bytes/vbytes)On-Chain Cost (Bytes/vbytes)On-Chain Cost (Bytes/vbytes)On-Chain Cost (Bytes/vbytes)Client-Side Cost (Bytes)Client-Side Cost (Bytes)Client-Side Cost (Bytes)Client-Side Cost (Bytes)Client-Side Cost (Bytes)
TypeTapretTapret #4KeytweakSigtweakOpretTapretTapret #4KeytweakSigtweakOpret
Single-sig00003200320?0
MuSig (n-of-n)0000320032? > 00
Multi-sig 2-of-332/832/8 or 00n/a32~2706532n/a0
Multi-sig 3-of-532/832/8 or 00n/a32~3406532n/a0
Multi-sig 2-of-3 with timeouts32/800n/a32646532n/a0
LayerOn-Chain Cost (vbytes)On-Chain Cost (vbytes)On-Chain Cost (vbytes)Client-Side Cost (bytes)Client-Side Cost (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
Branch MuSig / Multi_a (n-of-m)1+16n+8n+8xlog(n)806465
With timeouts (n-of-m)1+16n+8n+8xlog(n)806465
MethodPrivacy & ScalabilityInteroperabilityCompatibilityPortabilityComplexity
Keytweak (Deterministic P2C)πŸŸ’πŸ”΄πŸ”΄πŸŸ‘πŸŸ‘
Sigtweak (Deterministic S2C)πŸŸ’πŸ”΄πŸ”΄πŸŸ’πŸ”΄
Opret (OP_RETURN)πŸ”΄πŸŸ πŸ”΄πŸŸ’πŸŸ’
Algo Tapret: Top-left nodeπŸŸ πŸŸ’πŸŸ’πŸ”΄πŸŸ 
Algo Tapret #4: Any node + proofπŸŸ’πŸŸ’πŸŸ’πŸŸ πŸ”΄

In the course of the study, it became clear that none of the commitment schemes was fully compatible with the current Lightning standard (which does not employ Taproot, muSig2 or additional commitment support). Efforts are underway to modify Lightning's channel construction (BiFrost) to allow the insertion of RGB commitments. This is another area where we need to review the transaction structure, the keys and the way in which channel updates are signed.

The analysis showed that, in fact, other methods (key tweak, sig tweak, witness tweak, etc.) presented other forms of complication:

For RGB, two methods in particular stand out: Opret and Tapret, both classified as "Transaction Output", and compatible with the TxO2 mode used by the protocol.

Multi Protocol Commitments - MPC

In this section, we look at how RGB handles the aggregation of multiple contracts (or, more precisely, their transition bundles) within a single commitment (commitment) recorded in a Bitcoin transaction via a deterministic scheme (according to Opret or Tapret). To achieve this, the order of Merkelization of the various contracts takes place in a structure called MPC Tree (Multi Protocol Commitment Tree). In this section, we'll look at the construction of this MPC Tree, how to obtain its root, and how multiple contracts can share the same transaction confidentially and unambiguously.

Multi Protocol Commitment (MPC) is designed to meet two needs:

In concrete terms, each transition bundle belongs to a particular contract. All this information is inserted into a MPC Tree, whose root (mpc::Root) is then hashed again to give the mpc::Commitment. It is this last hash that is placed in the Bitcoin transaction (witness transaction), according to the deterministic method chosen.

RGB-Bitcoin

MPC Root Hash

The value actually written on-chain (in Opret or Tapret) is called mpc::Commitment. This is calculated in the form of BIP-341, according to the formula:

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

where:

RGB-Bitcoin

MPC Tree construction

To build this MPC Tree, we need to ensure that each contract corresponds to a unique leaf position. Suppose we have:

We then construct a tree of width w and depth d such that 2^d = w, with w > C, so that each contract can be placed in a separate leaf. The position pos(c_i) of each contract in the tree is determined by:

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

where cofactor is an integer that increases the probability of obtaining distinct positions for each contract. In practice, construction follows an iterative process:

The aim is to avoid trees that are too tall, while keeping the risk of collision to a minimum. Note that the collision phenomenon follows a random distribution logic, linked to the Anniversary Paradox.

Inhabited leaves

Once C distinct positions pos(c_i) have been obtained for contracts i = {0,1,..,C-1}, each sheet is filled with a hash function (tagged hash):

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

where:

Uninhabited leaves

The remaining leaves, not assigned to a contract (i.e. w - C leaves), are filled with a "dummy" value (entropy leaf):

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

where:

MPC nodes

After generating the w leaves (inhabited or not), we proceed to merkelization. Any internal nodes are hashed as follows:

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

where:

Progressing in this way, we obtain the root mpc::Root. We can then calculate mpc::Commitment (as explained above) and insert it on-chain.

To illustrate this, let's imagine an example where C=3 (three contracts). Their positions are assumed to be pos(c_0)=7, pos(c_1)=4, pos(c_2)=2. The other leaves (positions 0, 1, 3, 5, 6) are entropy leaves. The diagram below shows the sequence of hashes to the root with:

The final result is the mpc::Root, then the mpc::Commitment.

RGB-Bitcoin

MPC shaft check

When a verifier wishes to ensure that a c_i contract (and its BundleId) is included in the final mpc::Commitment, he simply receives a Merkle proof. This proof indicates the nodes needed to trace the leaves (in this case, c_i's contract leaf) back to the root. There's no need to disclose the entire MPC Tree: this protects the confidentiality of other contracts.

In the example, a c_2 verifier only needs an intermediate hash (tH_MPC_LEAF(D)), two tH_MPC_BRANCH(...), the pos(c_2) position proof and the cofactor value. It can then locally reconstruct the root, then recalculate the mpc::Commitment and compare it to the one written in the Bitcoin transaction (within Opret or Tapret).

RGB-Bitcoin

This mechanism ensures that:

Summary of the MPC structure

Multi Protocol Commitment* (MPC) is the principle that enables RGB to aggregate multiple contracts into a single Bitcoin transaction, while maintaining the uniqueness of commitments and confidentiality vis-Γ -vis other participants. Thanks to the deterministic construction of the tree, each contract is assigned a unique position, and the presence of "dummy" leaves (Entropy Leaves) partially masks the total number of contracts participating in the transaction.

The entire Merkle tree is never stored on the client. We simply generate a Merkle path for each contract concerned, to be transmitted to the recipient (who can then validate the commitment). In some cases, you may have several assets that have passed through the same UTXO. You can then merge several Merkle paths into a so-called multi-protocol commitment block, to avoid duplicating too much data.

Each Merkle proof is therefore lightweight, especially as the tree depth will not exceed 32 in RGB. There's also a notion of "Merkle block", which retains more information (cross-section, entropy, etc.), useful for combining or separating several branches.

That's why it took so long to finalize RGB. We had the overall vision from 2019: putting everything on client-side, circulating tokens off-chain. But for details like sharding for multiple contracts, the structure of the Merkle tree, how to handle collisions and merge proofs... all this required iterations.

Anchors: a global assembly

Following on from the construction of our commitments (Opret or Tapret) and our MPC (Multi Protocol Commitment), we need to address the notion of Anchor in the RGB protocol. An Anchor is a client-side validated structure that brings together the elements needed to verify that a Bitcoin commitment actually contains specific contractual information. In other words, an Anchor summarizes all the data needed to validate the commitments described above.

An Anchor consists of three ordered fields:

Each of these fields plays a part in the validation process, whether it's a matter of reconstructing the underlying Bitcoin transaction or proving the existence of a hidden commitment (particularly in the case of Tapret).

TxId

The Txid field corresponds to the 32-byte identifier of the Bitcoin transaction containing the Opret or Tapret commitment.

In theory, it would be possible to find this Txid by tracing the chain of state transitions which themselves point to each witness transaction, following the logic of Single-use Seals. However, to facilitate and accelerate verification, this Txid is simply included in the Anchor, thus saving the validator from having to go back through the entire off-chain history.

MPC Proof

The second field, MPC Proof, refers to the proof that this particular contract (e.g. c_i) is included in the Multi Protocol Commitment. It is a combination of:

This mechanism was described in the previous section on building the MPC Tree, where each contract obtains a unique leaf thanks to:

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

Then, a deterministic merkelization scheme is used to aggregate all the leaves (contracts + entropy). In the end, the MPC Proof allows the root to be reconstructed locally and compared with the mpc::Commitment included on-chain.

Extra Transaction Proof - ETP

The third field, the ETP, depends on the type of commitment used. If the commitment is of type Opret, no additional proof is required. The validator inspects the first OP_RETURN output of the transaction and finds the mpc::Commitment directly there.

If the commitment is of type Tapret, an additional proof called Extra Transaction Proof - ETP must be provided. It contains:

This additional proof is essential because, unlike Opret, the Tapret commitment is integrated into the structure of a taproot script, which requires revealing part of the taproot tree in order to correctly validate the location of the commitment.

RGB-Bitcoin

The Anchors therefore encapsulate all the information required to validate a Bitcoin commitment in the context of RGB. They indicate both the relevant transaction (Txid) and the proof of contract positioning (MPC Proof), while managing the additional proof (ETP) in the case of Tapret. In this way, an Anchor protects the integrity and uniqueness of the off-chain state by ensuring that the same transaction cannot be reinterpreted for other contractual data.

Conclusion

In this chapter, we covered:

In practice, the technical implementation is divided between several dedicated Rust crates (in client_side_validation, commit-verify, bp_core, etc.). The fundamental notions are there:

RGB-Bitcoin

In the next chapter, we'll look at the purely off-chain component of RGB, namely contract logic. We'll see how RGB contracts, organized as partially replicated finite state machines, achieve much higher expressiveness than Bitcoin scripts, while preserving the confidentiality of their data.

Introduction to smart contracts and their states

In this and the next chapter, we'll look at the notion of smart contract within the RGB environment and explore the different ways in which these contracts can define and evolve their state. We'll see why the RGB architecture, using the ordered sequence of Single-use Seals, makes it possible to execute various types of Contract Operations in a scalable way and without going through a centralized registry. We'll also look at the fundamental role of Business Logic in framing the evolution of the contract state.

Smart contracts and digital bearer rights

RGB's aim is to provide an infrastructure for implementing smart contracts on Bitcoin. By "smart contract" we mean an agreement between several parties that is automatically and computationally enforced, without human intervention to enforce the clauses. In other words, the law of the contract is enforced by the software, not by a trusted third party.

This automation raises the question of decentralization: how can we free ourselves from a centralized registry (e.g. a central platform or database) to manage ownership and contract performance? The original idea, taken up by RGB, is to return to a mode of ownership known as "bearer instruments". Historically, certain securities (bonds, shares, etc.) were issued in bearer form, enabling anyone who physically possessed the document to enforce his or her rights.

RGB-Bitcoin

RGB applies this concept to the digital world: rights (and obligations) are encapsulated in data that is manipulated off-chain, and the status of this data is validated by the participants themselves. This allows, a priori, a much greater degree of confidentiality and independence than that offered by other approaches based on public registers.

Introduction to Smart Contract RGB status

A smart contract in RGB can be seen as a state machine, defined by:

RGB-Bitcoin

It's important to understand that these contracts are not limited to the simple transfer of tokens. They can embody a wide variety of applications: from traditional assets (tokens, stocks, bonds) to more complex mechanics (usage rights, commercial terms, etc.). Unlike other blockchains, where the contract code is accessible and executable by all, RGB's approach compartmentalizes access and knowledge of the contract to participants ("contract participants"). There are several roles:

This separation of roles contributes to censorship resistance, by ensuring that only authorized persons can interact with the contractual state. It also gives RGB the ability to scale horizontally: the majority of validations take place outside the blockchain, and only cryptographic anchors (the commitments) are inscribed on Bitcoin.

Status and Business Logic in RGB

From a practical point of view, the contract's Business Logic takes the form of rules and scripts, defined in what RGB calls a Schema. The Schema encodes:

At the same time, the Contract State often breaks down into two components:

As we'll see in the following chapters, any status update (Contract Operation) must dock to a Bitcoin commitment (via Opret or Tapret) and comply with the Business Logic scripts to be considered valid.

Contract Operations: creation and evolution of the State

In the RGB universe, a Contract Operation is any event that changes the contract from a old state to a new state. These operations follow the following logic:

RGB-Bitcoin

The end result is an updated contract, now with a different state. This transition does not require the entire Bitcoin network to be concerned with the details, since only a small cryptographic fingerprint (the commitment) is recorded in the blockchain. The sequence of Single-use Seals prevents any double-spending or double-use of the State.

Operations chain: from Genesis to Terminal State

To put this into perspective, an RGB smart contract starts with a Genesis, the very first state. Thereafter, various Contract Operations follow one another, forming a DAG (Directed Acyclic Graph) of operations:

RGB-Bitcoin

This DAG topology (instead of a simple linear chain) reflects the possibility that different parts of the contract may evolve in parallel, as long as they do not contradict each other. RGB then takes care of avoiding any inconsistencies by client-side verification of each participant involved.

Summary

Smart contracts in RGB introduce a model of digital bearer instruments, decentralized but anchored in Bitcoin for time-stamping and guaranteeing the order of transactions. Automated execution of these contracts is based on:

In the next chapter, we'll go into more detail about the concrete representation of these states and state transitions at the off-chain level, and how they relate to the UTXOs and Single-use Seals embedded in Bitcoin. This will be an opportunity to see how RGB's internal mechanics, based on client-side validation, manage to maintain the consistency of smart contracts while preserving data confidentiality.

RGB contract operations

In this chapter, we'll look at how operations in smart contracts and state transitions work, again within the RGB protocol. The aim will also be to understand how several participants cooperate to transfer ownership of an asset.

State transitions and their mechanics

The general principle is still that of Client-side Validation, where state data is held by the owner and validated by the recipient. However, the specificity here with RGB lies in the fact that Bob, as recipient, asks Alice to incorporate certain information into the contract data in order to have real control over the asset received, via a hidden reference to one of his UTXOs.

To illustrate the process of a State Transition (which is one of the fundamental Contract Operations in RGB), let's take a step-by-step example of an asset transfer between Alice and Bob:

Initial situation:

Alice has a stash RGB of locally validated data (client-side). This stash refers to one of her UTXOs on Bitcoin. This means that a seal definition in this data points to a UTXO belonging to Alice. The idea is to enable her to transfer certain digital rights linked to an asset (e.g. RGB tokens) to Bob.

RGB-Bitcoin

Bob also has UTXOs:

Bob, on the other hand, has at least one UTXO of his own, with no direct link to Alice's. In the event that Bob has no UTXO, it is still possible to make the transfer to him using the witness transaction itself: the output of this transaction will then include the commitment (commitment) and implicitly associate ownership of the new contract with Bob.

RGB-Bitcoin

Construction of the new property (New State):

Bob sends Alice information encoded in the form of an invoice (we'll go into more detail on invoice construction in later chapters), asking her to create a new state that conforms to the rules of the contract. This state will include a new seal definition pointing to one of Bob's UTXOs. In this way, Bob is given ownership of the assets defined in this new state, for example a certain amount of RGB tokens.

RGB-Bitcoin

Preparation of the sample transaction:

Alice then creates a Bitcoin transaction spending the UTXO referenced in the previous seal (the one that legitimized her as the holder). In the output of this transaction, a commitment (via Opret or Tapret) is inserted to anchor the new RGB state. The Opret or Tapret commitments are derived from a MPC tree (as seen in previous chapters), which can aggregate several transitions from different contracts.

Transmission of Consignment to Bob:

Before broadcasting the transaction, Alice sends Bob a Consignment containing all the necessary client-side data (his stash) and the new state information in Bob's favor. At this point, Bob applies the RGB consensus rules:

Transition completion:

If Bob is satisfied, he may give his approval (for example, by signing the consignment). Alice can then broadcast the prepared sample transaction. Once confirmed, this closes the seal previously held by Alice and formalizes ownership by Bob. Anti-double-spending security is then based on the same mechanism as in Bitcoin: the UTXO is spent, proving that Alice can no longer reuse it.

RGB-Bitcoin

The new state now references Bob's UTXO, giving Bob the ownership previously held by Alice. The Bitcoin output where the RGB data is anchored becomes the irrevocable proof of the transfer of ownership.

An example of a minimal DAG (Directed Acyclic Graph) comprising two contract operations (a Genesis then a State Transition) can illustrate how the RGB state (client-side layer, in red) connects to the Bitcoin blockchain (Commitment layer, in orange).

RGB-Bitcoin

It shows that a Genesis defines a seal (seal definition), then a State Transition closes this seal to create a new one in another UTXO.

In this context, here are a few reminders of terminology:

State Transitions, described in the previous chapter, are the main form of contract operation. They refer to one or more previous states (from Genesis or another State Transition) and update them to a new state.

RGB-Bitcoin

This diagram shows how, in a State Transition Bundle, several seals can be closed in a single sample transaction, while simultaneously opening new seals. Indeed, an interesting feature of the RGB protocol is its ability to scale: several transitions can be aggregated into a Transition Bundle, each aggregation being associated with a distinct leaf of the MPC tree (a unique bundle identifier). Thanks to the Deterministic Bitcoin Commitment (DBC) mechanism, the entire message is inserted into a Tapret or Opret output, while closing previous seals and possibly defining new ones. The Anchor serves as a direct link between the commitment stored in the blockchain and the client-side validation structure (client-side).

In the following chapters, we'll look at all the components and processes involved in building and validating a State Transition. Most of these elements are part of the RGB consensus, implemented in the RGB Core Library.

Transition Bundle

On RGB, it is possible to bundle different State Transitions belonging to the same contract (i.e. sharing the same ContractId, derived from the Genesis OpId). In the simplest case, as between Alice and Bob in the example above, a Transition Bundle contains just one transition. But support for multi-payer operations (such as coinjoins, Lightning channel openings, etc.) means that several users can combine their State Transitions in a single bundle.

Once collected, these transitions are anchored (by the MPC + DBC mechanism) in a single Bitcoin transaction:

Technically speaking, the BundleId inserted in the MPC sheet is obtained from a tagged hash applied to the strict serialization of the bundle's InputMap field:

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

In which bundle_tag = urn:lnp-bp:rgb:bundle#2024-02-03 for example.

The InputMap is a data structure which lists, for each input i of the sample transaction, the reference to the OpId of the corresponding State Transition. For example:

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 

By referencing each entry only once and in an orderly fashion, we prevent the same seal from being spent twice in two simultaneous State Transitions.

State Generation and Active State

State Transitions can therefore be used to transfer ownership of an asset from one person to another. However, they are not the only possible operations in the RGB protocol. The protocol defines three Contract Operations:

Among these, Genesis and State Extension are sometimes called "State Generation operations", because they create new states without immediately closing any. This is a very important point: Genesis and State Extension do not involve closing a seal. Rather, they define a new seal, which must then be spent by a subsequent State Transition to be truly validated in the blockchain history.

RGB-Bitcoin

The Active State of a contract is often defined as the set of latest states resulting from the history (the DAG) of transactions, starting with the Genesis and following all anchors in the Bitcoin blockchain. Any old states that are already obsolete (i.e. attached to spent UTXOs) are no longer considered active, but remain essential for checking the consistency of the history.

Genesis

The Genesis is the starting point of every RGB contract. It is created by the contract issuer and defines the initial parameters, in accordance with the Schema. In the case of an RGB token, the Genesis may specify, for example:

Being the first transaction in the contract, the Genesis does not reference any previous state, nor does it close any seal. However, to appear in the history and be validated, the Genesis must be consumed (closed) by a first State Transition (often a scan/auto-spend transaction to the issuer itself, or the initial distribution to users).

State Extension

State Extensions offer an original feature for smart contracts. They make it possible to redeem certain digital rights (Valencies) provided for in the contract definition, without immediately closing the seal. Most often, this concerns:

Technically speaking, a State Extension references a Redeem (a particular type of RGB input) that corresponds to a Valency defined previously (for example, in Genesis or another State Transition). It defines a new seal, available to the person or condition benefiting from it. For this seal to become effective, it must be spent by a subsequent State Transition.

RGB-Bitcoin

For example: the Genesis creates a right of issue (Valency). This can be exercised by an authorized actor, who then builds a State Extension:

Components of a Contract Operation

I'd now like to take a detailed look at each of the constituent elements of a Contract Operation in RGB. A Contract Operation is the action which modifies the state of a contract, and which is validated on the client side, in a deterministic way, by the legitimate recipient. In particular, we'll see how the Contract Operation takes into account, on the one hand, the old state of the contract, and on the other, the definition of a 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 |         ...              |  |    
+------+       |  | | +----------+ +-------------+ |              |  |  +-------------+  +-------------+                          |  |    
               |  | +------------------------------+              |  |                                                            |  |   
               |  |                                               |  |                                                            |  |      
               |  +-----------------------------------------------+  +------------------------------------------------------------+  |    
               |                                                                                                                     |    
               +---------------------------------------------------------------------------------------------------------------------+

If we look at the diagram above, we can see that a Contract Operation includes elements referring to the New State and others referring to the updated Old State.

The elements of the New State are:

The Old State is referenced via:

In addition, a Contract Operation includes more general fields specific to the operation:

Finally, all these fields are condensed by a customized hashing process, to produce a unique fingerprint, the OpId. This OpId is then integrated into the Transition Bundle, enabling it to be authenticated and validated within the protocol.

Each Contract Operation is therefore identified by a 32-byte hash named OpId. This hash is calculated by a SHA256 hash of all the elements making up the operation. In other words, each Contract Operation has its own cryptographic commitment, which includes all the data needed to verify the authenticity and consistency of the operation.

An RGB contract is then identified by a ContractId, derived from the Genesis OpId (since there is no pre-Genesis operation). In concrete terms, we take the Genesis OpId, reverse the byte order and apply a Base58 encoding. This encoding makes the ContractId easier to handle and recognize.

Status update methods and rules

The Contract State represents the set of information that the RGB protocol must track for a given contract. It is composed of:

RGB-Bitcoin

The Global State is directly included in the Contract Operation as a single block. The Owned States are defined in each Assignment, alongside the Seal Definition.

A major feature of RGB is the way in which the Global State and Owned States are modified. There are generally two types of behavior:

If, in the contract, a state element is not defined as mutable or cumulative, this element will remain empty for subsequent operations (in other words, there are no new versions for this field). It's the contract Schema (i.e. the coded business logic) that determines whether a state (Global or Owned) is mutable, cumulative or fixed. Once the Genesis has been defined, these properties can only be modified if the contract itself allows it, for example via a specific State Extension.

The table below illustrates how each type of Contract Operation can manipulate (or not) the Global State and the Owned State:

GenesisState ExtensionState Transition
Addition of Global State+-+
Mutation of Global Staten/a-+
Addition of Owned State+-+
Mutation of Owned Staten/aNo+
Addition of Valencies+++

+: action possible if the contract's Schema allows it.

-: the operation must be confirmed by a subsequent State Transition (the State Extension alone does not close the Single-use Seal).

In addition, the temporal scope and update rights of each type of data can be distinguished in the following table:

MetadataGlobal StateOwned State
ScopeDefined for a single Contract OperationDefined globally for the contractDefined for each seal (Assignment)
Who can update it?Non-updatable (ephemeral data)Operation issued by actors (issuer, etc.)Depends on the legitimate holder who owns the seal (the one who can spend it in a following transaction)
Temporal ScopeOnly for the current operationState is established at the end of the operationState is defined before the operation (by the Seal Definition of the previous operation)

Global State

The Global State is often described as "nobody owns, everybody knows". It contains general information about the contract, which is publicly visible. For example, in a token-issuing contract, it potentially contains:

This Global State can be placed on public resources (websites, IPFS, Nostr, Torrent, etc.) and distributed to the community. Also, the economic incentive (the need to hold and transfer these tokens, etc.) naturally drives contract users to maintain and propagate this data themselves.

Assignments

The Assignment is the basic structure for defining:

An Assignment can be seen as the analogue of a Bitcoin transaction output, but with more flexibility. Herein lies the logic of property transfer: the Assignment associates a particular type of asset or right (AssignmentType) with a seal. Whoever possesses the private key of the UTXO linked to this seal (or whoever can spend this UTXO) is considered the owner of this Owned State.

One of RGB's great strengths lies in the ability to reveal or hide (conceal) the Seal Definition and Owned State fields at will. This offers a powerful combination of confidentiality and selectivity. For example, you can prove that a transition is valid without disclosing all the data, by providing the revealed version to the person who has to validate it, while third parties only see the hidden version (a hash). In practice, the OpId of a transition is always calculated from the concealed data.

RGB-Bitcoin

Seal Definition

The Seal Definition, in its revealed form, has four basic fields: txptr, vout, blinding and method:

The concealed form of the Seal Definition is a SHA256 hash (tagged) of the concatenation of these 4 fields, with a tag specific to RGB.

RGB-Bitcoin

Owned States

The second component of Assignment is the Owned State. Unlike the Global State, it can exist in public or private form:

RGB defines four possible state types (StateTypes) for an Owned State:

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

With, for example:

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)

With, for example:

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

To summarize, here are the 4 possible types of state in the public and hidden form:

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

ElementDeclarativeFungibleStructuredAttachments
DataNoneSigned or unsigned 64-bit integerAny strict data typeAny file
Info TypeNoneSigned or unsignedStrict typesMIME Type
PrivacyNot requiredPedersen commitmentHash with blindingHashed file identifier
Size LimitsN/A256 bytesUp to 64 KBUp to ~500 GB

Inputs

The Inputs of a Contract Operation refer to the Assignments that are being spent in this new operation. An Input indicates:

Inputs never appear in Genesis, since there are no previous Assignments. Nor do they appear in State Extensions (because State Extensions don't close seals; rather, they redefine new seals based on Valencies).

When we have Owned States of type Fungible, the validation logic (via the AluVM script provided in the Schema) checks the consistency of the sums: the sum of incoming tokens (Inputs) must be equal to the sum of outgoing tokens (in the new Assignments).

Metadata

The Metadata field can be up to 64 KiB and is used to include temporary data useful for validation, but not integrated into the permanent state of the contract. For example, intermediate calculation variables for complex scripts can be stored here. This space is not intended to be stored in the global history, which is why it is outside the scope of Owned States or Global State.

Valencies

Valencies are an original RGB protocol mechanism. They can be found in Genesis, State Transitions or State Extensions. They represent numerical rights that can be activated by a State Extension (via Redeems), then finalized by a subsequent Transition. Each Valency is identified by a ValencyType (16 bits). Its semantics (reissue right, token swap, burn right, etc.) are defined in the Schema.

In concrete terms, we could imagine a Genesis defining a "right to reissue" valency. A State Extension will consume it (Redeem) if certain conditions are met, in order to introduce a new quantity of tokens. Then, a State Transition emanating from the holder of the seal thus created can transfer these new tokens.

Redeems

Redeems are the Valency equivalent of Inputs for Assignments. They only appear in State Extensions, as this is where a previously defined Valency is activated. A Redeem consists of two fields:

Example: a Redeem can correspond to a CoinSwap execution, depending on what was coded in the Valency.

RGB status characteristics

We're now going to take a look at several fundamental state characteristics in RGB. In particular, we'll look at:

As always, bear in mind that everything to do with contract status is validated on the client side according to consensus rules set out in the protocol, and whose ultimate cryptographic reference is anchored in Bitcoin transactions.

Strict Type System

RGB uses a Strict Type System and a deterministic serialization mode (Strict Encoding). This organization is designed to guarantee perfect reproducibility and precision in the definition, handling and validation of contract data.

In many programming environments (JSON, YAML...), the data structure can be flexible, even too permissive. In RGB, on the other hand, the Structure and Types of each field are defined with explicit constraints. For example:

Thanks to this strict encoding protocol:

In practice, the structure (Schema) and the resulting code (Interface and associated logic) are compiled. A descriptive language is used to define the contract (types, fields, rules) and generate a strict binary format. When compiled, the result is:

The strict type system also enables precise monitoring of changes: any modification to the structure (even a change of field name) is detectable and can lead to a change in the overall footprint.

Finally, each compilation produces a fingerprint, a cryptographic identifier that attests to the exact version of the code (data, rules, validation). For example, an identifier of the form:

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

This makes it possible to manage consensus or implementation updates, while ensuring detailed traceability of the versions used in the network.

To prevent the state of an RGB contract from becoming too cumbersome to validate on the client side, a consensus rule imposes a maximum size of 2^16 bytes (64 Kio) for any data involved in validation calculations. This applies to each variable or structure: no more than 65536 bytes, or the equivalent in numbers (32768 16-bit integers, etc.). This also applies to collections (lists, sets, maps), which may not exceed 2^16 elements.

This limit guarantees:

The Validation != Ownership paradigm

One of RGB's major innovations is the strict separation between two concepts:

Validation takes place at the level of the RGB software stack (libraries, commitments protocol, etc.). Its role is to ensure that the internal rules of the contract (amounts, permissions, etc.) are respected. Observers or other participants can also validate the data history.

Ownership, on the other hand, relies entirely on Bitcoin's security. Owning the private key of a UTXO means controlling the ability to launch a new transition (closing the Single-use Seal). So, even if someone can see or validate the data, they can't change the state if they don't own the UTXO concerned.

RGB-Bitcoin

This approach limits the classic vulnerabilities encountered in more complex blockchains (where all the code of a smart contract is public and modifiable by anyone, which has sometimes led to hacks). On RGB, an attacker cannot simply interact with the on-chain state, as the right to act on the state (ownership) is protected by the Bitcoin layer.

What's more, this decoupling allows RGB to integrate naturally with the Lightning Network. Lightning channels can be used to engage and move RGB assets without engaging on-chain commitments every time. We'll take a closer look at this integration of RGB on Lightning in later chapters of the course.

Consensus developments in RGB

In addition to semantic code versioning, RGB includes a system for evolving or updating a contract's consensus rules over time. There are two main forms of evolution:

A fast-forward occurs when a previously invalid rule becomes valid. For example, if the contract evolves to allow a new type of AssignmentType or a new field:

A push-back means that a previously valid rule becomes invalid. It is therefore a "hardening" of the rules, but not strictly speaking a softfork:

In this chapter on RGB contract operations, we've explored the fundamental principles underlying this protocol. As you will have noticed, the inherent complexity of the RGB protocol requires the use of many technical terms. So, in the next chapter, I'll provide you with a glossary that will summarize all the concepts covered in this first theoretical part, with definitions of all the technical terms relating to RGB. Then, in the next section, we'll take a practical look at the definition and implementation of RGB contracts.

RGB Glossary

If you need to come back to this short glossary of important technical terms used in the RGB world (listed in alphabetical order), you'll find it useful. This chapter isn't essential if you've already understood everything we've covered in the first section.

AluVM

The abbreviation AluVM stands for "Algorithmic logic unit Virtual Machine", a register-based virtual machine designed for smart contract validation and distributed computing. It is used (but not exclusively reserved) for the validation of RGB contracts. Scripts or operations included in an RGB contract can thus be executed in the AluVM environment.

For further information: AluVM official website

Anchor

An Anchor represents a set of client-side data used to prove the inclusion of a unique commitment in a transaction. In the RGB protocol, an Anchor consists of the following elements:

An Anchor therefore serves to establish a verifiable link between a specific Bitcoin transaction and private data validated by the RGB protocol. It guarantees that these data are indeed included in the blockchain, without their exact content being publicly exposed.

Assignment

In RGB's logic, an Assignment is the equivalent of a transaction output that modifies, updates or creates certain properties within the state of a contract. An Assignment comprises two elements:

An Assignment therefore indicates that a portion of the state (for example, an asset) is now allocated to a particular holder, identified via a Single-use Seal linked to a UTXO.

Business Logic

The Business Logic groups together all the rules and internal operations of a contract, described by its schema (i.e. the structure of the contract itself). It dictates how the state of the contract can evolve, and under what conditions.

Client-side Validation

Client-side Validation refers to the process by which each party (client) verifies a set of data exchanged privately, according to the rules of a protocol. In the case of RGB, this exchanged data is grouped together in what are known as consignments. Unlike the Bitcoin protocol, which requires all transactions to be published on-chain, RGB allows only commitments (anchored in Bitcoin) to be stored in public, while the essential contract information (transitions, attestations, proofs) remains off-chain, shared only between the users concerned.

Commitment

A Commitment (in the cryptographic sense) is a mathematical object, denoted C, derived deterministically from an operation on structured data m (the message) and a random value r. We write:

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

This mechanism comprises two main operations:

A commitment must respect two properties:

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

Such as:

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

In the RGB protocol, a commitment is included in a Bitcoin transaction to prove the existence of a certain piece of information at a given time, without revealing the information itself.

Consignment

A Consignment groups together the data exchanged between the parties, subject to Client-side Validation in RGB. There are two main categories of consignment:

These consignments are not recorded publicly on the blockchain; they are exchanged directly between the parties concerned via the communication channel of their choice.

Contract

A Contract is a set of rights executed digitally between several actors via the RGB protocol. It has an active state and a business logic, defined by a Schema, which specifies which operations are authorized (transfers, extensions, etc.). The state of a contract, as well as its validity rules, are expressed in the Schema. At any given time, the contract evolves only in accordance with what is permitted by this Schema and by validation scripts (run, for example, in AluVM).

Contract Operation

A Contract Operation is a contract status update performed according to Schema rules. The following operations exist in RGB:

Each operation modifies the state by adding or replacing certain data (Global State, Owned State...).

Contract Participant

A Contract Participant is an actor who takes part in operations relating to the contract. In RGB, a distinction is made between:

Contract Rights

Contract Rights refer to the various rights that can be exercised by those involved in an RGB contract. They fall into several categories:

Contract State

The Contract State corresponds to the current state of a contract at a given moment. It can be made up of both public and private data, reflecting the state of the contract. RGB distinguishes between:

Deterministic Bitcoin Commitment - DBC

Deterministic Bitcoin Commitment (DBC) is the set of rules used to provably and uniquely register a commitment in a Bitcoin transaction. In the RGB protocol, there are two main forms of DBC:

These mechanisms define precisely how the commitment is encoded in the output or structure of a Bitcoin transaction, to ensure that this commitment is deterministically traceable and verifiable.

Directed Acyclic Graph - DAG

A DAG (or Acyclic Guided Graph) is a cycle-free graph, enabling topological scheduling. Blockchains, like the shards of RGB contracts, can be represented by DAGs.

For further information: Directed Acyclic Graph

Engraving

Engraving is an optional data string that successive owners of a contract can enter into the contract history. This feature exists, for example, in the RGB21 interface and enables commemorative or descriptive information to be added to the contract history.

Extra Transaction Proof - ETP

The ETP (Extra Transaction Proof) is the part of the Anchor that contains the additional data required to validate a Tapret commitment (in the context of taproot). It includes, among other things, the taproot script's internal public key (internal PubKey) and information specific to the Script Path Spend.

Genesis

Genesis refers to the set of data, governed by a Schema, that forms the initial state of any contract in RGB. It can be compared to Bitcoin's Genesis Block concept, or to the Coinbase transaction concept, but here at the client-side and RGB token level.

Global State

The Global State is the set of public properties contained in the Contract State. It is defined at Genesis and, depending on the contract rules, can be updated by authorized transitions. Unlike Owned States, the Global State does not belong to a particular entity; it is closer to a public registry within the contract.

Interface

The Interface is the set of instructions used to decode the binary data compiled in a Schema or in contract operations and their states, in order to make them readable for the user or his wallet. It acts as an interpretation layer.

Interface Implementation

Interface Implementation is the set of declarations that link an Interface to a Schema. It enables the semantic translation performed by the Interface itself, so that the raw data of a contract can be understood by the user or the software involved (the wallets).

Invoice

An Invoice takes the form of a URL encoded in base58, which embeds the data necessary for the construction of a State Transition (by the payer). In other words, it's an invoice enabling the counterparty (payer) to create the corresponding transition to transfer the asset or update the state of the contract.

Lightning Network

The Lightning Network is a decentralized network of payment channels (or state channels) on Bitcoin, made up of 2/2 multi-signature wallets. It enables fast, low-cost off-chain transactions, while relying on Bitcoin's Layer 1 for arbitration (or closure) when necessary.

For more information on how Lightning works, I recommend you take this other course:

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

Multi Protocol Commitment - MPC

Multi Protocol Commitment (MPC) refers to the Merkle tree structure used in RGB to include, within a single Bitcoin transaction, several Transition Bundles from different contracts. The idea is to group together several commitments (potentially corresponding to different contracts or different assets) in a single anchor point in order to optimize the occupation of block space.

Owned State

An Owned State is the part of a Contract State that is enclosed in an Assignment and associated with a particular holder (via a Single-use Seal pointing to a UTXO). This represents, for example, a digital asset or a specific contractual right assigned to that person.

Ownership

Ownership refers to the ability to control and spend a UTXO referenced by a Seal Definition. When an Owned State is linked to a UTXO, the owner of this UTXO has the right, potentially, to transfer or evolve the associated state, according to the rules of the contract.

Partially Signed Bitcoin Transaction - PSBT

A PSBT (Partially Signed Bitcoin Transaction) is a Bitcoin transaction that is not yet fully signed. It can be shared between several entities, each of which can add or verify certain elements (signatures, scripts...), until the transaction is deemed ready for on-chain distribution.

For further information: BIP-0174

Pedersen commitment

A Pedersen commitment is a type of cryptographic commitment with the property of being homomorphic with respect to the addition operation. This means that it is possible to validate the sum of two commitments without revealing the individual values.

Formally, if:

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

then:

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

This property is useful, for example, for concealing the amounts of tokens exchanged, while still being able to verify the totals.

Further information: Pedersen commitment

Redeem

In a State Extension, a Redeem refers to the action of reclaiming (or exploiting) a previously declared Valency. As a Valency is a public right, the Redeem allows an authorized participant to claim a specific contract state extension.

Schema

A Schema in RGB is a declarative piece of code describing the set of variables, rules and business logic (Business Logic) that govern the operation of a contract. The Schema defines the state structure, the types of transitions allowed and the validation conditions.

Seal Definition

The Seal Definition is the part of an Assignment that associates the commitment with a UTXO owned by the new holder. In other words, it indicates where the condition is located (in which UTXO), and establishes ownership of an asset or right.

Shard

A Shard represents a branch in the DAG of the State Transitions history of an RGB contract. In other words, it is a coherent subset of the contract's overall history, corresponding, for example, to the sequence of transitions required to prove the validity of a given asset since the Genesis.

Single-use Seal

A Single-use Seal is a cryptographic promise of commitment to an as yet unknown message, which will be revealed only once in the future and must be known by all members of a specific audience. The aim is to prevent the creation of multiple competing commitments for the same seal.

Stash

The Stash is the set of client-side data that a user stores for one or more RGB contracts, for the purpose of validation (Client-side Validation). This includes transition history, consignments, proofs of validity, etc. Each holder retains only the parts of the history they need (shards).

State Extension

A State Extension is a contract operation used to re-trigger state updates by redeeming previously declared Valencies. To be effective, a State Extension must then be closed by a State Transition (which updates the final state of the contract).

State Transition

State Transition is the operation that changes the state of an RGB contract to a new state. It can modify Global State and/or Owned State data. In practice, each transition is verified by Schema rules and anchored in the Bitcoin blockchain via a commitment.

Taproot

Refers to Bitcoin's Segwit v1 transaction format, introduced by BIP341 and BIP342. Taproot improves the confidentiality and flexibility of scripts, in particular by making transactions more compact and harder to distinguish from one another.

Terminal Consignment - Consignment Endpoint

The Terminal Consignment (or Consignment Endpoint) is a transfer consignment containing the final state of the contract, including the State Transition created from the recipient's Invoice (payee). It is therefore the endpoint of a transfer, with the necessary data to prove that ownership or state has been transferred.

Transition Bundle

A Transition Bundle is a set of RGB State Transitions (belonging to the same contract) that are all engaged in the same witness transaction Bitcoin. This makes it possible to bundle several updates or transfers into a single on-chain anchor.

UTXO

A Bitcoin UTXO (Unspent Transaction Output) is defined by the hash of a transaction and the output index (vout). It is also sometimes called an outpoint. In the RGB protocol, reference to an UTXO (via a Seal Definition) enables the location of the Owned State, i.e. the property held on the blockchain.

Valency

A Valency is a public right which does not require state storage as such, but which can be redeemed via a State Extension. It is therefore a form of possibility open to all (or certain players), declared in the logic of the contract, in order to carry out a particular extension at a later date.

Witness Transaction

The Witness Transaction is the Bitcoin transaction that closes the Single-use Seal around a message containing a Multi Protocol Commitment (MPC). This transaction spends a UTXO or creates one, so as to seal the commitment linked to the RGB protocol. It acts as an on-chain proof that the state has been set at a specific point in time.

Programming on RGB

Implementing RGB contracts

In this chapter, we'll take a closer look at how an RGB contract is defined and implemented. We'll see what the components of an RGB contract are, what their roles are and how they are constructed.

The components of an RGB contract

So far, we've already discussed the Genesis, which represents the starting point of a contract, and we've seen how it fits in with the logic of a Contract Operation and the state of the protocol. The complete definition of an RGB contract, however, is not limited to the Genesis alone: it involves three complementary components which, together, form the heart of the implementation.

The first component is called the Schema. This is a file describing the fundamental structure and business logic (business logic) of the contract. It specifies the data types used, the validation rules, the operations permitted (e.g. initial token issuance, transfers, special conditions, etc.) - in short, the general framework that dictates how the contract works.

The second component is the Interface. It focuses on how users (and by extension, portfolio software) will interact with this contract. It describes the semantics, i.e. the readable representation of the various fields and actions. So, while the Schema defines how the contract works technically, the Interface defines how to present and expose these functionalities: method names, data display, etc.

The third component is the Interface Implementation, which complements the previous two by acting as a kind of bridge between the Schema and the Interface. In other words, it associates the semantics expressed by the Interface with the underlying rules defined in the Schema. It is this implementation that will manage, for example, the conversion between a parameter entered in the wallet and the binary structure imposed by the protocol, or the compilation of validation rules in machine language.

This modularity is an interesting feature of RGB, as it allows different groups of developers to work separately on these aspects (Schema, Interface, Implementation), as long as they follow the protocol's consensus rules.

To sum up, each contract consists of:

RGB-Bitcoin

It's important to note that for a wallet to manage an RGB asset (be it a fungible token or a right of any kind), it must have all these elements compiled: Schema, Interface, Interface Implementation and Genesis. This is transmitted via a contract consignment, i.e. a data package containing everything needed to validate the client-side contract.

To help clarify these notions, here is a summary table comparing the components of an RGB contract with concepts already known either in object-oriented programming (OOP) or in the Ethereum ecosystem:

RGB Contract ComponentMeaningOOP EquivalentEthereum Equivalent
GenesisInitial state of the contractClass constructorContract constructor
SchemaBusiness logic of the contractClassContract
InterfaceSemantics of the contractInterface (Java) / Trait (Rust) / Protocol (Swift)ERC Standard
Interface ImplementationMapping semantics and logicImpl (Rust) / Implements (Java)Application Binary Interface (ABI)

The left-hand column shows the elements specific to the RGB protocol. The middle column shows the concrete function of each component. Then, in the "OOP equivalent" column, we find the equivalent term in object-oriented programming:

In the Ethereum context, the Genesis is closer to the contract constructor, the Schema to the contract definition, the Interface to a standard such as ERC-20 or ERC-721, and the Interface Implementation to the ABI (Application Binary Interface), which specifies the format of interactions with the contract.

The advantage of RGB's modularity also lies in the fact that different stakeholders can write, for example, their own Interface Implementation, as long as they respect the logic of the Schema and the semantics of the Interface. Thus, an issuer could develop a new, more user-friendly front-end (Interface), without modifying the logic of the contract, or conversely, one could extend the Schema to add functionality, and provide a new version of the adapted Interface Implementation, while the old implementations would remain valid for basic functionality.

When we compile a new contract, we generate a Genesis (the first step in issuing or distributing the asset), as well as its components (Schema, Interface, Interface Implementation). After this, the contract is fully operational and can be propagated to wallets and users. This method, where Genesis is combined with these three components, guarantees a high degree of customization (each contract can have its own logic), decentralization (everyone can contribute to a given component), and security (validation remains strictly defined by the protocol, without depending on arbitrary on-chain code as is often the case on other blockchains).

I'd now like to take a closer look at each of these components: the Schema, the Interface and the Interface Implementation.

Schema

In the previous section, we saw that in the RGB ecosystem, a contract is made up of several elements: the Genesis, which establishes the initial state, and several other complementary components. The purpose of the Schema is to declaratively describe all the business logic of the contract, i.e. the data structure, the types used, the permitted operations and their conditions. It is therefore a very important element in making a contract operational on the client side, since each participant (a wallet, for example) must check that the state transitions it receives conform to the logic defined in the Schema.

A Schema can be likened to a "class" in object-oriented programming (OOP). Generally speaking, it serves as a model defining the components of a contract, such as:

RGB-Bitcoin

When the issuer of an asset on RGB publishes a contract, it provides the Genesis and Schema associated with it. Users or wallets who wish to interact with the asset retrieve this Schema to understand the logic behind the contract, and to be able to verify later that the transitions they will participate in are legitimate.

The first step, for anyone receiving information about an RGB asset (e.g. a token transfer), is to validate this information against the Schema. This involves using the Schema compilation to:

In practice, Schema is not executable code, as can be seen in blockchains that store on-chain code (EVM on Ethereum). On the contrary, RGB separates business logic (declarative) from executable code on the blockchain (which is limited to cryptographic anchors). Thus, the Schema determines the rules, but the application of these rules takes place outside the blockchain, at each participant's site, according to the Client-side Validation principle.

A Schema must be compiled before it can be used by RGB applications. This compilation produces a binary file (e.g. .rgb) or an encrypted binary file (.rgba). When the wallet imports this file, it knows:

As explained in previous chapters, the strict type system gives us a stable, deterministic encoding format: all variables, whether Owned States, Global States or Valencies, are described precisely (size, lower and upper bounds if necessary, signed or unsigned type, etc.). It is also possible to define nested structures, for example to support complex use cases.

Optionally, the Schema can reference a root SchemaId, which facilitates the reuse of an existing basic structure (a template). In this way, you can evolve a contract or create variations (e.g. a new type of token) from an already proven template. This modularity avoids the need to recreate entire contracts, and encourages the standardization of best practices.

Another important point is that the logic of state evolution (transfers, updates, etc.) is described in the Schema in the form of scripts, rules and conditions. So, if the contract designer wishes to authorize a reissue or impose a burn mechanism (destruction of tokens), he can specify the corresponding scripts for AluVM in the validation part of the Schema.

Difference from programmable on-chain blockchains

Unlike systems like Ethereum, where the smart contract code (executable) is written into the blockchain itself, RGB stores the contract (its logic) off-chain, in the form of a compiled declarative document. This implies that:

Use by the issuer and by users

When a issuer creates an asset (for example, a non-inflationary fungible token), it prepares:

It then makes the compiled Schema (a .rgb file) available to users, so that anyone receiving a transfer of this token can check the consistency of the operation locally. Without this Schema, a user would not be able to interpret the status data or check that it complies with the contract rules.

So when a new wallet wants to support an asset, it simply needs to integrate the relevant Schema. This mechanism makes it possible to add compatibility to new RGB asset types, without invasively changing the wallet's software base: all that's required is to import the Schema binary and understand its structure.

The Schema defines the business logic in RGB. It lists the evolution rules of a contract, the structure of its data (Owned States, Global State, Valencies) and the associated validation scripts (executable by AluVM). Thanks to this declarative document, the definition of a contract (compiled file) is clearly separated from the actual execution of the rules (client-side). This decoupling gives RGB great flexibility, enabling a wide range of use cases (fungible tokens, NFT, more sophisticated contracts) while avoiding the complexity and flaws typical of programmable on-chain blockchains.

Schema example

Let's take a look at a concrete example of Schema for an RGB contract. This is an extract in Rust from the file nia.rs (initials for "Non-Inflatable Assets"), which defines a model for fungible tokens that cannot be reissued beyond their initial supply (a non-inflationary asset). This type of token can be seen as the equivalent, in the RGB universe, of the ERC20 on Ethereum, i.e. fungible tokens that respect certain basic rules (e.g. on transfers, supply initialization, etc.).

Before diving into the code, it's worth recalling the general structure of an RGB Schema. There is a series of declarations framing:

RGB-Bitcoin

The code below shows the complete definition of the Rust Schema. We will comment it part by part, following the annotations (1) to (9) below:

// ===== 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),
            },
        }),
    }
}

The nia_schema() function returns a SubSchema, indicating that this Schema can partially inherit from a more generic schema. In the RGB ecosystem, this flexibility makes it possible to reuse certain standard elements of a master schema, and then define rules specific to the contract in question. Here, we choose not to enable inheritance, since subset_of will be None.

The ffv property corresponds to the fast-forward version of the contract. A value of zero!() here indicates that we are at version 0 or the initial version of this schema. If you later wish to add new functionalities (new type of operation, etc.), you can increment this version to indicate a consensus change.

The subset_of: None property confirms the absence of inheritance. The type_system field refers to the strict type system already defined in the types library. This line indicates that all data used by the contract uses the strict serialization implementation provided by the library in question.

In the global_types block, we declare four elements. We use the key, such as GS_NOMINAL or GS_ISSUED_SUPPLY, to reference them later:

The keyword once(...) means that each of these fields can only appear once.

In owned_types, we declare OS_ASSET, which describes a fungible state. We use StateSchema::Fungible(FungibleType::Unsigned64Bit), indicating that the quantity of assets (tokens) is stored as a 64-bit unsigned integer. Thus, any transaction will send a certain amount of units of this token, which will be validated according to this strictly typed numerical structure.

We indicate valency_types: none!(), which means that there are no Valencies in this schema, in other words no special or extra rights (such as reissue, conditional burn, etc.). If a schema included any, they would be declared in this section.

Here we enter the part that declares Contract Operations. The Genesis is described by:

This is how we limit the definition of the initial token issue: we must declare the supply issued (GS_ISSUED_SUPPLY), plus at least one holder (an Owned State of type OS_ASSET).

The extensions: none!() field indicates that no State Extension is foreseen in this contract. This means that there is no operation to redeem a digital right (Valency) or to perform a state extension before a Transition. Everything is done via Genesis or State Transitions.

In transitions, we define the TS_TRANSFER type of operation. We explain that:

This models the behavior of a basic transfer, which consumes tokens on a UTXO, then creates new Owned States in favor of the recipients, and thus preserves the equality of the total amount between inputs and outputs.

Finally, we declare an AluVM script (Script::AluVM(AluScript { ... })). This script contains:

This validation code is responsible for applying business logic. For example, it will check:

If these rules are not respected, the transition will be considered invalid.

This example of a "Non Inflatable Fungible Asset" Schema gives us a better understanding of the structure of a simple RGB fungible token contract. We can clearly see the separation between data description (Global and Owned States), operation declaration (Genesis, Transitions, Extensions) and validation implementation (AluVM scripts). Thanks to this model, a token behaves like a classic fungible token, but remains validated on the client side and does not depend on the on-chain infrastructure to execute its code. Only cryptographic commitments are anchored in the Bitcoin blockchain.

Interface

The interface is the layer designed to make a contract readable and manipulable, both by users (human reading) and by portfolios (software reading). The Interface therefore plays a role comparable to that of an interface in an object-oriented programming language (Java, Rust trait, etc.), in that it exposes and clarifies the functional structure of a contract, without necessarily revealing the internal details of the business logic.

Unlike Schema, which is purely declarative and compiled into a binary file that is difficult to use as is, Interface provides the reading keys needed to:

RGB-Bitcoin

Thanks to the Interface, you can, for example, write code in a wallet which, instead of manipulating fields, directly manipulates labels such as "number of tokens", "asset name", etc. This way, managing a contract becomes more intuitive. In this way, contract management becomes more intuitive.

General operation

This method has many advantages:

The same type of contract can be supported by a standard Interface, shared between several wallet implementations. This facilitates compatibility and code reuse.

In RGB design, Schema (business logic) and Interface (presentation and manipulation) are two independent entities. The developers who write the contract logic can concentrate on the Schema, without worrying about ergonomics or data representation, while another team (or the same team, but on a different timeline) can develop the Interface.

The Interface can be modified or added to after the asset has been issued, without having to change the contract itself. This is a major difference from some on-chain smart contract systems, where the Interface (often mixed with the execution code) is frozen in the blockchain.

The same contract could be exposed through different Interfaces adapted to distinct needs: a simple Interface for the end-user, another more advanced one for the issuer who needs to manage complex configuration operations. The wallet can then choose which Interface to import, depending on its use.

RGB-Bitcoin

In practice, when the wallet retrieves an RGB contract (via a .rgb or .rgba file), it also imports the associated Interface, which is also compiled. At runtime, the wallet can, for example:

Difference from Ethereum and other systems

On Ethereum, the Interface (described via the ABI, Application Binary Interface) is generally derived from on-chain stored code (the smart contract). It can be costly or complicated to modify a specific part of the interface without touching the contract itself. However, RGB is based on an entirely off-chain logic, with data anchored in commitments on Bitcoin. This design makes it possible to modify the Interface (or its implementation) without impacting the fundamental security of the contract, as the validation of the business rules remains in the Schema and the referenced AluVM code.

Interface compilation

As with Schema, the Interface is defined in source code (often in Rust) and compiled into a .rgb or .rgba file. This binary file contains all the information required by the wallet to:

Once the Interface has been imported, the wallet can correctly display the contract and propose interactions to the user.

Interfaces standardized by the LNP/BP association

In the RGB ecosystem, an Interface is used to give a readable and manipulable meaning to the data and operations of a contract. The Interface thus complements the Schema, which describes the business logic internally (strict types, validation scripts, etc.). In this section, we'll take a look at the standard Interfaces developed by the LNP/BP association for common contract types (fungible tokens, NFT, etc.).

As a reminder, the idea is that each Interface describes how to display and manipulate a contract on the wallet side, clearly naming the fields (such as spec, ticker, issuedSupply...) and defining the possible operations (such as Transfer, Burn, Rename...). Several Interfaces are already operational, but there will be more and more in the future.

Some ready-to-use interfaces

RGB20 is the Interface for fungible assets, which can be compared to Ethereum's ERC20 standard. However, it goes a step further, offering more extensive functionality:

For example, the RGB20 Interface can be linked to the Non-Inflatable Asset (NIA) scheme, which imposes a non-inflatable initial supply, or to other more advanced schemes as required.

RGB21 concerns NFT-type contracts, or more broadly, any unique digital content, such as the representation of digital media (images, music, etc.). In addition to describing the issue and transfer of a single asset, it includes features such as:

RGB25 is a hybrid standard combining fungible and non-fungible aspects. It is designed for partially fungible assets, such as real estate tokenization, where you want to split up a property while retaining a link to a single root asset (in other words, you have fungible pieces of a house, linked to a non-fungible house). Technically, this interface can be linked to the Collectible Fungible Asset (CFA)* schema, which takes into account the notion of splitting while tracing the original asset.

Interfaces under development

Other Interfaces are planned for more specialized uses, but are not yet available:

Of course, depending on the date on which you consult this course, these interfaces may already be operational and accessible.

Interface example

This Rust code snippet shows a RGB20 Interface (fungible asset). This code is taken from the rgb20.rs file in the standard RGB library. Let's take a look at it to understand the structure of an Interface and how it provides a bridge between, on the one hand, the business logic (defined in the Schema) and, on the other, the functionalities exposed to wallets and users.

// ...
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(),
    }
}

In this interface, we notice similarities with the Schema structure: we find a declaration of Global State, Owned States, Contract Operations (Genesis and Transitions), as well as error handling. But the Interface focuses on the presentation and manipulation of these elements for a wallet or any other application.

The difference with Schema lies in the nature of the types. Schema uses strict types (such as FungibleType::Unsigned64Bit) and more technical identifiers. The Interface uses field names, macros (fname!(), tn!()), and references to argument classes (ArgSpec, OwnedIface::Rights...). The aim here is to facilitate the functional understanding and organization of elements for the wallet.

In addition, the Interface can introduce additional functionality to the basic Schema (e.g. management of a burnEpoch right), as long as this remains consistent with the final validated client-side logic. The AluVM "script" section in the Schema will ensure cryptographic validity, while the Interface describes how the user (or wallet) interacts with these states and transitions.

Global State and Assignments

In the global_state section, we find fields such as spec (asset description), data, created, issuedSupply, burnedSupply, replacedSupply. These are fields that the wallet can read and present. For example:

In the assignments section, we define various roles or rights. For example:

The public or private keyword (e.g. AssignIface::public(...)) indicates whether these states are visible (public) or confidential (private). As for Req::NoneOrMore, Req::Optional, they indicate the expected occurrence.

Genesis and transitions

The genesis part describes how the asset is initialized:

Then, for each Transition (Transfer, Issue, Burn...), the Interface defines which fields the operation expects as input, which fields the operation will produce as output, and any errors that may occur. For example:

Transition:

Transition Issue:

Burn transition:

Each operation is therefore described in a way that is readable for a wallet. This makes it possible to display a graphical interface where the user can clearly see: "You have the right to burn. Would you like to burn a certain amount? The code knows to fill in a burnedSupply field and check that the burnRight is valid.

To sum up, it's important to bear in mind that an Interface, however complete, does not by itself define the internal logic of the contract. The heart of the work is done by the Schema, which includes strict types, Genesis structure, transitions and so on. The Interface simply exposes these elements in a more intuitive and named way, for use in an application.

Thanks to RGB's modularity, the Interface can be upgraded (for example, by adding a Rename transition, correcting the display of a field, etc.) without having to rewrite the entire contract. Users of this Interface can then benefit immediately from these improvements, as soon as they update the .rgb or .rgba file.

But once you've declared an Interface, you need to link it to the corresponding Schema. This is done via the Interface Implementation, which specifies how to map each named field (such as fname!("assetOwner")) to the strict ID (such as OS_ASSET) defined in the Schema. This ensures, for example, that when a wallet manipulates a burnRight field, this is the state which, in the Schema, describes the ability to burn tokens.

Interface Implementation

In the RGB architecture, we have seen that each component (Schema, Interface, etc.) can be developed and compiled independently. However, there's still one indispensable element that links these different building blocks together: the Interface Implementation. This is what explicitly maps the identifiers or fields defined in the Schema (on the business logic side) to the names declared in the Interface (on the presentation and user interaction side). So when a wallet loads a contract, it can understand exactly which field corresponds to what, and how an operation named in the Interface relates to the logic of the Schema.

An important point is that Interface Implementation is not necessarily intended to expose all Schema functionalities, nor all Interface fields: it can be limited to a subset. In practice, this makes it possible to restrict or filter certain aspects of the Schema. For example, you could have a Schema with four types of operation, but an Implementation Interface that maps only two of them in a given context. Conversely, if an Interface proposes additional endpoints, we can choose not to implement them here.

Here's a classic example of Interface Implementation, where we associate a Non-Inflatable Asset (NIA) Schema with the RGB20 Interface:

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!(),
    }
}

In this Implementation Interface:

The result after compilation is a separate .rgb or .rgba file, to be imported into the wallet in addition to the Schema and Interface. Thus, the software knows how to concretely connect this NIA contract (whose logic is described by its Schema) to the "RGB20" Interface (which provides human names and an interaction mode for fungible tokens), applying this Interface Implementation as a gateway between the two.

Why separate Interface Implementation?

Separation enhances flexibility. A single Schema could have several distinct Interface Implementations, each mapping a different set of functionalities. What's more, the Interface Implementation itself can evolve or be rewritten without requiring a change in either the Schema or the Interface. This retains RGB's principle of modularity: each component (Schema, Interface, Interface Implementation) can be versioned and updated independently, as long as the compatibility rules imposed by the protocol are respected (same identifiers, consistency of types, etc.).

In concrete use, when the wallet loads a contract, it must:

This modular architecture makes possible use scenarios such as:

In the next chapter, we'll look at how a contract transfer works, and how RGB invoices are generated.

Contract transfers

In this chapter, we're going to analyze the process of a contract transfer in the RGB ecosystem. To illustrate this, we'll take a look at Alice and Bob, our usual protagonists, who wish to exchange an RGB asset. We'll also show some command excerpts from the rgb command-line tool, to see how it works in practice.

Understanding RGB contract transfer

Let's take an example of a transfer between Alice and Bob. In this example, we assume that Bob is just starting to use RGB, while Alice already holds RGB assets in her wallet. We'll see how Bob sets up his environment, imports the relevant contract, then requests a transfer from Alice, and finally how Alice carries out the actual transaction on the Bitcoin blockchain.

1) Installing the RGB wallet

First of all, Bob needs to install an RGB wallet, i.e. software compatible with the protocol. This does not contain any contracts at the outset. Bob will also need:

As a reminder, Owned States in RGB refer to Bitcoin UTXOs. We must therefore always be able to manage and spend UTXOs in a Bitcoin transaction that incorporates cryptographic commitments (Tapret or Opret) pointing to RGB data.

2) Contract information acquisition

Bob then needs to retrieve the contract data he's interested in. This data can circulate via any channel: website, e-mail, messaging application... In practice, they are grouped together in a consignment, i.e. a small packet of data containing:

RGB-Bitcoin

The total size is often of the order of a few kilobytes, as each component generally weighs less than 200 bytes. It may also be possible to broadcast this consignment in Base58, via censorship-resistant channels (like Nostr or via the Lightning Network, for example), or as a QR code.

3) Contract import and validation

Once Bob has received the consignment, he imports it into his RGB wallet. This will then:

Bob can now see the asset in his wallet (even if he doesn't own it yet) and understand what fields are available, what operations are possible... He then needs to contact a person who actually owns the asset to be transferred. In our example, this is Alice.

The process of discovering who holds a certain RGB asset is similar to finding a Bitcoin payer. The details of this connection depend on the use (marketplaces, private chat channels, invoicing, sale of goods and services, salary...).

4) Issuing an invoice

To initiate the transfer of an RGB asset, Bob must first issue an invoice. This invoice is used to:

Bob uses the rgb tool on the command line. Suppose he wants 100 units of a token whose ContractId is known, wants to rely on Tapret, and specifies its UTXO (456e3..dfe1:0):

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

We'll take a closer look at the structure of RGB invoices at the end of this chapter.

5) Invoice transmission

The generated invoice (e.g. as URL: rgb:2WBcas9.../RGB20/100+utxob:...) contains all the information Alice needs to prepare the transfer. As with the consignment, it can be encoded compactly (Base58 or another format) and sent via a messaging application, e-mail, Nostr...

RGB-Bitcoin

6) Transaction preparation on the Alice side

Alice receives Bob's invoice. In her RGB wallet, she has a stash containing the asset to be transferred. To spend the UTXO containing the asset, she must first generate a PSBT (Partially Signed Bitcoin Transaction), i.e. an incomplete Bitcoin transaction, using the UTXO she has:

alice$ wallet construct tx.psbt

This basic transaction (unsigned for the moment) will be used to anchor the cryptographic commitment linked to the transfer to Bob. Alice's UTXO will thus be spent, and in the output, we'll place the Tapret or Opret commitment for Bob.

7) Generation of transfer consignment

Next, Alice builds the terminal consignment (sometimes called "transfer consignment") via the command:

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

This new consignment.rgb file contains:

At this stage, the transaction is not yet broadcast on the Bitcoin network. The consignment is larger than a basic consignment, as it includes the entire history (proof chain) to prove the asset's legitimacy.

8) Bob checks and accepts the consignment

Alice transmits this terminal consignment to Bob. Bob will then:

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

9) Option: Bob sends confirmation back to Alice (payslip)

If Bob wishes, he can send this signature back to Alice. This indicates:

This is not compulsory, but it can provide Alice with the assurance that there will be no subsequent disputes over the transfer.

10) Alice signs and publishes the transaction

Alice can then:

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

Once confirmed, this transaction marks the conclusion of the transfer. Bob becomes the new owner of the asset: he now has an Owned State pointing to the UTXO he controls, proven by the presence of the commitment in the transaction.

To summarize, here is the complete transfer process:

RGB-Bitcoin

Advantages of RGB transfers

Only Alice and Bob have access to all State Transition data. They exchange this information outside the blockchain, via consignments. The cryptographic commitments in the Bitcoin transaction do not reveal the type of asset or the amount, which guarantees far greater confidentiality than other on-chain token systems.

Bob can check the consistency of the transfer by comparing the consignment with the anchors in the Bitcoin blockchain. He does not need third-party validation. Alice doesn't have to publish the full history on the blockchain, which reduces the load on the base protocol and improves confidentiality.

Complex exchanges (atomic swaps between BTC and an RGB asset, for example) can be carried out within a single transaction, avoiding the need for HTLC or PTLC scripts. If the agreement is not broadcast, everyone can reuse their UTXOs in other ways.

Transfer summary diagram

Before looking at the invoices in more detail, here's a summary diagram of the overall flow of an RGB transfer:

RGB-Bitcoin

The transfer illustrates all the power and flexibility of the RGB protocol: a private exchange, validated on the client side, anchored minimally and discreetly on the Bitcoin blockchain, and retaining the best of the protocol's security (no risk of double-spending). This makes RGB a promising ecosystem for value transfers that are more confidential and scalable than on-chain programmable blockchains.

Invoices RGB

In this section, we'll explain in detail how invoices work in the RGB ecosystem and how they enable operations (in particular transfers) to be carried out with a contract. First, we'll look at the identifiers used, then at how they are encoded, and finally at the structure of an invoice expressed as a URL (a format that's handy enough for use in wallets).

Identifiers and encoding

A unique identifier is defined for each of the following elements:

This uniqueness is very important, as each component of the system must be distinguishable. For example, a contract X must not be confused with another contract Y, and two different interfaces (RGB20 vs. RGB21, for example) must have distinct identifiers.

To make these identifiers both efficient (small size) and readable, we use:

For example, a ContractId could be represented by something like:

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

The rgb: prefix confirms that this is an RGB identifier, and not an HTTP link or other protocol. Thanks to this prefix, wallets are able to interpret the string correctly.

Identifier segmentation

RGB identifiers are often quite long, as the underlying (cryptographic) security may require fields of 256 bits or more. To facilitate human reading and verification, we chunk these strings into several blocks separated by a hyphen (-). So, instead of having a long, uninterrupted string of characters, we divide it into shorter blocks. This practice is common for credit card or telephone numbers, and it also applies here for ease of verification. So, for example, a user or partner can be told: "Please check that the third block is 9GEgnyMj7", rather than having to compare the whole thing at once. The last block is often used as a checksum, in order to have an error or typos detection system.

As an example, a ContractId in base58 encoded and segmented could be:

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

Each of the dashes breaks the string into sections. This does not affect the semantics of the code, only its presentation.

Using URLs for invoices

An RGB invoice is presented as a URL. This means that it can be clicked or scanned (as a QR code), and a wallet can directly interpret it to carry out a transaction. This simplicity of interaction differs from some other systems where you have to copy and paste various pieces of data into different fields in the software.

An invoice for a fungible token (e.g. an RGB20 token) might look like this:

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

Let's analyze this URL:

The fact that everything fits into a single URL makes life easier for the user: a simple click or scan in the wallet, and the operation is ready to be executed.

One could imagine systems where a simple ticker (e.g. USDT) is used instead of the ContractId. However, this would raise major problems of trust and security: a ticker is not a unique reference (several contracts could claim to be called USDT). With RGB, we want a unique, unambiguous cryptographic identifier. Hence the adoption of the 256-bit string, encoded in base58 and segmented. The user knows that he is manipulating precisely the contract whose ID is 2WBcas9-yjz... and not any other.

Additional URL parameters

You can also add additional parameters to the URL, in the same way as with HTTP, such as:

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

Let's take the case of an NFT via the RGB21 interface. For example, we could have:

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

Here we see:

The idea is the same: transmit a unique link that the wallet can interpret, clearly identifying the unique asset to be transferred.

Other operations via URL

RGB URLs aren't just used to request a transfer. They can also encode more advanced operations, such as issuing new tokens (issuance). For example:

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

Here we find:

For example, the wallet might read: "I have been asked to carry out an issue operation from the RGB20 interface, on such and such a contract, for 100,000 units, for the benefit of such and such a Single-use Seal.*"

Now that we've looked at the main elements of RGB programming, I'll take you through the next chapter on how to draw up an RGB contract.

Drafting smart contracts

In this chapter, we'll take a step-by-step approach to writing a contract, using the command-line tool rgb. The aim is to show how to install and manipulate the CLI, compile a Schema, import the Interface and the Interface Implementation, then issue (issue) an asset. We'll also look at the underlying logic, including compilation and state validation. By the end of this chapter, you should be able to reproduce the process and create your own RGB contracts.

As a reminder, the internal logic of RGB is based on Rust libraries that you, as developers, can import into your projects to manage the Client-side Validation part. In addition, the LNP/BP Association team is working on bindings for other languages, but this has not yet been finalized. In addition, other entities such as Bitfinex are developing their own integration stacks (we'll talk about these in the last 2 chapters of the course). For the time being, therefore, the rgb CLI is the official reference, even if it remains relatively unpolished.

Installation and presentation of the rgb tool

The main command is simply called rgb. It is designed to be reminiscent of git, with a set of sub-commands for manipulating contracts, invoking them, issuing assets and so on. Bitcoin Wallet is not currently integrated, but will be in an imminent version (0.11). This next version will enable users to create and manage their wallets (via descriptors) directly from rgb, including PSBT generation, compatibility with external hardware (e.g. a hardware wallet) for signing, and interoperability with software such as Sparrow. This will simplify the entire asset issuance and transfer scenario.

Installation via Cargo

We install the tool in Rust with:

cargo install rgb-contracts --all-features

(Note: the crate is called rgb-contracts, and the installed command will be named rgb. If a crate named rgb already existed, there could have been a collision, hence the name)

The installation compiles a large number of dependencies (e.g. command parsing, Electrum integration, zero-knowledge proofs management, etc.).

Once installation is complete, the:

rgb

Running rgb (without arguments) displays a list of available sub-commands, such as interfaces, schema, import, export, issue, invoice, transfer, etc. You can change the local storage directory (a stash that holds all logs, schematics and implementations), choose the network (testnet, mainnet) or configure your Electrum server.

RGB-Bitcoin

First overview of controls

When you run the following command, you'll see that an RGB20 interface is already integrated by default:

rgb interfaces

If this interface is not integrated, clone the:

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

Compile it:

cargo run

Then import the interface of your choice:

rgb import interfaces/RGB20.rgb
RGB-Bitcoin

On the other hand, we are told that no schema has yet been imported into the software. Nor is there a contract in the stash. To see it, run the command:

rgb schemata

You can then clone the repository to retrieve certain schematics:

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

This repository contains, in its src/ directory, several Rust files (for example nia.rs) which define schemas (NIA for "Non Inflatable Asset", UDA for "Unique Digital Asset", etc.). To compile, you can then run:

cd rgb-schemata
cargo run

This generates several .rgb and .rgba files corresponding to the compiled schematics. For example, you'll find NonInflatableAsset.rgb.

Importing Schema and Interface Implementation

You can now import the schematic into rgb:

rgb import schemata/NonInflatableAssets.rgb
RGB-Bitcoin

This adds it to the local stash. If we run the following command, we see that the schema now appears:

rgb schemata

Contract creation (issuing)

There are two approaches to creating a new asset:

You can find examples in Rust in the examples folder, which illustrate how you build a ContractBuilder, fill in the global state (asset name, ticker, supply, date, etc.), define the Owned State (to which UTXO it is assigned), then compile all this into a contract consignment that you can export, validate and import into a stash.

The other way is to manually edit a YAML file to customize the ticker, the name, the supply, and so on. Suppose the file is called RGB20-demo.yaml. You can specify:

Here is an example of a YAML file to create:

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

Then simply run the command:

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

In my case, the unique schema identifier (to be enclosed in single quotes) is RDYhMTR!9gv8Y2GLv9UNBEK1hcrCmdLDFk9Qd5fnO8k and I haven't put any issuer. So my order is:

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

If you don't know the schema ID, run the command:

rgb schemata

The CLI replies that a new contract has been issued and added to the stash. If we type the following command, we see that there is now an additional contract, corresponding to the one just issued:

rgb contracts
RGB-Bitcoin

Then, the next command displays the global states (name, ticker, supply...) and the list of Owned States, i.e. allocations (for example, 1 million PBN tokens defined in UTXO b449f7eaa3f98c145b27ad0eeb7b5679ceb567faef7a52479bc995792b65f804:1).

rgb state '<ContractId>'
RGB-Bitcoin

Export, import and validation

To share this contract with other users, it can be exported from the stash to a:

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

The myContractPBN.rgb file can be passed on to another user, who can add it to his stash with the command:

rgb import myContractPBN.rgb

On import, if it's a simple contract consignment, we'll get an "Importing consignment rgb" message. If it's a larger state transition consignment, the command will be different (rgb accept).

To ensure validity, you can also use the local validation function. For example, you could run:

rgb validate myContract.rgb

Stash usage, verification and display

As a reminder, the stash is a local inventory of schemas, interfaces, implementations and contracts (Genesis + transitions). Each time you run "import", you add an element to the stash. This stash can be viewed in detail with the command:

rgb dump
RGB-Bitcoin

This will generate a folder with details of the entire stash.

Transfer and PSBT

To carry out a transfer, you'll need to manipulate a local Bitcoin wallet to manage the Tapret or Opret commitments.

Generate an invoice

In most cases, interaction between the participants in a contract (e.g. Alice and Bob) takes place via the generation of an invoice. If Alice wants Bob to execute something (a token transfer, a reissue, an action in a DAO, etc.), Alice creates an invoice detailing her instructions to Bob. So we have:

Unlike other ecosystems, an RGB invoice is not limited to the notion of payment. It can embed any request linked to the contract: revoke a key, vote, create an engraving (engraving) on an NFT, etc. The corresponding operation can be described in the contract interface. The corresponding operation can be described in the contract interface.

The following command generates an RGB invoice:

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

With:

For example, with the following commands

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

The CLI will generate an invoice like:

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

It can be transmitted to Bob via any channel (text, QR code, etc.).

Making a transfer

To transfer from this invoice:

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

In the next chapter, we'll take a closer look at integrating RGB into the Lightning Network.

RGB on the Lightning Network

In this chapter, I propose to examine how RGB can be used within the Lightning Network, to integrate and move RGB assets (tokens, NFTs, etc.) via off-chain payment channels.

The basic idea is that the RGB state transition (State Transition) can be committed to a Bitcoin transaction which, in turn, can remain off-chain until the Lightning channel is closed. So, each time the channel is updated, a new RGB state transition can be incorporated into the new committing transaction, which then invalidates the old transition. In this way, Lightning channels can be used to transfer RGB assets, and can be routed in the same way as conventional Lightning payments.

Channel creation and funding

To create a Lightning channel that carries RGB assets, we need two elements:

In Bitcoin terms, the funding transaction must exist to define the reference UTXO, even if it only contains a small amount of sats (it's just a matter of each output in future commitment transactions remaining above the dust limit all the same). For example, Alice may decide to provide 10k sats and 500 USDT (issued as an RGB asset). On the funding transaction, we add a commitment (Opret or Tapret) which anchors the RGB state transition.

RGB-Bitcoin

Once the funding transaction has been prepared (but not yet broadcast), commitment transactions are created so that either party can close the channel unilaterally at any time. These transactions resemble Lightning's classic commitment transactions, except that we add an additional output containing the RGB anchor (OP_RETURN or Taproot) linked to the new state transition.

The RGB state transition then moves the assets from the 2/2 multisig of the funding to the outputs of the commitment transaction. The advantage of this process is that the security of the RGB state exactly matches Lightning's punitive mechanics: if Bob broadcasts an old channel state, Alice can punish him and spend the output, in order to recover both the sats and the RGB tokens. The incentive is therefore even stronger than in a Lightning channel without RGB assets, since an attacker can lose not only sats, but also the channel's RGB assets.

A commitment transaction signed by Alice and sent to Bob would therefore look like this:

RGB-Bitcoin

And the accompanying commitment transaction, signed by Bob and sent to Alice, will look like this:

RGB-Bitcoin

Channel update

When a payment occurs between two channel participants (or they wish to change the asset allocation), they create a new pair of commitment transactions. The amount in sats on each output may or may not remain unchanged, depending on the implementation, as its main role is to enable the construction of valid UTXOs. On the other hand, the OP_RETURN (or Taproot) output must be modified to contain the new RGB anchor, representing the new distribution of assets in the channel.

For example, if Alice transfers 30 USDT to Bob in the channel, the new state transition will reflect a balance of 400 USDT for Alice and 100 USDT for Bob. The commit transaction is added to (or modified by) the OP_RETURN/Taproot anchor to include this transition. Note that, from RGB's point of view, the input to the transition remains the initial multisig (where on-chain assets are actually allocated until the channel closes). Only the RGB outputs (allocations) change, depending on the redistribution decided upon.

The commitment transaction signed by Alice, ready to be distributed by Bob:

RGB-Bitcoin

The commitment transaction signed by Bob, ready to be distributed by Alice:

RGB-Bitcoin

HTLC management

In reality, the Lightning Network enables payments to be routed via multiple channels, using HTLCs (Hashed Time-Locked Contracts). It's the same with RGB: for every payment in transit through the channel, an HTLC output is added to the committing transaction, and an RGB allocation linked to this HTLC. Thus, whoever spends the HTLC output (thanks to the secret or after expiry of the timelock) recovers both the sats and the associated RGB assets. On the other hand, you obviously need to have enough cash on the road in terms of both sats and RGB assets.

RGB-Bitcoin

The operation of RGB on Lightning must therefore be considered in parallel with that of the Lightning Network itself. If you'd like to delve deeper into this subject, I highly recommend you take a look at this other comprehensive training course:

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

RGB code map

Finally, before moving on to the next section, I'd like to give you an overview of the code used in RGB. The protocol is based on a set of Rust libraries and open source specifications. Here's an overview of the main repositories and crates:

RGB-Bitcoin

Client-side Validation

Management of off-chain validation and Single-use Seals logic.

Deterministic Bitcoin Commitments (DBC)

Management of deterministic anchoring in Bitcoin transactions (Tapret, OP_RETURN, etc.).

Multi Protocol Commitment (MPC)

Multiple engagement combinations and integration with different protocols.

Strict Types & Strict Encoding

The strict typing system and deterministic serialization used for client-side validation.

RGB Core

The core of the protocol, which encompasses the main logic of RGB validation.

RGB Standard Library & Wallet

Standard implementations, stash and wallet management.

RGB CLI

The rgb CLI and crate wallet, for command-line manipulation of contracts.

RGB Schema

Contains examples of schemas (NIA, UDA, etc.) and their implementations.

ALuVM

Registry-based virtual machine used to run validation scripts.

Bitcoin Protocol - BP

Add-ons to support the Bitcoin protocol (transactions, bypasses, etc.).

Ubiquitous Deterministic Computing - UBIDECO

Ecosystem linked to open-source deterministic developments.

Building on RGB

DIBA and the Bitmask project

This final section of the course is based on presentations made by various speakers at the RGB bootcamp. It includes testimonials and reflections on RGB and its ecosystem, as well as presentations of tools and projects based on the protocol. This first chapter is moderated by Hunter Beast, and the next two by Frederico Tenga.

From JavaScript to Rust, and into the Bitcoin ecosystem

At first, Hunter Beast worked mainly in JavaScript. Then he discovered Rust, whose syntax seemed unappealing and frustrating at first. However, he came to appreciate the power of the language, the control over memory (heap and stack), and the security and performance that come with it. He emphasizes that Rust is an excellent training ground for in-depth understanding of how a computer works.

Hunter Beast recounts his background in various projects in the altcoin ecosystem, such as Ethereum (with Solidity, TypeScript, etc.), and later Filecoin. He explains that he was initially impressed by some of the protocols, but ended up feeling disillusioned by most of them, not least because of their tokenomics. He denounces the dubious financial incentives, the inflationary creation of tokens that dilutes investors, and the potentially exploitative aspect of these projects. So he ended up adopting a Bitcoin maximalist stance, not least because some people opened his eyes to Bitcoin's sounder economic mechanisms, and to the robustness of this system.

The appeal of RGB and building on layers

What definitively convinced him of Bitcoin's relevance, in his words, was the discovery of RGB and the concept of layers. He believes that existing functionalities on other blockchains could be reproduced on higher layers, above Bitcoin, without altering the basic protocol.

In February 2022, he joined DIBA to work specifically on RGB, and in particular on the Bitmask wallet. At the time, Bitmask was still at version 0.01 and was running RGB at version 0.4, only for the management of single tokens. He notes that this was less self-custody-oriented than today, as the logic was partly server-based. Since then, the architecture has evolved towards this model, much appreciated by bitcoiners.

The foundations of the RGB protocol

The RGB protocol is the most recent and most advanced embodiment of the colored coins concept, already explored around 2012-2013. At the time, several teams were looking to associate different bitcoin value on UTXOs, which led to multiple scattered implementations. This lack of standardization and the low demand at the time prevented these solutions from gaining a lasting foothold.

Today, RGB stands out for its conceptual robustness and unified specifications via the LNP/BP association. The principle is based on client-side validation. The Bitcoin blockchain only stores cryptographic commitments (commitments, via Taproot or OP_RETURN), while the majority of data (contract definitions, transfer histories, etc.) is stored by the users concerned. In this way, the storage load is distributed and the confidentiality of exchanges is reinforced, without weighing down the blockchain. This approach enables the creation of fungible assets (RGB20 standard) or unique assets (RGB21 standard), within a modular and scalable framework.

The token function (RGB20) and unique assets (RGB21)

With RGB20, we define a fungible token on Bitcoin. The issuer chooses a supply, a precision, and creates a contract in which he can then make transfers. Each transfer is referenced to a Bitcoin UTXO, which acts as a Single-use Seal. This logic ensures that the user will not be able to spend the same asset twice, since only the person capable of spending the UTXO actually holds the key to update the state of the client-side contract.

RGB21 targets unique assets (or "NFT"). The asset has a supply of 1, and can be associated with metadata (image file, audio, etc.) described via a specific field. Unlike NFTs on public blockchains, data and their MIME identifiers can remain private, distributed peer-to-peer at the owner's discretion.

The Bitmask solution: a wallet for RGB

To exploit RGB's capabilities in practice, the DIBA project has designed a wallet called Bitmask. The idea is to provide a non-custodial, Taproot-based tool, accessible as a web application or browser extension. Bitmask manages both RGB20 and RGB21 assets, and integrates various security mechanisms:

Thanks to this architecture, all asset transactions take place on the client side. From the outside, the Bitcoin transaction is nothing more than a classic Taproot spending transaction, which nobody would suspect is also carrying a transfer of fungible tokens or NFTs. The absence of on-chain overloading (no publicly stored metadata) guarantees a certain degree of discretion and makes it easier to resist possible censorship attempts.

Security and distributed architecture

Insofar as the RGB protocol requires each participant to retain its transaction history (to prove the validity of the transfers it receives), the question of storage arises. Bitmask proposes to serialize this stash locally, then send it to several servers or clouds (optional). The data remains encrypted by the user via Carbonado, so a server cannot read it. In the event of partial corruption, the error correction layer can reconstitute the content.

The use of CRDT (Conflict-free replicated data type) enables different versions of the stash to be merged, should they diverge. Everyone is free to host this data wherever they wish, as no single full node carries all the information linked to the asset. This exactly reflects the Client-side Validation philosophy, where each owner is responsible for storing evidence of the validity of their RGB asset.

Towards a broader ecosystem: marketplace, interoperability and new functions

The company behind Bitmask is not limiting itself to the simple development of a wallet. DIBA intends to develop:

At the same time, we're working on WebBTC or WebLN (standards enabling websites to ask the wallet to sign Bitcoin or Lightning transactions), as well as on the ability to "teleburn" Ordinals entries (if we want to repatriate Ordinals to a more discreet and flexible RGB format).

Conclusion

The whole process shows how the RGB ecosystem can be deployed and made accessible to end-users through robust technical solutions. The transition from an altcoin perspective to a more Bitcoin-centric vision, coupled with the discovery of Client-side Validation, illustrates a fairly logical path: we understand that it is possible to implement various functionalities (fungible tokens, NFT, smart contracts...) without forking the blockchain, simply by taking advantage of cryptographic commitments on Taproot transactions or OP_RETURNs.

The Bitmask wallet is part of this approach: on the blockchain side, all you see is an ordinary Bitcoin transaction; on the user side, you manipulate a web interface where you create, exchange and store all kinds of off-chain assets. This model clearly dissociates the monetary infrastructure (Bitcoin) from the issuing and transfer logic (RGB), while ensuring a high level of confidentiality and better scalability.

Bitfinex's work on RGB

In this chapter, based on a presentation by Frederico Tenga, we look at a set of tools and projects created by the Bitfinex team dedicated to RGB, with the aim of fostering the emergence of a rich and diverse ecosystem around this protocol. The team's initial aim is not to release a specific commercial product, but rather to provide software building blocks, contribute to the RGB protocol itself, and propose concrete implementation references such as a mobile wallet (Iris Wallet) or an RGB-compatible Lightning node.

Background and objectives

Since around 2022, the Bitfinex RGB team has been concentrating on developing the technology stack that enables RGB to be exploited and tested efficiently. Several contributions have been made:

This approach aims to cover the entire chain of needs: from the low-level library (RGBlib), enabling the implementation of a wallet, to the production aspect (a Lightning node, a wallet for Android, etc.).

The RGBlib library: simplifying the development of RGB applications

An important point in democratizing the creation of RGB wallets and applications is to make available an abstraction simple enough so that developers don't have to learn everything about the protocol's internal logic. This is precisely the aim of RGBlib, written in Rust.

RGBlib acts as a bridge between the highly flexible (but sometimes complex) requirements of RGB that we have been able to study in previous chapters, and the concrete needs of an application developer. In other words, a wallet (or service) wishing to manage token transfers, asset issuance, verification, etc., can rely on RGBlib without knowing every cryptographic detail or every customizable RGB parameter.

The bookshop offers:

RGBlib therefore relies on complex notions specific to RGB (Client-side Validation, Tapret/Opret anchors), but encapsulates them so that the final application doesn't have to reprogram everything or make risky decisions. What's more, RGBlib is already binded in several languages (Kotlin and Python), opening the door to uses beyond a simple Rust universe.

Iris Wallet: an example of an RGB wallet on Android

To prove the effectiveness of RGBlib, the Bitfinex team has developed Iris Wallet, exclusively on Android at this stage. It's a mobile wallet that illustrates a user experience similar to that of an ordinary Bitcoin wallet: you can issue an asset, send it, receive it, and view its history, while remaining on a self-custody model.

Iris has a number of interesting features:

Using an Electrum server:

Like any wallet, Iris needs to know about transaction confirmations on the blockchain. Rather than embedding a complete node, Iris defaults to an Electrum server maintained by the Bitfinex team. Users can, however, configure their own server or another third-party service. In this way, Bitcoin transactions can be validated and information retrieved (indexing) in a modular way.

The RGB proxy server:

Unlike Bitcoin, RGB requires the exchange of off-chain metadata (consignments) between sender and receiver. To simplify this process, Iris offers a solution where communication takes place via a proxy server. The receiving wallet generates an invoice that mentions where the sender should send the client-side data. By default, the URL points to a proxy hosted by the Bitfinex team, but you can change this proxy (or host your own). The idea is to return to a familiar user experience where the recipient displays a QR code, and the sender scans this code for the transaction, without any complex additional manipulations.

Continuous backup:

In a strictly Bitcoin context, backing up your seed is generally sufficient (although these days we recommend backing up the seed and descriptors instead). With RGB, this isn't enough: you also need to keep the local history (the consignments) proving that you really do own an RGB asset. Each time you receive a receipt, the device stores new data, which is essential for subsequent spending. Iris automatically manages an encrypted backup in the user's Google Drive. This requires no special trust in Google, as the backup is encrypted, and more robust options (such as a personal server) are planned for the future to avoid any risk of censorship or deletion by a third-party operator.

Other features:

All in all, Iris offers a user experience close to that of a classic Bitcoin wallet, masking the additional complexity (stash management, consignment history, etc.) thanks to RGBlib and the use of a proxy server.

Proxy server and user experience

The proxy server introduced above deserves to be detailed, as it is the key to a smooth user experience. Instead of the sender having to manually transmit the consignments to the recipient, the RGB transaction takes place in the background via a:

In this way, the wallet behaves almost like a normal wallet. The user is unaware of all the intermediate steps. Admittedly, the current proxy is neither encrypted nor authenticated (which leaves concerns about confidentiality and integrity), but these improvements are possible in later versions. The proxy concept remains extremely useful for recreating the "I send a QR code, you scan to pay" experience.

RGB integration on the Lightning Network

Another key focus of the Bitfinex team's work is to make the Lightning Network compatible with RGB assets. The aim is to enable Lightning channels in USDT (or any other token), and to benefit from the same advantages as bitcoin on Lightning (near-instantaneous transactions, routing, etc.). In concrete terms, this involves creating a Lightning node modified to:

This solution, dubbed "RGB Lightning Node", uses LDK (Lightning Dev Kit) as a base, and adds the mechanisms needed to inject RGB tokens into the channels. Lightning commitments retain the classic structure (puncturable outputs, timelock...), and in addition anchor an RGB state transition (via Opret or Tapret). For the user, this opens the way to Lightning channels in stablecoins or in any other asset emitted via RGB.

DEX potential and impact on Bitcoin

Once several assets are managed via Lightning, it becomes possible to imagine an atomic exchange on a single Lightning routing path, using the same logic of secrets and timelocks. For example, user A holds bitcoin on one Lightning channel, and user B holds USDT RGB on another Lightning channel. They can build a path linking their two channels and simultaneously exchange BTC for USDT, without the need for trust. This is nothing more than an atomic swap taking place in several hops, making outside participants almost oblivious to the fact that they are making a trade, not just a routing. This approach offers:

We can then imagine an ecosystem where Lightning nodes offer swap prices (by providing liquidity). Each node, if it wishes, can play the role of market maker, buying and selling various assets on Lightning. This prospect of a layer-2 DEX reinforces the idea that it is not necessary to fork or use third-party blockchains to obtain decentralized asset exchanges.

The impact on Bitcoin could be positive: Lightning's infrastructure (nodes, channels and services) would be more fully utilized thanks to the volumes generated by these stablecoins, derivatives and other tokens. Merchants interested in USDT payments on Lightning would mechanically discover BTC payments on Lightning (managed by the same stack). The maintenance and financing of the Lightning Network infrastructure could also benefit from the multiplication of these non-BTC flows, which would indirectly benefit Bitcoin users.

Conclusion and resources

The Bitfinex team dedicated to RGB illustrates, through its work, the diversity of what can be done on top of the protocol. On the one hand, there's RGBlib, a library that facilitates the design of wallets and applications. On the other, we have Iris Wallet, a practical demonstration on Android of a neat end-user interface. Finally, the integration of RGB with Lightning shows that stablecoin channels are possible, and opens the way to a potential decentralized DEX on Lightning.

This approach remains largely experimental and continues to evolve: the RGBlib library is being refined as we go along, Iris Wallet is receiving regular enhancements, and the dedicated Lightning node is not yet a mainstream Lightning client.

For those who wish to learn more or contribute, several resources are available, including:

In the next chapter, we'll take a closer look at how to launch an RGB Lightning node.

RLN - RGB Lightning Node

In this final chapter, Frederico Tenga takes you step-by-step through setting up a Lightning RGB node on a Regtest environment, and shows you how to create RGB tokens on it. By launching two separate nodes, you'll also discover how to open a Lightning channel between them and exchange RGB assets.

This video serves as a tutorial, similar to what we covered in a previous chapter, but specifically focused on Lightning this time!

The main resource for this video is the Github repository RGB Lightning Node, which makes it easy for you to launch this configuration in Regtest.

Deploying an RGB-compatible Lightning node

The process takes up and puts into practice all the concepts covered in the previous chapters:

Introducing rgb-lightning-node

The rgb-lightning-node project is a Rust daemon based on an rust-lightning (LDK) fork modified to take into account the existence of RGB assets in a channel. When a channel is opened, the presence of assets can be specified, and each time the channel state is updated, an RGB transition is created, reflecting the distribution of the asset in the Lightning outputs. This enables:

The code is still at the alpha stage: we recommend using it in regtest or on the testnet only.

Node installation

To compile and install the rgb-lightning-node binary, we start by cloning the repository and its sub-modules, then we run the:

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

From the project root, run the following command to compile and install the binary:

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

At the end of this command, an rgb-lightning-node executable will be available in your $CARGO_HOME/bin/. Make sure this path is in your $PATH so you can invoke the command from any directory.

Performance requirements

To function, the rgb-lightning-node daemon requires the presence and configuration of:

Each RLN instance will need to communicate with bitcoind to broadcast and monitor its on-chain transactions. Authentication (login/password) and URL (host/port) will need to be provided to the daemon.

The daemon must be able to list and explore on-chain transactions, in particular to find the UTXO on which an asset has been anchored. You'll need to specify the URL of your Electrum server or Esplora.

As seen in previous chapters, the proxy server is a component (optional, but highly recommended) to simplify the exchange of consignments between Lightning peers. Once again, a URL must be specified.

IDs and URLs are entered when the daemon is unlocked via the API. More on this later.

Regtest launch

For simple use, there's a regtest.sh script that automatically starts, via Docker, a set of services: bitcoind, electrs (indexer), rgb-proxy-server.

RGB-Bitcoin

This allows you to launch a local, isolated, pre-configured environment. It creates and destroys containers and data directories on each reboot. We'll begin by starting the:

./regtest.sh start

This script will:

RGB-Bitcoin

Next, we'll launch several RLN nodes. In separate shells, run, for example (to launch 3 RLN nodes):

# 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

You can also run commands on your RLN nodes from your browser:

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

For a node to open a channel, it must first have bitcoins on an address generated with the following command (for node nΒ°1, for example):

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

The answer will provide you with an address.

RGB-Bitcoin

On the bitcoind Regtest, we're going to mine a few bitcoins. Run:

./regtest.sh mine 101
RGB-Bitcoin

Send funds to the node address generated above:

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

Then mine a block to confirm the transaction:

./regtest.sh mine 1
RGB-Bitcoin

Testnet launch (without Docker)

If you want to test a more realistic scenario, you can launch 3 RLN nodes on the Testnet rather than in Regtest, pointing to public services:

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

By default, if no configuration is found, the daemon will try to use the:

With login:

You can also customize these elements via the init/unlock API.

Issuing an RGB token

To issue a token, we'll start by creating "colorable" UTXOs:

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

You can, of course, adapt the order. To confirm the transaction, we mine a:

./regtest.sh mine 1

We can now create an RGB asset. The command will depend on the type of asset you wish to create and its parameters. Here I'm creating a NIA (Non Inflatable Asset) token named "PBN" with a supply of 1000 units. The precision allows you to define the divisibility of the units.

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

The response includes the ID of the newly created asset. Remember to note this identifier. In my case, it's:

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

You can then transfer it on-chain, or allocate it in a Lightning channel. That's exactly what we're going to do in the next section.

Opening a channel and transferring an RGB asset

You must first connect your node to a peer on the Lightning network using the /connectpeer command. In my example, I control both nodes. So I'll retrieve the public key of my second Lightning node with this command:

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

The command returns the public key of my node nΒ°2:

031e81e4c5c6b6a50cbf5d85b15dad720fec92c62e84bafb34088f0488e00a8e94
RGB-Bitcoin

Next, we'll open the channel by specifying the relevant asset (PBN). The /openchannel command lets you define the size of the channel in satoshis and opt to include the RGB asset. It depends on what you want to create, but in my case, the command is:

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

Find out more here:

RGB-Bitcoin

To confirm the transaction, 6 blocks are mined:

./regtest.sh mine 6
RGB-Bitcoin

The Lightning channel is now open and also contains 500 PBN tokens on node nΒ°1's side. If node nΒ°2 wishes to receive PBN tokens, it must generate an invoice. Here's how to do it:

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

With:

In response, you will get an RGB invoice (as described in previous chapters):

lnbcrt30u1pncgd4rdqud3jxktt5w46x7unfv9kz6mn0v3jsnp4qv0grex9c6m22r9ltkzmzhddwg87eykx96zt47e5pz8sfz8qp28fgpp5jksvqtleryhvwr299qdz96qxzm24augy5agkdhltudk463lt9dassp5d6n0sqgl0c4gx52fdmutrdtqamt0y4xuz2rcgel4hpjwne08gmls9qyysgqcqpcxqzdylz5wfnkywnxvvmkvnt2x4fj6wre0gshvjtv95ervvzzg4592t2gdgchx6mkf5k45jrrdfn8j73d2f2xx4mrxycq7qzry4v4jan6uxhhacyqa4gn6plggwpq9j74tu74f2zsamtz6ymt600p8su4c4ap9g9d8ku2x3wdh6fuc8fd8pff2yzpjrf24ys3cltca9fgqut6gzj
RGB-Bitcoin

We will now pay this invoice from the first node, which holds the necessary cash with the PBN token:

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

Payment has been made. This can be verified by executing the command:

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

Here's how to deploy a Lightning node modified to carry RGB assets. This demonstration is based on:

Thanks to this process:

The project remains in the alpha stage. It is therefore strongly recommended that you limit yourself to test environments (regtest, testnet).

The opportunities opened up by this LN-RGB compatibility are considerable: stablecoins on Lightning, DEX layer-2, transfer of fungible tokens or NFTs at very low cost... The previous chapters have outlined the conceptual architecture and validation logic. Now you have a practical view of how to get such a node up and running, for your future developments or tests.

Final Section

Reviews & Ratings

true

Conclusion

true