Only this pageAll pages
Powered by GitBook
1 of 57

Radiant Blockchain Wiki

Loading...

Loading...

Loading...

Loading...

White Papers

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Programming

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Guides

Loading...

Loading...

Loading...

Loading...

Loading...

Flux

Radiant Blockchain

Radiant is a decentralized P2P Digital Asset System and PoW UTXO Network for everyone

Welcome to the Radiant Blockchain Community Wiki

If you have an idea for Radiant, feel free to share it and get involved.

This Wiki is maintained by active community members who continuely show up to be reliable, timely and dedicated to the growth and health of the Radiant Blockchain and ecosystem.

Radiant is a Peer-to-Peer Digital Asset System

21 billion coins

The total supply of Radiant is predetermined for 21 billion RXD, with a 50,000 block emission and 2-year halving time.

5 minute (300 secs) block time

Instant transactions with 0-confs

256mb default block size

A safe block size to handle high throughput to start, designed fundamentally to achieve 10+ GB and beyond.

Layer One

Radiant is a layer 1 blockchain with no secondary layers ever needed.

SHA512/256d

Unique proof-of-work consensus designed to embrace GPU/FPGA/ASICs while being more efficient than Bitcoin.

10+ mining pools

Diversified mining pools and solo operators contributing to decentralization.

4,000 TX/sec

Protocol designed to handle 4,000 tx/sec with default 256 MB block sizes. Miners can optimize in the future to scale linearly.

8 decimal places

Each Radiant has 8 decimal places, and the smallest indivisible unit is called a photon. 100 million photons per RXD coin.


Community driven

Fairly mined with no developer allocation, just technology for you to build with and grow.

Massively scalable

When you create a hybrid account + UTXO Layer 1 blockchain, you get magic. Imagine no limitations on growth. Imagine fees that are fractions of a penny. Imagine a blockchain that is ready to build on at launch and not always waiting on the next update, pivot, or fix. Imagine Radiant.

Unbounded creativity

Make your dreams come to life – auctions, games, tokens, apps, NFTs – anything you can think of with ultra-flexible Turing Complete smart contracts. Radiant allows creatives of all kinds to have real ownership of their digital assets and use the peer-to-peer network to monetize and exchange their creations.


PoW SHA512/256

Proof-of-work is the only consensus system for maximum security and efficiency. Embracing full decentralization so that no miner can stake to centralize the network. Fairly launched and community mined from the beginning. The FIPS SHA512/256 Standard is up to 50% more efficient than SHA256 on 64 bit architectures.

Full stack open source

The Radiant Node, associated modules, and libraries were released upon launch. All source code is available as open source (MIT licensed) and comes with no warranties or guarantees of any kind. It is free to use or copy as users and developers wish.

Network fuel

The fuel for the network is the Radiant unit (RXD). A small amount of RXD is used to pay transaction fees to miners for processing transfers and for the execution of smart contracts.

Proof of Work is green

Proof-of-Work uses energy intensive processes to secure the network but is highly efficient. This is because POW’s energy expenditure needs to be weighed against the massive energy usage of the legacy systems that POW replaces. When POW’s energy consumption is examined in the context of current wasteful legacy systems, POW is highly energy efficient and green.

Induction proofs

The induction proof system makes it possible to efficiently compose outputs in any manner, without compromising the inherent parallelism and scalability characteristics of the UTXO based architecture.

Account emulation

With the novel induction proof technique, it is now possible to create globally unique identifiers and therefore implement accounts in the UTXO based architecture. The best of both worlds in emulating the account models while also providing the coin (UTXO) model that offers massive scale and parallelism.

Radiant: A Peer-to-Peer Digital Asset System
Radiant Blockchain System Design
FAQs
Induction Proof System FAQs
Links

Radiant: un Système d'Actifs Numériques Pair-à-Pair

French version of the Radiant whitepaper

The Radiant Developers

11 août 2022

radiantblockchain.org

Résumé : Le réseau Radiant est un système d'actifs numériques pair-à-pair qui permet l'échange direct de valeur sans passer par une partie centrale. Le protocole Bitcoin original [1] fournit ce qui est nécessaire pour créer un système de trésorerie électronique pair-à-pair, mais il ne possède pas la capacité de vérifier les historiques de transactions et, par conséquent, ne peut pas être utilisé pour valider des actifs numériques. Les signatures numériques et les contraintes de sortie fournissent une partie de la solution, mais les principaux avantages sont perdus si une tierce partie de confiance est encore nécessaire pour valider les actifs numériques. De manière similaire à Bitcoin, le réseau Radiant ne nécessite qu'une structure minimale et horodate les transactions dans une chaîne de preuve de travail basée sur le hachage. Nous introduisons deux techniques pour valider les actifs numériques : des références uniques et un système de preuve d'induction général à la fois opérant dans un temps et un espace constants O(1). Il est possible de composer les sorties de n'importe quelle manière, sans compromettre les caractéristiques intrinsèques de parallélisme et de performance d'une architecture basée sur les sorties de transaction non dépensées (UTXO). Par conséquent, les utilisateurs peuvent quitter et rejoindre le réseau Radiant à leur guise et être assurés de l'intégrité et de l'authenticité de leurs actifs numériques.

1. Introduction

Le commerce avec les blockchains, ou la technologie de registre numérique (DLT), repose souvent sur des émetteurs et des gardiens agissants en tant que parties de confiance pour authentifier les actifs numériques. Bien que ces systèmes fonctionnent assez bien pour les transactions de type paiement électronique, ils souffrent toujours des faiblesses inhérentes du modèle de confiance pour les utilisations avancées. Les blockchains basées sur la machine virtuelle Ethereum (EVM) [2] sont très flexibles pour tous les types de programmes, mais les frais élevés les rendent impraticables pour les applications de micropaiement.

Ce dont nous avons besoin, c'est un système de paiement électronique pouvant être utilisé pour un système de gestion d'actifs numériques avec des frais réduits, une haute performance et des capacités de programmation avancées. Dans cet article, nous proposons une solution au problème de l'expansion et de la contraction des blockchains en utilisant deux techniques novatrices qui fournissent des références uniques et un système général de preuve par induction, ce qui permet des programmes de Turing complet [3] à travers les frontières de transaction. Le système proposé est décentralisé, utilise un mécanisme de consensus de preuve de travail comme Bitcoin, mais avec des niveaux de débit considérablement plus élevés, tout en offrant la même flexibilité que les blockchains basées sur EVM, avec des frais très bas.

2. Transactions

Similaire à Bitcoin, nous définissons une pièce électronique comme une chaîne de signatures numériques. Ce qui distingue les transactions dans Radiant, c'est que chaque propriétaire transfère la pièce au suivant en signant numériquement un hachage de la transaction précédente, en plus des paramètres d'entrée requis pour déverrouiller la pièce. Une transaction crée également de nouvelles contraintes de verrouillage de sortie, qui peuvent inclure la clé publique du prochain propriétaire, parmi toutes les autres règles définies par l'utilisateur.

Diagram 1. Transactions Radiant.

Pour vérifier qu'une double dépense ne s'est pas produite, nous utilisons un serveur d'horodatage distribué, en utilisant un système de preuve de travail basé sur le hachage pour organiser l'historique canonique afin de déterminer quelle transaction est arrivée en premier. Les transactions sont organisées en blocs. Par convention, la première transaction, appelée "transaction de coinbase", dans un bloc est une transaction spéciale qui démarre une nouvelle pièce appartenant au créateur du bloc. Les blocs sont chaînés ensemble et organisent les transactions en un arbre de Merkle [4]. Toutes les transactions, à l'exception de la première, doivent référencer une transaction précédente formant un graphe acyclique dirigé (DAG) où toutes les pièces se connectent finalement à au moins l'une des transactions spéciales au début d'un bloc.

Diagram 2. Structure des blocs ; les transactions sont organisées dans un arbre de Merkle.

Le problème avec cette conception, dans le contexte des actifs numériques, est qu'il n'y a qu'un seul type de pièce ou d'actif numérique, et pas de concept de pièces définies par l'utilisateur (ou de types d'actifs numériques). La conception fonctionne assez bien pour les transactions électroniques de type paiement dans l'unité de compte native, mais elle ne se prête pas immédiatement à être utilisée pour d'autres types de pièces ou d'actifs numériques. Une solution courante consiste à introduire un service tel qu'un indexeur de transaction qui surveille les transactions pour détecter des séquences de données spéciales signalant la création d'un actif numérique. Le problème avec cette solution est qu'elle dépend de l'entreprise qui exécute le service, l'authenticité de l'actif numérique devant être de confiance, tout comme tout autre service sur le Web.

Nous avons besoin d'un moyen pour que les utilisateurs puissent indiquer la création de types de pièces personnalisées, mais sans dépendre d'un service de confiance pour être utilisé pour la présentation de données.

3. Actifs Numériques

Nous définissons une pièce électronique personnalisée, ou un actif numérique, comme une chaîne de signatures électroniques. Un actif numérique est un type de pièce défini par l'utilisateur à l'aide d'un marqueur de transaction spécial, appelé une "transaction d'actif de base", pour créer ou émettre un actif numérique. De manière similaire aux transactions Coinbase, qui injectent de nouvelles pièces dans le système, la transaction d'actif de base colore ou étiquette la pièce électronique avec un identifiant unique de 36 octets pour sa durée de vie. La pièce électronique personnalisée est superposée au type de pièce de base et fonctionne de manière similaire. La transaction d'actif de base peut apparaître n'importe où dans le bloc et peut imposer toutes les règles et contraintes personnalisées décidées à l'avance.

Diagram 3. Transactions représentant des types de pièces définis par l'utilisateur — ou des actifs numériques.

Pour réaliser cela, nous devons créer un identifiant unique stable et un mécanisme de transaction pour suivre l'authenticité du type de pièce (actif numérique). Les utilisateurs du système doivent avoir la preuve que les types de pièces personnalisées ne sont pas des contrefaçons et représentent avec précision les actifs numériques.

Diagram 4. qui montre que les types de pièces personnalisées définies par l'utilisateur sont créés à partir d'une transaction spéciale de "mint". Un identifiant unique est utilisé pour classer le type de pièce.

4. Identifiants Uniques

Pour mettre en place un identificateur unique pour un type de coin, nous utilisons une transaction marqueur spéciale, appelée "transaction d'actif de base", qui agit comme le début (la création) de la chaîne de signatures numériques. Au lieu de nécessiter une nouvelle structure de données pour l'identificateur unique, nous réutilisons l'identifiant de transaction et l'index de sortie, appelé un "outpoint", comme identificateur unique pour le type de coin. Il est assuré que les outpoints (36 octets) sont aléatoires et globalement uniques.

Une instruction de programmation, appelée OP_PUSHINPUTREF, est utilisée pour attacher une référence à une sortie. L'instruction accepte exactement un paramètre de 36 octets qui doit correspondre soit 1) au point de sortie de l'une des sorties dépensées, ou 2) à la même valeur de 36 octets qui apparaît déjà dans un OP_PUSHINPUTREF spécifié précédemment dans l'une des sorties dépensées. La seule façon pour une valeur donnée d'apparaître dans une sortie de transaction est que, grâce à une transaction ancêtre, elle correspondait au point de sortie de la transaction initiale de création d'actifs. Les transactions qui spécifient une valeur qui ne répond pas à l'une ou l'autre de ces conditions sont invalides.

Diagram 5. Les identifiants uniques sont initialisés en associant un outpoint de l'une des sorties dépensées, puis sont maintenus aussi longtemps qu'au moins l'une des sorties dépensées contient le même identifiant unique dans le corps du script.

Cette simple instruction de programmation fournit un identifiant unique qui peut être utilisé comme une référence stable pour créer des règles avancées. Par exemple, différents types de pièces, des actifs numériques, peuvent maintenant dépendre d'autres types de pièces. Comme toutes les données sont locales à la transaction, via ses transactions d'entrée parent immédiates, il est facile pour les clients et les services de valider l'authenticité d'un actif numérique en temps constant O(1) et d'espace, évitant ainsi le besoin d'un service de confiance.

5. Preuves par induction

Il est possible de créer des identifiants uniques d'une manière alternative et de fournir également un mécanisme de preuves par induction mathématique en utilisant un algorithme de hachage de transaction modifié. En permettant aux scripts d'entrée d'accepter la transaction parent dépensée, les règles peuvent vérifier que le parent, ainsi que son grand-parent, respectent les règles requises. Le problème évident est que chaque copie complète des transactions parent est intégrée, une explosion de taille exponentielle se produit et empêche l'utilisation pratique de la technique. Ce qui est nécessaire, c'est une façon de compresser la transaction, de sorte qu'une structure de données de taille fixe puisse être utilisée à la place pour dériver le hachage de la transaction, au lieu de nécessiter le contenu complet de la transaction.

Diagram 6. Validation complète de la transaction parent, preuve par induction mathématique en intégrant les transactions parent complètes dans les entrées, entraînant une augmentation exponentielle de la taille de la transaction.

Nous pouvons y parvenir en modifiant l'algorithme de hachage de transaction utilisé dans Bitcoin, où une somme de contrôle double sha-256 est calculée à partir de la transaction sérialisée, en une nouvelle version qui résume d'abord le contenu de la transaction pour dériver le hachage. Nous introduisons l'algorithme de hachage de transaction version 3, pour le distinguer de l'utilisation des versions 1 et 2 dans Bitcoin. Le processus consiste à hacher chaque champ ou composant d'une transaction, pour obtenir un hachage intermédiaire, qui peut être utilisé comme entrée de taille fixe et ainsi éviter la croissance exponentielle de la taille de la transaction.

Nous utilisons la structure de données suivante de 112 octets, au lieu des octets de transaction sérialisés complets, qui sont à leur tour hachés en double sha-256 pour obtenir le hachage de transaction final.

Voici la structure de données de 112 octets pour l'antécédent de hachage de transaction de version 3, qui est utilisée à la place des octets de transaction sérialisés complets :

Champs d'antécédents de hachage de transaction de version 3 :

  1. nVersion (= 3) de la transaction (4 octets en petit endian)

  2. nTotalInputs (4 octets en petit endian)

  3. hashPrevoutInputs (32 octets de hachage)

  4. hashSequence (32 octets de hachage)

  5. nTotalOutputs (4 octets en petit endian)

  6. hashOutputHashes (32 octets de hachage)

  7. nLocktime de la transaction (4 octets en petit endian)

En utilisant l'algorithme de hachage de transaction pour les transactions de version 3, nous sommes en mesure d'intégrer la transaction parent et la transaction grand-parent à chaque étape d'une preuve d'induction mathématique pour éviter une augmentation de la taille de la transaction, et pouvons appliquer toutes les règles nécessaires.

Diagram 7. Validation compressée de la transaction parent, preuve par induction mathématique en intégrant la structure de données d'antécédent de hachage de transaction de version 3 de la transaction parent et de la transaction grand-parent pour appliquer des règles et des contraintes arbitraires.

6. Réseau

La topologie du réseau est un graphe presque complet, dans lequel chaque nœud de minage est connecté à tous les autres nœuds de minage. Les étapes pour exécuter le réseau sont les mêmes que pour Bitcoin, avec quelques distinctions pour les différents types de nœuds : les nœuds de minage, les nœuds agents et les nœuds d'archives. Les nœuds de minage sont les éditeurs actifs de blocs et maintiennent le consensus avec tous les autres nœuds, les nœuds d'archives fournissent des données de bloc historiques, et les nœuds agents sont conçus pour filtrer les blocs et suivre les transactions d'intérêt pour les applications qu'ils servent. Les nœuds d'archives et les nœuds agents peuvent opérer sur le même réseau pair-à-pair mais ne produisent pas de blocs. Les nœuds non-miniers tels que les nœuds d'archives et les nœuds agents sont parfois appelés "nœuds d'écoute" pour distinguer leur rôle dans le réseau.

Diagram 8. Les nœuds de minage sont bien connectés et construisent sur les blocs les uns des autres. Les nœuds d'archive stockent des blocs complets pour l'analyse historique et le démarrage. Les nœuds agents sont des nœuds d'écoute qui filtrent et stockent les transactions pour servir les clients.

Les nœuds de minage sont bien connectés dans un graphe presque complet avec d'autres nœuds de minage. Leur travail consiste à construire sur les blocs les uns des autres et à maintenir un consensus pour les quelques centaines de blocs les plus récents, ainsi qu'à maintenir l'ensemble des UTXO pour prévenir les doubles dépenses.

Les nœuds agents n'ont besoin de stocker qu'un sous-ensemble de transactions, par exemple, un type de pièce ou un actif numérique spécifique. Même avec de grands blocs, un nœud agent peut rapidement filtrer les transactions par références ou séquences de bytes spécifiques, puis stocker ces transactions pour les servir via une interface de programmation d'application. Entre agents coopérants, une racine d'arbre de Merkle peut être annoncée publiquement pour les transactions dans chaque bloc qui correspondent à un modèle prédéterminé, afin de signaler aux autres agents et aux consommateurs quelles transactions cet agent a traitées.

Les nœuds d'archive sont utilisés pour créer des copies de sauvegarde de blocs entiers pour diverses applications, notamment la conservation de données, l'analyse et l'apprentissage automatique. Étant donné que les nœuds d'archive ne sont pas directement impliqués dans le minage, ils n'ont pas les mêmes exigences de performances en temps réel et de bande passante que les nœuds de minage ou d'agents.

7. Calculs

Nous examinons le scénario dans lequel le réseau Radiant continue de croître et ce que cela implique pour les exigences de traitement des nœuds miniers, d'archivage et d'agent. Pour comparaison, au moment de la rédaction, il y a environ 83 millions de sorties de transaction non dépensées pour la blockchain Bitcoin, pour un total d'environ 6 Go de données nécessaires pour éviter les doubles dépenses. Il est seulement nécessaire que les quelques centaines de blocs les plus récents soient conservés par les nœuds miniers, les blocs plus anciens étant disponibles auprès des nœuds d'archivage. Pour les nœuds d'agent, il est nécessaire de conserver la partition pertinente de sorties de transaction non dépensées qui concernent les applications qu'ils desservent, la mise à l'échelle étant une fonction de la bande passante et non des exigences de stockage.

Pour nos besoins, nous supposons qu'il y aura des blocs de 3 Go toutes les 5 minutes à horodater et à distribuer sur le réseau, soit environ 20 000 transactions par seconde avec une taille de transaction moyenne de 500 octets, soit environ 6 000 000 transactions par bloc. Nous montrons que pour chaque type de noeud, le réseau est capable de se développer suffisamment pour répondre à la demande mondiale. Cela équivaut à environ 1 transaction tous les 5 jours pour chacun des 8 milliards de personnes sur la planète.

Noeuds de minage

Les noeuds miniers (Mining Nodes) sont les seuls types de nœuds à construire sur les blocs des autres. Pour maintenir un consensus, il suffit de synchroniser l'ensemble des sorties de transactions non dépensées (UTXO) et de ne conserver que les cent derniers blocs. Au moment de la rédaction, les disques durs bon marché à haute performance sont capables de réaliser plus de 120 000 E/S par seconde, coûtant environ 500 USD pour 280 Go, et peuvent donc gérer environ 20 000 transactions par seconde (en supposant que chaque transaction a 2 entrées et 2 sorties). Il y a 2 lectures pour les entrées, 2 mises à jour des entrées et 2 écritures pour la nouvelle sortie: 120 000 / 6 = 20 000 transactions/seconde.

Les nœuds d'archivage

Les Archive Nodes fournissent des données de blocs historiques et conviennent aux applications de machine learning, d'analyse et d'entreposage de données. Les Archive Nodes peuvent compléter le démarrage des Mining Nodes et exécuter des vérifications de cohérence périodiques sur l'ensemble des transactions non dépensées (UTXO). Au moment de la rédaction, des disques durs de 18 To sont disponibles pour environ 350 USD. En supposant 3 Go de données toutes les 5 minutes, cela équivaut à une exigence de stockage de 732 Go par jour, soit environ 22 To par mois. Le coût matériel pour une année est de 15 disques durs, avec une capacité de 18 To, pour un coût incrémentiel annuel de 5 000 USD.

Les nœuds d'Agent

Les nœuds d'agent se mettent à l'échelle de la manière la plus facile parmi les types de nœuds car ils ne traitent que les types de transactions pertinents pour les applications qu'ils servent. Par conséquent, les nœuds d'agent peuvent aller d'un serveur web à un appareil IoT léger avec des capacités de traitement et de stockage limitées, et pourtant suivre le rythme des blocs de 3 Go, ou 20 000 transactions par seconde. Par exemple, une entreprise peut souhaiter suivre l'utilisation de ses points de fidélité, créés en tant qu'actif numérique, et n'a donc besoin de sélectionner qu'un petit sous-ensemble de mises à jour de transactions de chaque bloc qui correspondent à l'identifiant unique de ce type de pièce.

Au moment de la rédaction, un dispositif informatique commercial, le Raspberry Pi 4, est vendu environ 275 USD, il dispose d'un processeur quadcore de 1,5 GHz et de 4 Go de RAM, qui peut être utilisé pour filtrer rapidement et éliminer les transactions non pertinentes à une vitesse de 5 000 transactions par cœur. Bien sûr, il ne s'agit là que d'un exemple de la facilité à traiter de grands blocs, dans une application Web typique, il peut y avoir beaucoup plus de cœurs disponibles.

La vitesse de bande passante médiane des 25 premiers pays dépasse 100 Mb/s, soit environ 10 Mo/seconde de téléchargement, de nombreux fournisseurs de services Internet offrant des téléchargements illimités. Les exigences en bande passante pour des blocs de 3 Go toutes les 5 minutes sont d'environ 10 Mo/seconde pour un total de 22 To par mois. Des hiérarchies de nœuds Agent peuvent également être créées pour réduire les exigences totales de bande passante pour les nœuds Agent ayant une capacité de bande passante plus faible.

8. Conclusion

Nous avons proposé un système de gestion d'actifs numériques sans avoir besoin de faire confiance à une tierce partie. Nous avons commencé par les blocs de base de pièces faites à partir de signatures numériques, qui fournissent un contrôle fort de la propriété. À partir des règles et des incitations nécessaires, nous avons introduit deux méthodes novatrices pour authentifier et suivre les actifs numériques en temps et en espace constants O(1). Les deux méthodes fournissent indépendamment un système de preuve par induction mathématique général qui peut encoder n'importe quelle configuration d'actifs numériques possible. Le système Turing est complet dans et entre les limites de transaction, sans avoir besoin de couches secondaires. Radiant est une conception révolutionnaire qui offre les avantages de performance et de parallélisme d'une blockchain de sortie de transaction non dépensée (UTXO), mais avec la capacité de contracter des blockchains basées sur des comptes en utilisant la machine virtuelle Ethereum (EVM).

References

[1] Satoshi Nakamoto, "Bitcoin: A Peer-to-Peer Electronic Cash System" URL

https://bitcoin.org/bitcoin.pdf, 2009.

[2] Vitalik Buterin, "Ethereum: A Next-Generation Smart Contract and Decentralized

Application Platform." URL https://ethereum.org/en/whitepaper/, 2014.

[3] Wikipedia contributors. "Turing completeness." Wikipedia, The Free Encyclopedia.

Wikipedia, The Free Encyclopedia, URL https://en.wikipedia.org/wiki/Turing_completeness,

21 Jul. 2022

[4] R.C. Merkle, "Protocols for public key cryptosystems," In Proc. 1980 Symposium on Security and Privacy, IEEE Computer Society, pages 122-133, April 1980.

[5] Britannica, T. Editors of Encyclopaedia. "mathematical induction." Encyclopedia Britannica, URL https://www.britannica.com/science/mathematical-induction, 2018

Diagram 1. Radiant Transactions.
Diagram 2. Block Structure; transactions are organized into a Merkle Tree.
Diagram 3. Transactions representing user-defined coin types — or digital assets.
Diagram 4. Custom user-defined coin types are defined from a special mint transaction. A unique identifier is used to classify the coin type.
Diagram 5. Unique identifiers are initialized by matching an outpoint of one of the outputs being spent, and then maintained as long as at least one of the outputs being spent contains the same unique identifier in the script body.
Diagram 6. Full parent transaction validation, mathematical induction proof by embedding the full parent transactions into the inputs resulting in exponential transaction size increase.
Diagram 7. Compressed parent transaction validation, mathematical induction proof by embedding the transaction hash version 3 preimage data-structure of the parent and grand-parent to enforce arbitrary rules and constraints.
Diagram 8. Mining Nodes are well-connected and build on top of each other's blocks. Archive nodes store complete blocks for historical analysis and bootstrapping purposes. Agent nodes are listener nodes which filter and store transactions to serve clients.

Radiant: A Peer-to-Peer Digital Asset System

The Radiant Developers

August 11, 2022

radiantblockchain.org

Abstract. The Radiant network is a peer-to-peer digital asset system that enables direct exchange of value without going through a central party. The original Bitcoin[1] protocol provides what is needed to create a peer-to-peer electronic cash system, but lacks the ability to verify transaction histories and therefore, cannot be used to validate digital assets. Digital signatures and output constraints provide part of the solution, but the main benefits are lost if a trusted third party is still required to validate digital assets. Similarly to Bitcoin, the Radiant network requires minimal structure, and timestamps transactions into an ongoing hash-based chain of proof-of-work. We introduce two techniques to validate digital assets: unique references and a general purpose induction proof system both of which operate in constant O(1) time and space. It is possible to compose outputs in any manner, without compromising the inherent parallelism and performance characteristics of an unspent transaction output (UTXO) based architecture. Therefore, users can leave and rejoin the Radiant network at will and be assured of the integrity and authenticity of their digital assets.

1. Introduction

Commerce with blockchains, or digital ledger technology (DLT), in many cases relies on issuers and custodians serving as trusted parties to authenticate digital assets. While those systems work well enough for electronic payment-like transactions, they still suffer from the inherent weaknesses of the trust based model for advanced uses. The Ethereum Virtual Machine (EVM) [2] based blockchains are very flexible for all kinds of programs, but the high fees makes use for micropayment applications impractical.

What is needed is an electronic payment system that can be used for digital asset management system with low fees, high performance, and advanced programming capabilities. In this paper, we propose a solution to the problem of blockchain scaling and contracting using two novel techniques which provide unique references and a general induction proof system, which makes Turing Complete [3] programs across transaction boundaries possible. The system proposed is decentralized, uses a proof-of-work consensus mechanism like Bitcoin, but with significantly higher levels of throughput, while providing the same flexibility of EVM-based blockchains, with very low fees.

2. Transactions

Similar to Bitcoin, we define an electronic coin as a chain of digital signatures. Where transactions in Radiant differ is that each owner transfers the coin to the next by digitally signing a hash of the previous transaction in addition to the required input params to unlock the coin. A transaction also creates new output locking constraints, which may include the public key of the next owner, amongst any other rules defined by the user.

Diagram 1. Radiant Transactions.\

To verify that a double-spend did not occur, we use a distributed timestamp server, using a hash-based proof-of-work system to organize the canonical history to determine which transaction arrived first. The transactions are organized into blocks. As a matter of convention, the first transaction, called a "coinbase transaction", in a block is a special transaction that starts a new coin owned by the creator of the block. Blocks are chained together and organize transactions into a Merkle Tree [4]. All transactions, with the exception of the first, must reference a previous transaction forming a directed acyclic graph (DAG) where all coins eventually connect back to at least one of the special transactions at the beginning of a block.

Diagram 2. Block Structure; transactions are organized into a Merkle Tree.\

The problem with this design, in the context of digital assets, is that there is only one type of coin, or digital asset, and no concept of user-defined coins (or digital asset types). The design works well enough for electronic payment-like transactions in the native unit of account, however it does not immediately lend itself to be used for other types of coins or digital assets. A common solution is to introduce a service such as a transaction indexer which monitors transactions for special data sequences to signify the creation of a digital asset. The problem with this solution is that it depends on the company running the service, with digital asset authenticity needing to be trusted, just like any other service on the web.

We need a way for the users to indicate the creation of custom coin types, but not rely on a trusted service to be used for data presentation.

3. Digital Assets

We define a custom electronic coin, or digital asset, as a chain of digital signatures. A digital asset is a user-defined coin type using a special transaction marker, called an "assetbase transaction", to create or mint a digital asset. Similar to coinbase transactions, which inject new coins into the system, the assetbase transaction colors or tags the electronic coin with a unique 36-byte identifier for it's lifetime. The custom electronic coin is overlayed on top of the base coin type and functions in a similar manner. The assetbase transaction can appear anywhere in the block and may enforce any custom rules and constraints decided upfront.

Diagram 3. Transactions representing user-defined coin types — or digital assets.\

To accomplish this we need to create a stable unique identifier and a transaction mechanism to track the authenticity of the coin type (digital asset). The users of the system need to have proof that custom coin types are not forgeries and accurately represent the digital assets.

Diagram 4. Custom user-defined coin types are defined from a special mint transaction. A unique identifier is used to classify the coin type.\

4. Unique Identifiers

To implement a unique identifier for a coin type, we use a special marker transaction, called "assetbase transaction", which acts as the start (mint) of the chain of digital signatures. Rather than require a new data-structure for the unique identifier, we reuse the transaction identifier and output index, called an "outpoint", as the unique identifier for the coin type. It is assured that outpoints (36-bytes) are random and globally unique.

A programming instruction, called OP_PUSHINPUTREF, is used to attach a reference to an output. The instruction accepts exactly one 36-byte parameter which must match either 1) the outpoint of one of the outputs being spent, or 2) the same 36-byte value already appears in a previously specified OP_PUSHINPUTREF in one of the outputs being spent. The only way for any given value to appear in a transaction output is that, through some ancestor transaction, it matched the outpoint from the initial minting assetbase transaction. Transactions that specify a value which do not meet either condition are invalid.

Diagram 5. Unique identifiers are initialized by matching an outpoint of one of the outputs being spent, and then maintained as long as at least one of the outputs being spent contains the same unique identifier in the script body.\

This simple programming instruction provides a unique identifier which can be used as a stable reference to create advanced rules. For example, different coin types, digital assets, can now depend on other coin types. Since all of the data is local to the transaction, via it's immediate parent input transactions, it is easy for clients and services to validate the authenticity of a digital asset in O(1) constant time and space, avoiding the need for a trusted service.

5. Proofs By Induction

It is possible to create unique identifiers in an alternative manner and also provide a mechanism for mathematical induction [5] proofs using a modified transaction hash algorithm. By allowing input scripts to accept the parent transaction being spent, the rules can verify that the parent, and it's grand-parent conform to the required rules. The obvious problem is that as each full copy of the parent transactions are embedded, an exponential size explosion occurs and prevents the practical use of the technique. What is needed is a way to compress the transaction, so that a fixed sized data-structure can be used instead to derive the transaction hash, instead of requiring the full transaction contents.

Diagram 6. Full parent transaction validation, mathematical induction proof by embedding the full parent transactions into the inputs resulting in exponential transaction size increase.\

We can accomplish it by modifying the transaction hash algorithm used in Bitcoin, wherein a double sha-256 digest is calculated from the serialized transaction, into a new version that first summarizes the transaction contents to derive the hash. We introduce transaction hash algorithm version 3, to distinguish it from the use of version 1 and version 2 in Bitcoin. The process is to hash each field, or component of a transaction, to an intermediate hash, which can be used as a fixed size input and thereby avoid the exponential transaction size growth.

We use the following 112-byte data-structure, instead of the complete serialized transaction bytes, which in turn is double sha-256 hashed to finally obtain the transaction hash.

Transaction Version 3 Hash Preimage Fields:

  1. nVersion(=3) of the transaction (4 byte little endian)

  2. nTotalInputs (4 byte little endian)

  3. hashPrevoutInputs (32 byte hash)

  4. hashSequence (32 byte hash)

  5. nTotalOutputs (4 byte little endian)

  6. hashOutputHashes (32 byte hash)

  7. nLocktime of the transaction (4 byte little endian)

By using the transaction hash algorithm for version 3 transactions, we are able to embed the parent and grand-parent transaction in each step of a mathematical induction proof to prevent transaction size increase, and can enforce any rules needed.

Diagram 7. Compressed parent transaction validation, mathematical induction proof by embedding the transaction hash version 3 preimage data-structure of the parent and grand-parent to enforce arbitrary rules and constraints.\

6. Network

The network topology is a nearly complete graph, wherein every Mining Node is connected to every other Mining Node. The steps to run the network are the same as Bitcoin, with some distinctions for different node types: Mining Nodes, Agent Nodes, Archive Nodes. Mining Nodes are the active publishers of blocks and maintain consensus with all other nodes, Archive Nodes serve historical block data, and Agent Nodes are designed to filter blocks and track transactions of interest to the applications they serve. Archive and Agent Nodes may operate on the same peer-to-peer network and yet do not produce blocks. The non-Mining Nodes such as Archive and Agent Nodes are sometimes referred to as "listener nodes" to distinguish their role in the network.

Diagram 8. Mining Nodes are well-connected and build on top of each other's blocks. Archive nodes store complete blocks for historical analysis and bootstrapping purposes. Agent nodes are listener nodes which filter and store transactions to serve clients.\

Mining Nodes are well-connected into a nearly complete graph between other Mining Nodes. Their job is to build on top of each other's blocks and maintain consensus for the most recent few hundred blocks, and maintain the UTXO set for the prevention of double-spending.

Agent Nodes only need to store a subset transactions, for example, a specific coin type or digital asset. Even with large blocks, an Agent Node can quickly filter transactions by references or specific byte-sequences, then store those transactions to serve via an application programmer interface. Between cooperating agents, a Merkle Tree root hash can be publicly announced for the transactions in each block that match a pre-determined pattern to signal to other Agents and consumers which transactions that Agent has processed.

Archive Nodes are used to create backup copies of entire blocks for various applications including data warehousing, analytics and machine learning. Since Archive Nodes are not involved directly in mining, they do not have the same real-time performance and bandwidth requirements as Mining or Agent Nodes.

7. Calculations

We consider the scenario where the Radiant network continues to grow and what it entails for the processing requirements of Mining, Archive and Agent Nodes. For comparison, at the time of writing, there are about 83 million unspent transaction outputs for the Bitcoin blockchain, for a total of about 6 GB of necessary data to prevent double spends. It is only necessary that the most recent few hundred blocks are kept by Mining Nodes, with older blocks being available from Archive Nodes. For Agent Nodes, it is necessary to keep the relevant partition of unspent transaction outputs that pertain to the applications they serve, scaling is a function of bandwidth and not storage requirements.

For our purposes, we assume that there will be 3 GB sized blocks every 5 minutes to be timestamped and distributed across the network, or about 20,000 transactions per second with the average transaction size of 500 bytes, or about 6,000,000 transactions per block. We show that for each type of node, the network is able to adequately scale to meet global demand. This equals about 1 transaction every 5 days for each of the 8 billion people on the planet.

Mining Nodes

Mining Nodes are the only node type which build on top of each other's blocks. To maintain consensus, it is sufficient to synchronize the unspent transaction output (UTXO) set, and only maintain about the last hundred blocks. At the time of writing, high performance commodity solid-state drives are capable of achieving upwards of 120,000 IOPS, costing about $500 USD for 280 GB, and can therefore handle about 20,000 transactions per second (assuming each transaction has 2 inputs and 2 outputs). There are 2 reads for inputs, 2 updates to the inputs, and 2 writes for the new output: 120,000 / 6 = 20,000 transactions/second.

Archive Nodes

Archive Nodes provide historical block data and are suitable for machine learning, analytics, and data warehousing applications. The Archive Nodes may supplement bootstrapping of Mining Nodes and to run periodic consistency checks on the UTXO set. At the time of writing, commodity hard disks of 18 TB are available for about $350 USD. Assuming 3 GB of data every 5 minutes equals 732 GB of data storage requirement per day, or about 22 TB per month. The hardware cost for a year is 15 hard drives, with 18 TB capacity, for a yearly incremental cost of $5,000 USD.

Agent Nodes

Agent Nodes scale most easily amongst the node types because they only process the relevant transaction types for the applications they serve. As a result the Agent Nodes can range from a web server to a lightweight IoT device with limited processing and storage capabilities, and yet still keep peace with 3 GB blocks, or 20,000 transactions per second. For example, a company may wish to track the usage of it's loyalty points, created as a digital asset, and therefore only need to select a small subset of transaction updates from each block that match the unique identifier for that coin type.

At the time of writing, a commercial computing device, Raspberry Pi 4, sells for about $275 USD which has a quadcore 1.5 GHZ processor and 4 GB of RAM, which can be used to quickly filter, and discard irrelevant transactions, at a rate of 5,000 transactions per core. Of course, this is just an example of how reasonable it is to process large blocks, in a typical web application may have many more cores available.

The median bandwidth speed of the top 25 countries exceeds 100 MBPS, or about 10 MB/second download, with many internet service providers offering unlimited downloads. The bandwidth requirements for 3 GB blocks every 5 minutes is about 10 MB/second for a total of 22 TB per month. Hierarchies of Agent Nodes can also be created to filter down the total bandwidth requirements for Agent Nodes with lower bandwidth capacity.

8. Conclusion

We have proposed a system for digital asset management without relying on trust. We started with the basic building blocks of coins made from digital signatures, which provides strong control of ownership. From the needed rules and incentives, we introduced two novel methods for authenticating and tracking digital assets in constant O(1) time and space. Both methods independently provide a general mathematical induction proof system which can encode any possible digital asset configuration. The system is Turing Complete within and across transaction boundaries, without need for secondary layers. Radiant is a breakthrough design which provides the performance and paralellism benefits of an unspent transaction output (UTXO) blockchain, but with the contracting ability of account-based blockchains based on the Ethereum Virtual Machine (EVM).

References

[1] Satoshi Nakamoto, "Bitcoin: A Peer-to-Peer Electronic Cash System" URL https://bitcoin.org/bitcoin.pdf, 2009.

[2] Vitalik Buterin, "Ethereum: A Next-Generation Smart Contract and Decentralized Application Platform." URL https://ethereum.org/en/whitepaper/, 2014.

[3] Wikipedia contributors. "Turing completeness." Wikipedia, The Free Encyclopedia. Wikipedia, The Free Encyclopedia, URL https://en.wikipedia.org/wiki/Turing_completeness, 21 Jul. 2022

[4] R.C. Merkle, "Protocols for public key cryptosystems," In Proc. 1980 Symposium on Security and Privacy, IEEE Computer Society, pages 122-133, April 1980.

[5] Britannica, T. Editors of Encyclopaedia. "mathematical induction." Encyclopedia Britannica, URL https://www.britannica.com/science/mathematical-induction, 2018

Diagram 1. Radiant Transactions.
Diagram 2. Block Structure; transactions are organized into a Merkle Tree.
Diagram 3. Transactions representing user-defined coin types — or digital assets.
Diagram 4. Custom user-defined coin types are defined from a special mint transaction. A unique identifier is used to classify the coin type.
Diagram 5. Unique identifiers are initialized by matching an outpoint of one of the outputs being spent, and then maintained as long as at least one of the outputs being spent contains the same unique identifier in the script body.
Diagram 6. Full parent transaction validation, mathematical induction proof by embedding the full parent transactions into the inputs resulting in exponential transaction size increase.
Diagram 7. Compressed parent transaction validation, mathematical induction proof by embedding the transaction hash version 3 preimage data-structure of the parent and grand-parent to enforce arbitrary rules and constraints.
Diagram 8. Mining Nodes are well-connected and build on top of each other's blocks. Archive nodes store complete blocks for historical analysis and bootstrapping purposes. Agent nodes are listener nodes which filter and store transactions to serve clients.

Induction Proof System FAQs

What is the induction proof system in the Radiant blockchain?

The induction proof system is a method used in the Radiant blockchain to ensure the authenticity and traceability of digital assets. It verifies that each transaction is linked back to a valid genesis transaction, preventing forgery and ensuring that all assets are genuine.

How does the induction proof system work?

The induction proof system works by including references to parent and grandparent transactions in each new transaction. These references create a chain of trust, where each transaction can be verified against its predecessors, all the way back to the original genesis transaction.

Why is the induction proof system important?

The induction proof system is important because it provides a secure and efficient way to validate digital assets without relying on trusted third parties. It ensures that all assets on the Radiant blockchain are genuine and prevents double-spending and forgery.

What are unique identifiers, and how are they used?

Unique identifiers are like serial numbers for digital assets. They help track the asset's journey from the genesis transaction through all subsequent transactions. Each transaction output has a unique identifier, ensuring that the asset's lineage can be verified.

How does Radiant prevent exponential growth in transaction size?

The Radiant blockchain uses a compressed transaction hash algorithm to summarize transaction contents into a fixed-size data structure. This prevents the exponential growth in transaction size that would occur if full copies of parent transactions were included in each new transaction.

What is a genesis transaction?

A genesis transaction is the original creation of a digital asset on the Radiant blockchain. It serves as the starting point for the asset's lineage and is referenced in all subsequent transactions to verify authenticity.

How can I validate a transaction on the Radiant blockchain?

To validate a transaction, you need to check the references to its parent and grandparent transactions. By verifying these references, you can ensure that the transaction is genuine and traceable back to the genesis transaction.

What are Opcodes, and how are they used in the induction proof system?

Opcodes are programming instructions used in the Radiant blockchain to manage and validate transaction references. Examples include OP_PUSHINPUTREF, OP_REQUIREINPUTREF, and OP_DISALLOWPUSHINPUTREF. These Opcodes help enforce rules and ensure the authenticity of transactions.

Can the induction proof system be used for smart contracts?

Yes, the induction proof system can be used to create and validate smart contracts on the Radiant blockchain. By ensuring the authenticity and traceability of transaction outputs, it supports the creation of complex contracts and digital assets.

How does the induction proof system enhance security?

The induction proof system enhances security by creating a chain of trust for each transaction. This makes it nearly impossible to forge or duplicate assets, as each transaction must be verified against its predecessors.

What are some practical applications of the induction proof system?

Practical applications of the induction proof system include digital asset management, creation of fungible and non-fungible tokens (NFTs), and implementation of complex smart contracts. It ensures the authenticity and traceability of these assets, making them secure and reliable.

How does Radiant compare to other blockchains' transaction validation?

The Radiant blockchain's use of the induction proof system and compressed transaction hashes allows for efficient and secure transaction validation. This provides a significant advantage over other blockchains that may rely on trusted third parties or less efficient validation methods.

FAQs

What is Radiant?

Radiant is a peer-to-peer programmable digital asset system, derived from a fork of the Bitcoin Cash (BCH) genesis block. It introduces new Opcodes that enable Turing completeness and mathematical induction proofs, effectively addressing the "back to genesis" problem. This innovative design combines the performance and parallelism advantages of an Unspent Transaction Output (UTXO) blockchain with the contract capabilities found in Ethereum-based blockchains, specifically the Ethereum Virtual Machine (EVM).

  • Powered by Proof of Work (PoW)

  • Supports Smart Contracts

  • Launched fairly

  • Utilizes a robust hashing algorithm, SHA512256d

  • Implements the ASERT DAA (Difficulty Adjustment Algorithm)

  • Genesis Date: June 20, 2022, 02:42:50 GMT+0000

  • Block Time: 300 seconds (5 minutes)

  • Halving: Every 2 years (every 210,000 blocks)

  • Subsidy Emission: 50,000 per block (Now 25,000 per block)

  • Total Coins: 21 billion, divisible into 8 decimal places (100 million photons per RXD)

What makes Radiant stand out from other blockchain platforms?

Radiant stands out for several reasons:

  • Induction Proofs that solve the "back-to-genesis" problem without the need for indexers.

  • Account Emulation, enabling Ethereum Virtual Machine (EVM)-like applications.

  • A Turing-complete scripting language known as RadiantScript.

  • The "0-conf" feature for instant transactions.

  • A Split Node System designed for scalability.

How was Radiant fairly launched?

Radiant was launched as a 100% PoW blockchain, ready for use, without any involvement of investors, ICOs, premining, or coin allocations. Mining guides and instructions, including how to mine with rented hash power, were publicly released by Atoshi, Radiant's developer, ensuring that everyone had equal access to RXD coins from day one.

What is Radiant ethos? Is Radiant considered an investment?

Radiant aims to be a scalable and flexible blockchain technology, not primarily designed for investment purposes or generating profits from RXD coin holdings. Similar to Bitcoin, which was created as a peer-to-peer electronic cash system, Radiant was designed as a peer-to-peer programmable digital asset system. While you can still acquire RXD coins from exchanges, it's essential to understand that there are no guarantees of further development. Always perform your research (DYOR) and exercise caution when considering any investment.

Why is it a fork from BCH?

BTC civil wars divided blockchain enthusiasts into small-block and big-block proponents. In BTC, small-blocks won and big-block proponents forked it to create BCH. BCH still ended deviating from what BTC was supposed to be based on what is stated in the Bitcoin Whitepaper.

Another fork (BSV) was made, aiming to make it as close as possible to the original BTC proposed by Satoshi Nakamoto. This version of Bitcoin is still far from perfect as it has to rely on third party indexers to function properly. Additionally, its associations with an individual who claims to be Satoshi Nakamoto who has initiated legal actions against some blockchain projects, raised concerns about potential legal challenges.

This made BCH a more favorable choice for Radiant's development.

What’s special about SHA512256d?

SHA512256d is a hashing algorithm that applies SHA-512 and truncates its 512-bit output to 256 bits. While SHA-256 is already secure, Radiant selected SHA512256d to provide a robust, battle-tested foundation for a blockchain designed for ASIC compatibility. By leveraging SHA-512's efficient 64-bit operations and widespread hardware support, SHA512256d optimizes performance on 64-bit processors, enhancing ASIC development and integration. This approach delivers efficient mining and scalable blockchain validation, outperforming more computationally demanding algorithms.

When will ASICs for Radiant mining be available?

Radiant was specifically designed to transition from GPU mining to ASIC mining for enhanced scalability and efficiency. The chosen SHA512/256d algorithm simplifies ASIC manufacturing.

Why is the block time 5 minutes?

A 5-minute block time offers faster transaction confirmations compared to Bitcoin (BTC) while maintaining stability and security by minimizing orphan blocks. With block sizes of up to 256MB and the potential for even larger blocks, selecting the right block time is essential. In Radiant, the block time, thanks to its features, does not directly limit transactions per second or transaction speed.

What is Radiant's total supply and emission schedule?

Radiant has a total maximum supply of 21 billion RXD, with a Proof of Work emission schedule emitting block rewards to miners every 5 minutes (300 seconds), with block rewards halving every 2 years.

Beginning with its Genesis Block on the 20th of June 2022, Radiant's initial block reward was 50,000 RXD per block, and Radiant's first halving event occurred on the 19th April 2024, halving to 25,000 RXD.

The next halving event for Radiant will occur around the middle of April 2026, reducing the block rewards to 12,500 RXD per block.

Year
Halving
Block Reward
Block Height
Circulating Supply

2022

Genesis

50,000 RXD

0

0

2024

1st

25,000 RXD

210,000

10,500,000,000 RXD

2026

2nd

12,500 RXD

420,000

15,750,000,000 RXD

2028

3rd

6,250 RXD

630,000

18,375,000,000 RXD

2030

4th

3,125 RXD

840,000

19,687,500,000 RXD

2032

5th

1,552.5 RXD

1,050,000

20,343,750,000 RXD

2034

6th

781.25 RXD

1,260,000

20,671,875,000 RXD

2036

7th

390.625 RXD

1,470,000

20,835,937,500 RXD

2038

8th

195.3125 RXD

1,680,000

20,917,968,750 RXD

Radiant will effectively have 0 block rewards within the year 2108, when the block reward falls below 1 photon (1/100,000,000 RXD) after the 43rd halving.

How many transactions per second can Radiant achieve?

With the current block size of 256MB, Radiant is capable of doing 4,000 TPS (Transaction Per Second). If needed, miners can optimize in the future to scale linearly. The Radiant Blockchain is designed to scale towards 500,000+ TPS.

What are the benefits of Radiant's tokenomics?

Radiant's tokenomics, with a 21 billion coin supply and 8 decimal places (100 million photons per RXD coin), positions it well for tokenizing digital and real-world assets while ensuring ample availability of RXD for both micro and macro transactions. The block time, block reward, and bi-yearly halvings serve as incentives attractive to miners and investors alike.

What is the Back-To-Genesis problem?

In blockchain terms, the "back-to-genesis" problem refers to the ability to validate that all coins, transactions, and digital assets can be traced back to the genesis block. Radiant successfully solves this problem, enabling it to operate as a truly decentralized and permissionless digital asset system without relying on external third-party indexers.

What does Turing Complete mean and why have it?

Turing completeness, in the context of a programming language, signifies that there are no limits to the types of computations that can be performed. Radiant's Turing completeness allows for highly flexible smart contract capabilities similar to those of Ethereum but without the scalability issues and high gas costs. This feature makes Radiant a unique and groundbreaking blockchain, capable of supporting a wide range of cryptocurrency and blockchain technology use cases.

How is Radiant protected against attacks that harness turing completeness if it doesn't have a gas system like ETH?

Radiant, while Turing complete, does not incorporate loops. Loops must be unrolled to the maximum number of iterations required. This design simplifies and secures script execution, allowing fees to be calculated per byte. This discourages malicious actors from creating overly complex or inefficient scripts, as they would incur higher fees.

What are 0conf transactions and are they safe?

A 0-conf transaction is one broadcasted to the blockchain but not yet included in a block (i.e., 5 minutes have not passed). These transactions are useful and safe for microtransactions. Thanks to induction proofs, recipients of 0-conf transactions can be confident that the sender indeed owns the funds being transferred. However, there is a minor risk of double-spending in such transactions.

For small transactions, the potential reward for attempting a double-spending attack is typically much lower than the cost of acquiring mining hardware, electricity, and competing with the network's hash rate. In practice, it's not economically rational for an attacker to invest significant resources in double-spending small transactions.

For larger transactions, it's advisable to wait for inclusion in a block, which provides increasing security with each additional block confirmation.

Does Radiant have Replace-By-Fee (RBF)?

Radiant does not have RBF implemented. Radiant's decision to exclude Replace-by-Fee is a strategic choice that enhances transaction security, reduces double spend risks, and simplifies the user experience.

By prioritizing transaction finality and promoting responsible fee management, Radiant fosters a more secure and reliable blockchain environment.

This approach aligns with its commitment to maintaining a robust, miner-driven network that users can trust for fast, low cost, and dependable transactions.

The Benefits of Not Having Replace-by-Fee (RBF) in Radiant, RXD

In the world of blockchains, transaction security and network reliability are paramount. One feature that often sparks debate is Replace-by-Fee (RBF) vs, First In First Out (FIFO). While some blockchains implement RBF to allow users to replace a pending transaction with a new one that includes a higher fee, Radiant has opted not to include this feature.

Here's why this decision benefits the Radiant network and its users:

1. Enhanced Transaction Finality

By not supporting RBF, Radiant ensures a higher degree of transaction finality once a transaction is broadcast to the network. This means that once a transaction is sent, it's committed to the network's mempool, reducing the risk of the transaction being replaced or tampered with. This provides more confidence to recipients, particularly in situations where quick transaction acknowledgment is essential.

2. Reduced Double Spend Risks

RBF can be exploited for double spend attacks, especially in low-confirmation transactions. For instance, a user could broadcast a low-fee transaction to one party, then replace it with a higher-fee transaction to a different party, attempting to defraud the first recipient. To mitigate these risks, many businesses and users wait for several confirmations before considering a transaction final, especially for larger amounts. Radiant's decision to exclude RBF minimizes this risk, making it safer for merchants and users to accept zero or low-confirmation transactions without the fear of an attack.

3. Simplicity and Predictability

Excluding RBF simplifies the transaction process for users and developers. With RBF, users have to be cautious about transaction replaceability, potentially leading to confusion or errors. Radiant's straightforward approach means that once a transaction is sent, users can have confidence that it won't be replaced, leading to a more predictable transaction experience.

4. Encouraging Responsible Fee Management

Without RBF, users are encouraged to consider transaction fees carefully before broadcasting. This helps prevent network congestion caused by users continually adjusting fees to prioritize their transactions. It fosters a more balanced network usage where users evaluate the urgency of their transactions in relation to current network conditions, promoting a healthier fee market. Miners can still choose to process higher fee transactions at will, but the volume and speed of putting more transactions in a block is more valuable and hence important in a scalable network.

5. Aligning with Proof of Work Security

Radiant's Proof of Work consensus mechanism relies on the security provided by miners who validate and add transactions to the blockchain. By not having RBF, Radiant reinforces the trust in its PoW system, ensuring that transactions are treated as irreversible once included in a block. This aligns with the core principles of PoW, where the longest chain and the work behind it determine the network's state.

6. Back to Satoshi's original ideals

Bitcoin originally operated on a First-In-First-Out (FIFO) basis for transaction processing within the mempool. This means that transactions were typically processed in the order they were received by the network, regardless of the transaction fee. Miners would include the earliest transactions in the next block, provided they met the standard criteria for validity and size. Networks like Radiant that focus on true scalability by having adequate block space and faster block production will not suffer the same fate as size constrained and artificially limited networks. So the need for something like RBF is negated.

How does the Split Node System work?

Radiant employs a Split Node System to accommodate its big-block design and scalability needs. Nodes in Radiant can operate in three different ways:

  • Mining Nodes: Lightweight, non-resource-intensive nodes dedicated to mining.

  • Agent Nodes: Contain essential information required for specific decentralized applications (dApps) and smart contracts.

  • Archival Nodes: Store the complete blockchain for comprehensive data access.

Who created Radiant?

Radiant was bootstrapped and introduced to the world by an individual or group of individuals using the pseudonym "Atoshi." Similar to the anonymous creation of Bitcoin by Satoshi Nakamoto, Atoshi delivered a fully functional product without making any promises or guarantees, allowing the community to shape its future.

How to mine Radiant?

How to run a Radiant node?

Check the Radiant node deployment guides

Is there any reward for running a node?

No, Radiant is a 100% PoW Blockchain. The only way to get RXD is through mining.

What wallets are compatible with Radiant?

You can access your RXD through:

Software wallets

You can access your RXD through:

Software

Hardware

What developer tooling is available on Radiant?

The current toolbox available to help developers build on Radiant is:

Links

Radiant Blockchain Links and Resources

Principal

Code

Faucets

Explorers

Mainnet Explorers


Testnet Explorers

Glyph Explorers

Communication

Organisations

Wallets

Exchanges

Market Info

Miner Resources

Token Mining

Pools - Mainnet

Pools - Testnet

Mining Ware

Genesis Hash:

Radiant now has ASIC miners publically available on the market as of September 5th 2024. The first manufacturer to announce a Radiant compatible SHA512/256d ASIC miner was DragonBall Miner, with the .

ICERIVER was the second manufacturer to release an ASIC miner for Radiant, with the .

A step-by-step guide on .

Radiant's initial GPU mining guide when it was first launched can be found on .

Open source desktop wallet based off the popular BTC wallet upgraded for RXD.

Mobile and desktop wallet compatible with 0-conf transactions.

A multi platform wallet developed by Radiant community developers, compatible with digital asset minting (Glyph Protocol assets).

Install via the Chrome Web Store.

Slim as a bank card, secure as a bank vault.

Radiant JavaScript Library.

Rust/WASM Library to interact with Radiant.

Fork of CashScript with support for Radiant Opcodes to create complex smart contracts.

Create Fungible Tokens, Non-Fungible Tokens, or Smart Contracts by utilizing digital signatures on Photons, the sub-units of Radiant's coin, RXD.

A non-custodial wallet for minting and transferring tokens, following the Glyphs Protocol. The code runs completely client side and only requires a connection to an ElectrumX server.

Utilize the Photonic Wallet extension available at the Chrome Web Store.

A powerful and easy-to-integrate solution for merchants looking to accept payments on the Radiant (RXD) blockchain.

Easy to follow guide on how to setup and run a Radiant Node and ElectrumX Server.

Learn the technical details on how the Glyphs Protocol works on Radiant.

Website:

Whitepaper:

System Design:

GitHub Community - Active:

GitHub Initial - Archived:

Radiant Node:

RadiantJS:

RXD.WASM/RXD-RS:

RadiantScript:

Muon Pay SDK:

Docker for Radiant Node:

Glyph Miner:

Photonic Wallet:

Radiant ElectrumX:

Faucet - Mainnet:

Faucet - Testnet:

Faucet Games - Mainnet:

Explorer 1:

Explorer 2:

Explorer 3:

Explorer 4:

Explorer 5:

Top 100 RXD Wallets:

Mempool Summary:

Transaction Stats:

Block Stats:

Explorer:

Explorer:

Discord - Main:

Discord - RXDDapps:

Telegram - Main:

Telegram - Traders:

Medium - Community:

Medium - Radiant Foundation:

Medium - Initial:

Reddit - Main:

Reddit - Initial:

X - Community:

X - Planet_Radiant:

X - RadiantFoundation:

X - Initial:

Bitcointalk:

RXDDapps:

Radiant Foundation:

Electron Wallet 0.1.4 - Win / Linux / Mac:

ChainBow Wallet 2.1.1 - iOS

ChainBow Wallet 2.1.1 - Android

Tangem Hardware Wallet - Mobile with NFC:

Photonic Wallet - Web App:

Photonic Wallet - Extension:

Photonic Wallet - Source:

CoinEx RXD/USDT:

CoinEx RXD/BTC:

MEXC RXD/USDT:

TradeOgre RXD/USDT:

TradeOgre RXD/BTC:

NonKYC RXD/USDT:

XT RXD/USDT:

CoinMarketCap:

CoinGecko:

CoinPaprika:

LiveCoinWatch:

CoinCodex:

CryptoRank:

RXD/USDT Chart:

CryptoCompare:

CoinCarp:

Glyph Miner:

Photonic Wallet:

Server-Status:

MiningPoolStats Rank:

HumPool:

Vipor:

Rplant Pool:

BlockMinerz:

Kryptex:

WoolyPooly:

RXD SoloPool:

ZergPool:

TW-Pool:

HashPool:

9kpool:

WhalePool:

Satopool:

ICERIVER RXD RX0 ASIC:

DragonBall A11 Dual ASIC:

ccminer+hiveos (NVIDIA):

rad-bfgminer (AMD/NVIDIA):

srbminer(AMD):

bzminer:

Rigel (NVIDIA):

wildrig:

MiningPoolStats:

Minerstats:

Hashrateno:

Whattomine:

0000000065d8ed5d8be28d6876b3ffb660ac2a6c0ca59e437e1f7a6f4e003fb4
A11 Dual ASIC Miner
ICERIVER RXD RX0
how to mine RXD with an ASIC miner
GitHub
Compile Node
Run Node with Docker
Run Node on Flux
Electron Radiant Wallet:
ChainBow Wallet:
Photonic Wallet:
Photonic Wallet - Extension:
Tangem Wallet:
RadiantJS:
RXD.WASM/RXD-RS:
RadiantScript:
Glyph Protocol:
Photonic Wallet:
Photonic Wallet - Extension:
Muon Pay SDK:
Radiant Server Setup Guide:
Glyphs Protocol Tech Guide:
https://radiantblockchain.org
https://radiantblockchain.org/radiant.pdf
https://github.com/RadiantBlockchain/radiant-node/blob/master/doc/whitepaper/radiant-system-design.md
https://github.com/RadiantBlockchain-Community
https://github.com/RadiantBlockchain
https://github.com/RadiantBlockchain-Community/radiant-node
https://github.com/chainbow/radiantjs
https://github.com/RadiantBlockchain-Community/rxd-wasm
https://github.com/RadiantBlockchain-Community/radiantscript
https://github.com/RadiantRanger42/MuonPaySDK
https://hub.docker.com/u/radiantcommunity
https://github.com/RadiantBlockchain-Community/glyph-miner
https://github.com/RadiantBlockchain-Community/photonic-wallet
https://github.com/RadiantBlockchain-Community/electrumx
https://faucet.radiant4people.com
https://faucet-testnet.radiant4people.com
https://faucetgames.rxddapps.com/?ref=104x
https://radiantexplorer.com
https://explorer.radiant.ovh
https://explorer.radiantblockchain.org
https://radiantscan.io
https://blockbook.radiant4people.com
https://radiantexplorer.com/richlist
https://explorer.radiantblockchain.org/mempool-summary
https://explorer.radiantblockchain.org/tx-stats
https://explorer.radiantblockchain.org/block-stats
https://explorer-testnet.radiantblockchain.org
https://glyph.blockminerz.com
https://discord.gg/radiantblockchain
https://discord.gg/BccnsD3Gsh
https://t.me/RadiantBlockchain
https://t.me/RadiantBlockchainTraders
https://radiant-community.medium.com
https://radiantfoundation.medium.com
https://medium.com/@RadiantLayerOne
https://www.reddit.com/r/Radiant_RXD
https://www.reddit.com/r/RadiantBlockchain
https://twitter.com/RXD_Community
https://x.com/Planet_Radiant
https://x.com/RXDFoundation
https://x.com/RadiantLayerOne
https://bitcointalk.org/index.php?topic=5436028;all
https://rxddapps.com
https://radiantfoundation.org
https://github.com/RadiantBlockchain/electron-radiant/releases/tag/v0.1.4
https://apps.apple.com/us/app/chainbow-wallet/id1568850934
https://play.google.com/store/apps/details?id=io.chainbow.bsvwallet
https://tangem.com/en/pricing/?promocode=RADIANT
https://photonic.radiant4people.com
https://chromewebstore.google.com/detail/photonic-wallet/ldagidbelgfoonfhbggfgmlhaccfeepb?pli=1
https://github.com/RadiantBlockchain-Community/photonic-wallet
https://www.coinex.com/en/exchange/rxd-usdt
https://www.coinex.com/en/exchange/rxd-btc
https://www.mexc.com/exchange/RXD_USDT
https://tradeogre.com/exchange/RXD-USDT
https://tradeogre.com/exchange/RXD-BTC
https://nonkyc.io/market/RXD_USDT
https://www.xt.com/en/trade/rxd_usdt
https://coinmarketcap.com/currencies/radiant
https://www.coingecko.com/en/coins/radiant
https://coinpaprika.com/coin/rxd-radiant
https://www.livecoinwatch.com/price/Radiant-RXD
https://coincodex.com/crypto/radiant
https://cryptorank.io/price/radiant
https://coinpaprika.com/trading-view/rxd-radiant
https://www.cryptocompare.com/coins/rxd/analysis/USDT
https://www.coincarp.com/currencies/radiant/price/
https://glyph.radiant4people.com
https://photonic.radiant4people.com
https://radiantblockchain-community.github.io/electrumx-check
https://miningpoolstats.stream/radiant
https://humpool.com
https://vipor.net/mine/rxd_pplns_1
https://pool.rplant.xyz/#radiant
https://blockminerz.com/en/pools/Radiant
https://pool.kryptex.com/rxd
https://woolypooly.com/en/coin/rxd
https://rxd.solopool.org
https://zergpool.com
https://tw-pool.com
https://hashpool.com
https://9kpool.com/#RadiantPool
https://www.whalepool.com/coin/RXD
https://satopool.com/#rxd-testnet
https://www.iceriver.io/product/iceriver-rxd-rx0
https://dragonballminer.com/en/product/dragonball-miner-a11/
https://github.com/radifier/radiator/releases
https://github.com/RadiantBlockchain/rad-bfgminer/releases
https://github.com/doktor83/SRBMiner-Multi/releases
https://www.bzminer.com
https://github.com/rigelminer/rigel/releases/
https://github.com/andru-kun/wildrig-multi/releases
https://miningpoolstats.stream/radiant
https://minerstat.com/coin/RXD
https://hashrate.no
https://whattomine.com

Radiant: Sistem Kasugihan Aset Digital Peer-to-Peer

Javanese version of the Radiant whitepaper

Kabeh Pangembang Radiant

11 Agustus, 2022

radiantblockchain.org

Abstrak. Radiant Network punika sistem aset digital peer-to-peer bisa ijol-ijolan tandha kanthi cara langsung tanpa liwat pesta tengah. Protokol asline Bitcoin [1] nyedhiyakake apa sing dibutuhake Kanggo gawe sistem kas elektronik peer-to-peer, nanging Ora duweke kabisan Kanggo ngecek sajarah transaksi lan mulane iku, ora saget digunakake Kanggo validasi aset digitale. Tandha digital lan wates output menehi bagean saka solusi, nanging entuk manfaat utama wis ilang Yen pesta sing dipercaya _ Isih dibutuhake Kanggo validasi aset digital . Serupa karo Bitcoin, jaringan Radiant mbutuhake struktur minimal , lan cathetan wektu transaksi kanggo ing rante bukti Kerja adhedhasar hashes medium lumaku . We introduce kalih teknik Kanggo validasi aset digital : referensi unik lan sistem bukti induksi objektif sing umum loro-lorone operate ing papan lan wektu O( 1) konstanta . Iku bisa Kanggo nyipta output karo cara apa wae , tanpa kurban ciri paralelisme inheren lan kinerja _ saka arsitektur adhedhasar output transaksi _ Ora digunakake (UTXO). amarga_ _ iku , pangguna saget metu lan melu bali karo Jaringan sumringah ing bakal ati lan yakin bakal integritas lan keasliane aset digitale .

1. Pambuka

Dagang karo blockchain, utawa teknologi ledger digital (DLT), ing akeh kasus gumantung marang sing ngetokake sekuritas lan kustodian sing dadi pihak sing dipercaya kanggo otentikasi aset digital. Nalika sistem kasebut bisa digunakake kanthi becik kanggo transaksi kayata pembayaran elektronik, dheweke isih nandhang kelemahane. -model adhedhasar kanggo majeng nggunakake. Mesin Virtual Ethereum (EVM) berbasis Blockchain [2] fleksibel banget kanggo kabeh jinis program, nanging biaya sing dhuwur nggawe aplikasi micropayment ora praktis. Sing dibutuhake yaiku sistem pembayaran elektronik sing bisa digunakake kanggo biaya sing murah, dhuwur- kinerja sistem manajemen aset digital, lan kemampuan pemrograman tingkat nglakokaké. Ing makalah iki, kita ngusulake solusi kanggo masalah skala lan kontrak blokchain nggunakake rong teknik anyar sing nyedhiyakake referensi unik lan sistem bukti induksi umum, sing ndadekake program Turing Complete [3] ngliwati wates transaksi bisa. Sistem sing diusulake didesentralisasi, nggunakake mekanisme konsensus bukti-kerja kaya Bitcoin, nanging kanthi tingkat throughput sing luwih dhuwur, nalika nyedhiyakake keluwesan sing padha karo blockchain berbasis EVM, kanthi biaya sing murah banget.

2. Transaksi

Kaya Bitcoin, kita nemtokake koin elektronik minangka seri tandha asta digital. Yen transaksi ing Radiant beda-beda yaiku saben pemilik nransfer dhuwit recehan menyang sabanjure kanthi menehi tandha digital hash saka transaksi sadurunge saliyane parameter input sing dibutuhake kanggo mbukak kunci koin kasebut. Transaksi uga nggawe watesan ngunci output anyar, sing bisa uga kalebu kunci umum saka pemilik sabanjure, ing antarane aturan sing ditemtokake pangguna liyane.

Diagram 1. Transaksi Radiant.

Kanggo verifikasi manawa mbuwang kaping pindho ora kedadeyan, kita nggunakake server timestamp sing disebarake, nggunakake sistem bukti kerja adhedhasar hash kanggo nyetel riwayat kanonik kanggo nemtokake transaksi sing teka dhisik. Transaksi diatur dadi blok. Miturut konvensi, transaksi pisanan, sing diarani "transaksi berbasis koin", ing blok minangka transaksi khusus sing miwiti koin anyar sing diduweni dening panyipta blok kasebut. Blok dirantai bebarengan lan ngatur transaksi dadi Merkle Tree [4]. Kabeh transaksi, kajaba sing pisanan, kudu ngrujuk marang transaksi sadurunge sing mbentuk grafik asiklik terarah (DAG) ing ngendi kabeh dhuwit recehan pungkasane nyambung menyang paling ora siji transaksi tartamtu ing wiwitan blok.

Diagram 2. Struktur Blok; transaksi diaturke menyang Wit Merkle.

Masalah karo desain iki, ing konteks aset digital, mung ana siji jinis koin, utawa aset digital, lan ora ana konsep koin sing ditemtokake pangguna (utawa jinis aset digital). Desain bisa uga cukup kanggo transaksi kaya pembayaran elektronik ing unit akun nyata, nanging ora langsung cocok kanggo nggunakake liyane jinis dhuwit recehan utawa aset digital. Solusi umum yaiku ngenalake layanan kayata indeksasi transaksi sing ngawasi transaksi kanggo urutan data tartamtu kanggo menehi tandha nggawe aset digital. Masalah karo solusi iki yaiku gumantung marang perusahaan sing mbukak layanan kasebut, kanthi keaslian aset digital sing kudu dipercaya, kaya layanan liyane ing web. We needed cara kanggo kedhaftar kanggo nduduhake nggawe jinis tartamtu saka duwit receh, nanging ora gumantung ing layanan dipercaya kanggo digunakake kanggo presentation data.

3. Aset Digital

Kita nemtokake koin elektronik khusus, utawa aset digital, minangka seri tandha asta digital. Aset digital minangka jinis koin sing ditemtokake pangguna nggunakake token transaksi khusus, sing diarani "transaksi basis aset", kanggo nggawe utawa nggawe aset digital. Mirip karo transaksi coinbase, kang inject dhuwit recehan anyar menyang sistem, transaksi assetbase werna utawa tandha dhuwit recehan elektronik karo pengenal 36-bait unik kanggo umur. Kostume e-coin dilapisi ing ndhuwur jinis duwit receh dhasar lan bisa digunakake kanthi cara sing padha. Transaksi basis aset bisa katon ing ngendi wae ing blok kasebut lan bisa ngetrapake aturan lan watesan khusus apa wae sing diputusake sadurunge.

Diagram 3. Transaksine kuwi makili jinis duwit receh pangguna-ditetepake — utawa digital asset

Kanggo nindakake, kita kudu nggawe pengenal unik sing stabil lan mekanisme transaksi kanggo nglacak keaslian jinis koin (aset digital). Pangguna sistem kudu duwe bukti yen jinis koin tartamtu ora palsu lan kanthi akurat nggambarake aset digital kasebut.

Diagram 4. Jinis koin khusus sing ditemtokake pangguna ditemtokake saka transaksi mint khusus. Pengenal unik digunakake kanggo nggolongake jinis koin.

4. Identifier Seng Unik

Kanggo aplikasi pengenal unik kanggo jinis duwit receh, kita nggunakake transaksi token khusus, disebut "transaksi basis aset", kang tumindak minangka wiwitan (mint) saka chain teken digital. Tinimbang mbutuhake struktur data anyar kanggo pengenal unik, kita nggunakake maneh pengenal transaksi lan indeks output, disebut "outpoint", minangka pengenal unik kanggo jinis duwit receh. Mesthekake yen outpoints (36-bait) iku acak lan global unik.

Instruksi pemrograman, sing diarani OP_PUSHINPUTREF, digunakake kanggo masang referensi menyang output. Instruksi kasebut nampa persis siji parameter 36-bait sing kudu cocog karo 1) titik metu saka salah sawijining output sing digunakake, utawa 2) nilai 36-bait sing padha wis katon ing OP_PUSHINPUTREF sing wis ditemtokake ing salah sawijining output sing digunakake. Cara mung kanggo nilai tartamtu kanggo katon ing output transaksi liwat sawetara transaksi leluhur, Nilai cocog titik metu saka transaksi basis aset minting dhisikan. Transaksi sing nemtokake nilai sing ora cocog karo kondisi apa wae ora sah.

Diagram 5. Pengenal seng unik diinisialisasi kanthi cocog karo titik metu saka salah sawijining output sing digunakake, lan banjur dijaga anggere paling ora siji output sing ditanggepi ngemot pengenal unik sing padha ing awak naskah.

Instruksi pemrograman prasaja iki nyedhiyakake pengenal unik sing bisa digunakake minangka referensi stabil kanggo nggawe aturan lanjut. Contone, macem-macem jinis dhuwit recehan, aset digital, saiki bisa gumantung ing jinis dhuwit recehan liyane. Amarga kabeh data lokal kanggo transaksi, liwat transaksi input wong tuwa langsung, gampang kanggo klien lan layanan kanggo validasi keaslian aset digital ing wektu lan papan O (1) konstan, ngindhari kabutuhan layanan sing dipercoyo.

5. Buktine Miturut Induksi

Sampeyan bisa ngasilake pengenal unik kanthi cara alternatif lan uga nyedhiyakake mekanisme kanggo bukti induksi matematika [5] nggunakake algoritma transaksi hash sing diowahi. Kanthi ngidini skrip input nampa transaksi wong tuwa sing wis dileksanakake, aturan bisa verifikasi manawa wong tuwane, lan simbah, cocog karo syarat aturan kasebut. Masalah ketok iku minangka saben salinan lengkap saka transaksi tiyang sepah ditempelake bledosan ukuran eksponensial ana lan ngalangi nggunakake praktis saka technique. Apa sing dibutuhake yaiku cara kanggo ngompres transaksi, supaya struktur data ukuran tetep bisa digunakake kanggo ngasilake hash transaksi, tinimbang mbutuhake isi transaksi lengkap.

Diagram 6. Validasi transaksi induk seng lengkap, bukti induksi matematika kanthi ngetik transaksi induk seng lengkap menyang input sing nyebabake paningkatan eksponensial ing ukuran transaksi.

Kita bisa nindakake iki kanthi ngowahi algoritma hash transaksi sing digunakake ing Bitcoin, ing ngendi intisarine sha-256 seng gandha diitung saka transaksi serial, menyang versi anyar sing pisanan ngringkes isi transaksi kanggo nurunake hash. Kita ngenalake versi 3 saka algoritma hash transaksi, kanggo mbedakake saka versi 1 lan 2 sing digunakake ing Bitcoin. Proses kasebut minangka ciri saben lapangan, utawa komponen transaksi, dadi hash perantara, sing bisa digunakake minangka input ukuran tetep lan kanthi mangkono ngindhari pertumbuhan ukuran transaksi eksponensial.

Kita nggunakake struktur data 112-bait ing ngisor iki, tinimbang bita transaksi serialized lengkap, kang siji pindho sha-256 hash kanggo pungkasanipun njaluk hash transaksi.

Pragambar Hash Kolom Transaksi Versi 3:

  1. nVersion(=3) transaksi (4 byte little endian)

  2. nTotalInputs (4 byte little endian)

  3. hashPrevoutInputs (hash 32 byte)

  4. hashSequence (hash 32 byte)

  5. nTotalOutputs (4 byte little endian)

  6. hashOutputHash (hash 32 byte)

  7. nLocktime transaksi (4 byte little endian)

Nggunakake algoritma hash transaksi kanggo versi transaksi ke 3 (ketigo), kita bisa nampilaké transaksi tiyang sepah lan simbah ing saben langkah saka bukti prabawa matématika kanggo nyegah Tambah ing ukuran transaksi, lan bisa aplikasi sembarang aturan yen perlu.

Diagram 7. Validasi transaksi induk sing dikompres, bukti induksi matematika kanthi nanem struktur data preimage hash transaksi versi 3 saka wong tuwa lan simbah kanggo ngetrapake aturan lan kendala sing sewenang-wenang

6. Jaringane

Topologi jaringan minangka grafik sing meh lengkap, ing ngendi saben Node Pertambangan disambungake karo saben Node Pertambangan liyane. Langkah-langkahe kanggo mbukak jaringane padha karo Bitcoin, kanthi sawetara beda kanggo macem-macem jinis simpul: Node Pertambangan, Node Agen, Node Arsip. Node Pertambangan minangka penerbit blok aktif lan njaga konsensus karo kabeh simpul liyane, Node Arsip iku nglayani riwayate data pemblokirane, lan Node Agen dirancang kanggo nyaring blok lan nglacak transaksi kapentingan kanggo aplikasi sing dilayani. Arsip lan Agen Node bisa beroperasi ing jaringan peer-to-peer sing padha nanging ora ngasilake blok. Node Non-Mining kayata Arsip lan Agen Node kadhangkala diarani minangka "node seng ngrungokake" kanggo mbedakake peran ing jaringane.

Diagram 8. Node Mining disambungake kanthi apik lan dibangun ing ndhuwur blok masing-masing. Node arsip nyimpen blok lengkap kanggo analisis historis lan tujuan bootstrap. Node agen minangka simpul pamireng sing nyaring lan nyimpen transaksi kanggo nglayani klien.

Node Pertambangane disambungake kanthi luwih apik menyang grafik sing meh lengkap ing antarane Node Pertambangan liyane. Tugase yaiku mbangun blok saben liyane lan njaga konsensus kanggo sawetara atus blok paling anyar, lan njaga set UTXO kanggo pencegahan belonjo kaping pindho..

Agen Node mung kudu nyimpen subset transaksine, contone jinis koin utawa aset digital seng tartamtu. Malah kanthi pamblokirane gedhe, Agen Node bisa kanthi cepet nyaring transaksine kanthi referensi tartamtu utawa pesenan byte, banjure nyimpen transaksi kasebut kanggo dilayani liwat antarmuka programmer aplikasine. Antarane agen kerja sama, Akar Wit Merkle seng muat hash bisa diumumake sacara umum kanggo transaksi ing saben blok sing cocog karo pola sing wis ditemtokake kanggo menehi sinyal menyang Agen lan konsumen liyane sing transaksi wis diproses Agen.

Node arsip digunakake kanggo nggawe salinan serep kabeh blok kanggo macem-macem aplikasi kalebu gudang data, analytics, lan pembelajaran mesin. Amarga Node Arsip ora langsung melu pertambangan, dheweke ora duwe kinerja wektu nyata lan syarat bandwidth sing padha karo Node Pertambangan utawa Agen.

7. Kalkulasine

Kita nimbang skenario ing ngendi jaringan Radiant terus berkembang lan apa sing dibutuhake kanggo syarate pangolahan Node Pertambangan, Arsip lan Agen. Ing comparison, ing wektu nulis, ana kira-kira 83 yuta output transaksi unspent kanggo pamblokiran Bitcoin, total watara 6 GB data dibutuhake kanggo nyegah pindho mbuwang. Mung mbutuhake sawetara atus blok paling anyar disimpen dening Mining Nodes, kanthi blok lawas sing kasedhiya saka Arsip Nodes. Kanggo Agen Nodes, penting kanggo njaga partisi sing cocog kanggo output transaksi sing ora digunakake sing ana gandhengane karo aplikasi sing dilayani, skala minangka fungsi bandwidth lan dudu syarat panyimpenane.

Kanggo tujuan kita, kita nganggep bakal ana blok 3GB saben 5 menit sing bakal diwenehi tandha wektu lan disebarake ing jaringan, utawa kira-kira 20.000 transaksi per detik kanthi ukuran transaksi rata-rata 500 bita, utawa kira-kira 6.000.000 transaksi saben blok. Kita nuduhake manawa kanggo saben jinis simpul, jaringan bisa cukup kanggo nyukupi kabutuhan global. Iki padha karo kira-kira 1 transaksi saben 5 dina kanggo saben 8 milyar wong ing planet iki.

Node Pertambangane

Node Mining minangka siji-sijine jinis simpul sing dibangun ing ndhuwur blok masing-masing. Kanggo njaga konsensus, cukup kanggo nyinkronake blumbang output transaksi sing ora digunakake (UTXO), lan nahan mung atusan blok pungkasan. Ing wektu nulis, kinerja dhuwur komoditas solid-state drive saged liwat 120.000 IOPS, biaya watara $500 USD kanggo 280 GB, lan mulane bisa nangani bab 20.000 transaksi per detik (assuming saben transaksi duwe 2 input lan 2 output). Ana 2 maca kanggo input, 2 nganyari kanggo input, lan 2 nulis kanggo output anyar: 120.000/6 = 20.000 transaksi/detik.

Node arsipe

Node arsip nyedhiyakake data blok historis lan cocok kanggo aplikasi machine learning, analytics, lan data warehousing. Arsip Node bisa nglengkapi Bootstrap Mining Nodes lan kanggo mbukak mriksa konsistensi periodik ing set UTXO. Nalika nulis, drive komoditas 18 TB kasedhiya sekitar $350 USD. Yen 3 GB data saben 5 menit padha karo 732 GB kabutuhan panyimpenan data saben dina, utawa watara 22 TB saben sasi. Biaya hardware kanggo setahun yaiku 15 drive, kanthi kapasitas 18 TB, kanthi biaya tambahan $5,000 USD.

Node Agen

Agent Nodes skala paling gampang saka jinis simpul amarga mung ngolah jinis transaksi sing cocog kanggo aplikasi sing dilayani. Akibaté, Agen Nodes bisa saka server web nganti piranti IoT sing entheng kanthi kemampuan pangolahan lan panyimpenan winates, nalika tetep sepi ing blok 3GB, utawa 20.000 transaksi per detik. Contone, perusahaan bisa uga pengin nglacak panggunaan poin kesetiaan, digawe minangka aset digital, lan mulane mung kudu milih sawetara nganyari transaksi saka saben blok sing cocog karo pengenal unik kanggo jinis koin kasebut.

Nalika nulis, piranti komputasi komersial, Raspberry Pi 4, didol udakara $ 275. Nduweni prosesor quadcore 1,5 GHz lan 4 GB RAM, sing ngidini kanthi cepet nyaring, lan mbuwang transaksi sing ora relevan, kanthi tingkat saka 5.000 transaksi saben inti. Mesthine, iki mung minangka conto babagan carane ngolah blok gedhe, ing aplikasi web sing khas bisa uga ana luwih akeh inti sing kasedhiya.

Kacepetan bandwidth rata-rata ing 25 negara paling dhuwur ngluwihi 100 MBPS, utawa udakara 10 MB/s download, kanthi akeh panyedhiya layanan internet sing nawakake undhuhan tanpa wates. Kebutuhan bandwidth kanggo blok 3GB saben 5 menit sekitar 10MB/s kanthi total 22TB saben wulan. Hierarki Agen Node uga bisa digawe kanggo nyaring total bandwidth syarat kanggo Agen Node kanthi kapasitas bandwidth sing luwih murah.

8. Kesimpulane

Kita wis ngusulake sistem manajemen aset digital tanpa ngandelake kepercayaan. Kita wes miwiti karo blok bangunan dhasar saka koin sing digawe saka tandha digital, sing nyedhiyakake kontrol kepemilikan sing kuwat. Saka aturan lan insentif sing dibutuhake, kita ngenalake rong cara anyar kanggo keasliane lan nglacak aset digital ing wektu lan papan O (1) konstan. Kaloro cara kasebut kanthi mandiri nyedhiyakake sistem bukti induksi matematika umum sing bisa ngode konfigurasi aset digital. Sistem Turing Lengkap ing lan ngliwati wates transaksi, tanpa perlu lapisan sekunder. Radiant minangka desain terobosan sing nyedhiyakake keuntungan kinerja lan paralelisme saka pamblokiran output transaksi sing ora digunakake (UTXO), nanging kanthi kemampuan kontrak blokade adhedhasar akun adhedhasar Ethereum Virtual Machine (EVM).

Referensine

[1] Satoshi Nakamoto, URL "Bitcoin: Sistem Uang Elektronik Peer-to-Peer" https://bitcoin.org/bitcoin.pdf, 2009.

[2] Vitalik Buterin, "Ethereum: Kontrak Cerdas Generasi Berikutnya dan Platform Aplikasi Terdesentralisasi." URL https://ethereum.org/en/whitepaper/, 2014.

[3] Kontributor Wikipedia. "Turing kelengkapan." Wikipedia, ensiklopedia gratis. Wikipedia, Ensiklopedia Gratis, URL https://en.wikipedia.org/wiki/Turing_completeness, 21 Juli 2022

[4] RC Merkle, "Protokol untuk kriptosistem kunci publik," Dalam Proc. 1980 Symposium on Security and Privacy, IEEE Computer Society, halaman 122-133, April 1980.

[5] Britannica, T. Editor Ensiklopedia. "induksi matematika." Ensiklopedia Britannica, URL https://www.britannica.com/science/mathematical-induction, 2018

Pay to Ref

Pay to ref is a powerful new script built using Radiant's unique induction proof system. This script provides a way of composing contracts from multiple outputs, introducing efficiencies and flexibility previously very difficult on UTXO based blockchains.

<REF> OP_REFTYPE_UTXO OP_0 OP_NUMNOTEQUAL

The above script uses OP_REFTYPE_UTXO to take a given ref from the stack and return the type of any ref found in the transaction's inputs. If the returned value is not equal to zero then we know the ref has been found in the inputs. We can check for any ref type or a specific type.

OP_REFTYPE_UTXO returns one of the following:

  • 0 - Ref not found in inputs

  • 1 - Ref found in inputs, of normal type

  • 2 - Ref found in inputs, of singleton type

Composing contracts from multiple UTXOs

This script can be used to break a contract up into multiple parts. For example a complex NFT contract, split into a token contract and ownership contract:

UTXO for contract functions

// Contract functions
// function 1...
// function 2...
OP_PUSHINPUTREFSINGLETON <ref1> OP_DROP
<ref2> OP_REFTYPE_UTXO OP_2 OP_NUMEQUAL

UTXO for token ownership/control

OP_PUSHINPUTREFSINGLETON <ref2> OP_DROP
OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

The above contract is composed of two outputs. A singleton ref has been assigned to each output. The first UTXO can only be spent if the second "control" UTXO is also an input.

We can now handle token transfers using only the P2PKH output and not have to touch the rest of the contract. Since the token contract doesn't contain the P2PKH script, only a pay to ref, it is automatically carried forward with the transfer. For wallets to support transfers of this token, they only need to handle a simple singleton + P2PKH script and not be concerned with custom functionality or parsing custom contract templates.

DIAGRAM

The two singletons are best created as a contiguous set, so if any script needs to read from another part of the contract, it can check for the existence of a ref within its own set to verify the authenticity of the input. This also makes it easy for wallets to find all parts of the contract.

In this example we have only used two outputs, but this can be extended to compose contracts from three or more outputs.

Upgrading contracts

Another powerful use case is to extend a contract's functionality by permitting the UTXO to be spent alongside one of a range of refs. These contract extensions can be defined at a later date, providing a way of upgrading contracts or adding new functionality.

The developer must plan this in advance, by keeping some refs in reserve, coded into the contract so they can be deployed and distributed to users later.

For example, a contract that allows locking conditions to be delegated to an input containing any ref from a specific transaction hash. The ref index is provided as a parameter in the unlocking script:

<extensionTxId> OP_SWAP OP_CAT OP_REFTYPE_UTXO OP_0 OP_NUMNOTEQUAL

The above script takes a ref index from the unlocking script, appends to the transaction ID to construct the ref and checks if the ref exists in an input.

Care must be taken to ensure the user remains in control of their coins and complete control is not given over to the developer. Requiring the owner's signature will ensure the developer cannot spend the coins.

Radiant Blockchain System Design

The Radiant Developers

August 11, 2022

radiantblockchain.org

Abstract

The Radiant network is a peer-to-peer digital asset system that enables direct exchange of value without going through a central party. The original Bitcoin[1] protocol provides what is needed to create a peer-to-peer electronic cash system, but lacks the ability to verify transaction histories and therefore cannot be used to validate digital assets. Digital signatures and output constraints provide part of the solution, but the main benefits are lost if a trusted third party is still required to validate digital assets. The Radiant network itself requires minimal structure, and operates similiarly to the Bitcoin network in timestamping transactions into an ongoing hash-based chain of proof-of-work. We introduce two techniques to validate digital assets using a general purpose induction proof system that operates in constant O(1) time and space. The induction proof system makes it possible to efficiently compose outputs in any manner, without compromising the inherent parallelism and scalability characteristics of the UTXO based architecture. Users can leave and rejoin the network at will and be assured of the integrity and authenticity of their digital assets.

Introduction

Commerce with blockchains and digital ledgers has come to rely on issuers and custodians serving as trusted third parties (sometimes referred to as "bridges", "oracles", "secondary layers") to authenticate digital assets and process electronic payments. While the system works well enough for electronic payment-like transactions, it still suffers from the inherent weaknesses of the trust based model for more advanced usages of the blockchain. The high costs of transactions associated with Ethereum Virtual Machines (EVM) based blockchains is due to the limited block space and the inherent limitations of the account based model of processing.

What is needed is an electronic payment system that can also act as a digital asset management system with the performance characterstics of an unspent transaction output (UTXO) blockchain architecture, with the flexibility of an account based blockchain. In this paper, we propose a solution to the problem of blockchain scaling using two novel methods which, independently, provide a general induction proof system capable of authenticating digital assets, emulating account based blockchains, while maintaining the performance characteristics of a UTXO based blockchain such as unbounded scale and parallelism.

The original Bitcoin[1] protocol provides what is needed to create a peer-to-peer digital asset system, but lacks the ability to verify transaction histories and as a result cannot authenticate digital assets. Blockchains such as Bitcoin Cash (BCH) and Bitcoin Satoshi Vision (BSV) attempt to authenticate digital assets via trusted third parties called "oracles" which indexes the relevant transactions. Such solutions, however, prevent the possibility of advanced blockchain contracts since a trusted custodian is required. In order to solve the problem of digital asset authenticity, without using central parties, we introduce two novel methods that operate in constant O(1) time and space. The additional programming instructions creates a general purpose induction proof system. Users and applications need only to verify that the latest digital asset transfer is accepted into a block.

Radiant is the first unspent transaction output (UTXO) blockchain that solves the key problems that prevented the development of advanced contracts on other blockchains such as Bitcoin, Cardano, and Dash. This breakthrough design revolutionizes what we imagined to be possible with blockchains; Radiant is a Turing Complete high performance layer one blockchain with no need for secondary layers.

Problems

There are three problems which make it impractical to use unspent transaction output (UTXO) blockchains as a general purpose digital ledger. The first problem is the ability to arbitrarily constrain the spend conditions — or forward conditions on all descendant transactions. The second problem is how to efficiently authenticate transaction outputs to ensure they originate from a valid genesis transaction — this is an essential requirement for many programs, especially to emulate accounts and create fungible tokens. The third problem is coordination and collaboration between contracts — precise control of message passing between transaction outputs. We will show that all three problems can be solved without compromising performance or the scalability of the UTXO-based blockchain model.

Contract Constraints

The first obstacle to programming with an unspent transaction output (UTXO) blockchain was a misunderstanding of Satoshi Nakamoto's original design and programming codes available in the original Bitcoin protocol. It is not generally acknowledged but the original Bitcoin blockchain had all of the programming codes necessary for Turing Complete [2] smart contracts. The necessary programming codes were removed from the protocol in the BTC upgrades of 2015.

The method to impose constraints on spending conditions is to restore all of the original programming codes from Bitcoin and to provide a method to inspect the current transaction context. There are two ways to inspect the current transaction as a type of introspection. The first way is to push the Signature Hash (known as the "SigHash Preimage") onto the stack and use a temporary private key to generate a signature and then apply the OP_CHECKSIG operation to validate that the expected SigHash Preimage for the current transaction is valid. The second way is to provide native introspection programming codes that push the relevant transaction component onto the stack for use in the unlocking script.

The key difference with a UTXO blockchain is there are no loops in the programming codes. However in practice any repetition can be simulated with unrolling the loop operations and replicating the logic for the necessary maximum number of repetitions. In this manner, UTXO blockchains can avoid any concept of "execution time cost" and instead estimate the execution cost by using only the transaction script size. For this reason, it is recommended that UTXO blockchains have a sufficiently large maximum transaction size, such as 2 megabytes or more to be able to accommodate any use cases that may need dozens or hundreds of loop iterations.

Contract Persistent Identity

An electronic coin is defined as a chain of digital signatures. A coin begins at a genesis transaction called a "coinbase" transaction. To transfer a coin, the owner of the unspent output signs the coin with their private key and locks the tokens in a new output which is associated with the public key of the recipient. At each transaction a new transaction identifier and output index is used, which is globally unique. The concept of a "wallet balance" for a user is the sum total of the nominal token units controlled by the user for the unspent outputs for their corresponding public keys. Each coin in essence is uniquely identified by it's most recent unspent output. There is no inherent concept of an "account" or "coin identity".

In unspent transaction output (UTXO) blockchains the native token unit is the only class — or type and therefore a unique persistent coin identity is not neccesary. It is sufficient to have a different UTXO identity to enumerate the coins that can be spent. However, if we wish to create a different class of tokens, in other words to "color" the native tokens to represent shares, points or any other enumerable type, then we need a way to represent and efficiently validate token class membership. The term "colored coins" have been used to describe an overlay network which mints tokens from a special genesis or minting transaction — similar to the native coin is emitted from a coinbase.

A custom (or colored) digital coin is defined as a chain of digital signatures anchored at a user defined genesis output. Users may mint or create a custom coin issuance by depositing the desired number of native token units at the output and designating it as a coinbase with 36's 0x00 null bytes as the first push data of the output. The contract logic is constrained such that the subsequent spend of the output must embed the outpoint (transaction id, output index) of the genesis transaction into the first push data (where the 36 0x00 null bytes were in the genesis transaction) for the entire lifecycle of the colored coin. Following this convention combined with the contract constraints, we can see that this tecnique effectively "colors" the native token and can be identified unambiguously. Additional logic can be added according to the application needs such as how the coins are redeemed or returned back to their native token units. For example the issuer can perform the operation or the token holder can "melt" out the native token unit and effectively destroy the color classification. The technique of embedding the genesis output forms a globally unique identifier sometimes referred to as asset identifier (or assetId for short) or contract identifier (or contractId for short) that may now be used to identify the coins that belong to that coin class. This identity will form the basis of the advanced usages outlined below.

There is an outstanding problem however: How can spending transactions ensure that only coins descended from the rightful genesis transaction can be spent and not passed off into spending a forgery that was merely copied?

Contract Traceability & Authenticity

Recall that an unspent transaction output (UTXO) has no persistent identity, but we can give a persistent identity by following the rule that a user may designate some transaction as a genesis minting event, where the outpoint stands in as the assetId or contractId. However, using this convention it is not sufficient because an attacker can copy one of the intermediate transaction spends and begin a new (albiet forgery) chain of signatures to spoof a coin class and pass it off. Any spending transaction are unable to differentiate between a real output that originated as a valid descendent versus the forgery from a false copy. What is required is a way to enforce global uniqueness that is unobtrusive and efficient to verify inside a spending script.

OP_PUSHINPUTREF : Push reference

We define the programming operation code (OP code) OP_PUSHINPUTREF <hash> is defined as valid accordingly:

  1. An OP_PUSHINPUTREF may appear only in an output and requires exactly 36 bytes immediately after that is treated as a push onto the stack in interpretor context.

  2. The transaction containing an output with a OP_PUSHINPUTREF is valid if and only if the provided argument is equal to one of the inputs' outpoints being spent or at least one of the inputs' output locking script bytecode also contains the same OP_PUSHINPUTREF argument value.

The only way an OP_PUSHINPUTREF can first apppear in an output is if the first occurrence is equal to one of the inputs outpoints being spent. In the case of using the above "Persistent Contract Identity", this corresponds to the transaction that contains the 36 0x00 null bytes signifiying a genesis minting coinbase for a custom (colored) coin class.

We demonstrate that this simple rule is sufficient to form a globally unique identifier, and carries no overhead — as in no extra indexes or lookup tables are required. Only a transaction and it's immediate parent inputs are needed to validate authenticity — all of the data is available to the virtual machine at the time of the unlocking script evaluation and also into accepting the transaction into the mempool and subsequently into a block.

As long as at least one of the input coins has a valid 36 byte hash — either as the outpoint itself (significanty the first genesis chain of the colored coin) or as one of the scripts containing the reference, then the identity exists as a persisted identity. To terminate the lineage, simply omit passing on the reference and that terminates the ability to use that unique identifier in any other UTXO forever.

Although this single OP code is sufficient, there are a handful of additional OP codes that provide flexibility for the programmer and are described next which complement OP_PUSHINPUTREF.

OP_REQUIREINPUTREF : Require reference

The OP_REQUIREINPUTREF functions identically to OP_PUSHINPUTREF except it does not pass on the reference identity to the output in which it appears. This is useful for demanding that at least one input is of a specific coin class — but without passing down the reference immediately.

OP_DISALLOWPUSHINPUTREF : Disallow reference in output

To disallow the use of a OP_PUSHINPUTREF in an output, the OP_DISALLOWPUSHINPUTREF may be used. This is a useful OP code for smart contracts which leave open the outputs to be used in various contexts, but allows the contract creator to restrict passing down a reference, for example in custom change outputs.

OP_DISALLOWPUSHINPUTREFSIBLING : Disallow reference in sibling outputs

Similar to OP_DISALLOWPUSHINPUTREF, disallow specific outputs in any sibling outputs for the specific reference. This effectively prohibits using a reference in more than one output and is a way to create a singleton outpoint. By using OP_DISALLOWPUSHINPUTREFSIBLING in an output we can create a simple and powerful Non-Fungible Token (NFT) contract which functions with SIGHASH_SINGLE signature flag.

OP_REFHASHDATASUMMARY_UTXO: Push UTXO data summary

Provides a summary of the contents of an output being spent in the current transaction. Takes the top element of the stack which is the index of the input being spent and then pushes the hash256 of the information about the UTXO being spent: hash256(<nValue><hash256(scriptPubKey)><numRefs><hash256(sorted_list(pushInputRefs))>). During unlocking script evaluation, the relevant data of an UTXO is able to be accessed and incorporated into the logic.

OP_REFHASHVALUESUM_UTXOS: Push value sum of UTXO by reference (color)

This programming code accepts a hash256 of the 36 byte reference and pushes onto the stack the sum total of all of the inputs that matches that reference coloring. This is useful for saving data and for quickly assessing the total inputs and the values input to the transaction.

This is very useful for building a compact fungible token accounting system as we shall see below.

Contract Authenticity via Induction

Another method for solving the traceability and authenticity problem is to allow the embedding of the parent transaction into an unlocking script. In this manner, we can perform induction proofs and guarantee that a transaction output originated from a valid genesis minting event.

The general principle in mathematical induction is to prove that some statement P(k) holds for k = 0, k = 1, k = 2... and so on that generally P(k) holds for P(0) and P(k + 1).

In the case of smart contracts, we wish to prove that a given transaction is valid in the base case, as in it descends from valid parent, the base case P(0), and in the inductive case that the grand parent also satisfies the condition, which is the P(k + 1) step.

With an induction proof it is impossible to forge an intermediate transaction because the grand parent transaction will not be of the required origination.

This system is not practical however because each time the output is spent a full copy of the parent (and it's parent) transaction must be embedded to calculate the transaction identifier. This leads to a factorial, or exponential, explosion in transaction size. It is not practical since after only about a dozen spends, the transaction size starts to exceed 1 GB and continues growing exponentially.

To solve this problem of exponential transaction size growth we leverage the "nVersion" field provided by the transaction format. Bitcoin has version 1 and version 2 transactions already and we simply create a version 3 that uses a different transaction identifier generation algorithm instead of hashing the entire bytes of the transaction. The version 3 transaction format is identical except the transaction id is generated from an intermediate fixed size data structure that compresseses the transaction contents into a preimage — that can be embedded in locking scripts to derive the transaction id and avoid the exponential transaction size problem.

Transaction Identifier Version 3

Similar to the Signature Hash algorithm which generates a "Sighash Preimage", we produce a TxId preimage according to the following components and fields of a transaction.

  1. nVersion of the transaction (4 byte little endian)

  2. nTotalInputs (4 byte little endian)

  3. hashPrevoutInputs (32 byte hash)

  4. hashSequence (32 byte hash)

  5. nTotalOutputs (4 byte little endian)

  6. hashOutputHashes (32 byte hash)

  7. nLocktime of the transaction (4 byte little endian)

Diagram 14. Transaction Identifier Version 3 Preimage\

By incrementing the nVersion field, we introduce a way to compress an entire transaction into a fixed size (128 bytes) that can be pushed onto the stack, and hashed to arrive at the Transaction identifier, and therefore solving the problem of exponential size increase from from the embedded parent transactions in the induction proofs.

Notice that this system itself is sufficient to create arbitrary induction proofs and is general purpose. This is a second method in which arbitrary induction proofs may be created in addition to the already discussed OP_PUSHINPUTREF technique.

Signature Hash Algorithm Upgrade

Building on the Transaction Id preimage the technique of segmenting the outputs, we can upgrade the default Sighash algorithm with an additional field called hashOutputsHashes to make it easier to constrain the outputs and save space and logic.

  1. nVersion of the transaction (4 byte little endian)

  2. hashPrevouts (32 byte hash)

  3. hashSequence (32 byte hash)

  4. outpoint (32 byte hash + 4 byte little endian)

  5. scriptCode of the input (serialized as scripts inside CTxOuts)

  6. value of the output spent by this input (8 byte little endian)

  7. nSequence of the input (4 byte little endian)

  8. hashOutputsHash (32 byte hash)

  9. hashOutputs (32 byte hash)

  10. nLocktime of the transaction (4 byte little endian)

  11. sighash type of the signature (4 byte little endian)

Diagram 16. Radiant Signature Hash Preimage Fields.

This is useful because the other sibling outputs do not need to be included and a hash can be used for the outputs that are not of interest. There is still the color of the push references so that we can assert whether the other outputs contain a valid color, but without requiring the full script to be pushed.

Contract Design Patterns

With the OP_PUSHINPUTREF and TxId Version 3 constructs, we are in a position to define various contract collaboration design patterns. together these patterns will be used in account emulation, non-fungible tokens (NFTS), fungible tokens (FTs) and other programs.

Non-Fungible Tokens (NFT)

Definition: A "Non-Fungible Token" is a uniquely-identified object in which it’s essential properties are conserved

We present a simple, yet powerful, design pattern called a Non-Fungible Token (NFT). Programs will recognize this by another name called a Singleton object. An NFT, or Singleton, guarantees that only one instance of an object can ever exist and is unique identified by a stable persistent identity. Most usages of the term Non-Fungible Token have centered around digital collectibles, however that need not be the case — the reason for that focus has to do with the high-gas fees on Ethereum and the speculative nature of digital artwork.

In the Radiant blockchain, we use the term Non-Fungible Tokens to refer to a uniquely identifable object, or colored coin, which maintains some essential properties in addition to being unambiguously traceable through the blockchain. This is a basic building block and design pattern that will appear in more complex contracts, and here we present a simple, yet very powerful, construction below to start.

Non-Fungible Token Pseudo-code:

contract NFT {

    // Asset identifier
    bytes assetId;
    
    // Current owner is the second push data
    Ripemd160 currentOwnerAddress;      

    public function unlock(
        SigHashPreimage txPreimage, 
        bytes outputSats, 
        bytes newOwnerAddress, 
        bool isMelt, 
        Sig senderSig, 
        PubKey unlockKey
    ) {
        require(hash160(unlockKey) == this.currentOwnerAddress);
        require(checkSig(senderSig, unlockKey));
        
        // Initial assetId is 36-bytes nulls(0x00 bytes)
        bytes actAssetId = (this.assetId == num2bin(0, 36) ? 
            txPreimage[ 68 : 104 ] : this.assetId);
        
        bytes lockingScript = SigHash.scriptCode(txPreimage);
        
        // The default usage is to update/transfer
        if (!isMelt) {
            require(
                hash256(
                    outputSats +

                    // Define length of output
                    b'fd' + num2bin(len(lockingScript, 2)) +
                    
                    // OP_PUSHINPUTREF <assetId>
                    b'd0' + actAssetId +
                    
                    // New owner (20 bytes)
                    b'14' + newOwnerAddress +    

                    // OP_DISALLOWPUSHINPUTREFSIBLING <assetId>       
                    b'd3' + actAssetId + 

                    // Get entire locking script after the push vars 
                    // 95 = 1+36 + 1+20 + 1+36
                    lockingScript[95 : ]                
                )
                ==
                // Compare to HashOuts
                txPreimage[len(txPreimage) - 40 : len(txPreimage) - 8] 
            );
        } else {
            // Melt the NFT back and destroy the reference

            // Use OP_DISALLOWPUSHINPUTREF and 
            // OP_DISALLOWPUSHINPUTREFSIBLING 
            // to prohibit the reference from being passed along
            
            require(
                hash256(
                    // Hardcode len '4b' is 57 bytes (1 + 1 + 36 + 1 + 36)
                    // 'd2' is OP_DISALLOWPUSHINPUTREF
                    // 'd3' is OP_DISALLOWPUSHINPUTREFSIBLING
                    b'00000000000000004b6ad2' + activeAssetId + 
                    b'd3' + activeAssetId 
                )
                ==
                // Compare to HashOuts
                txPreimage[len(txPreimage) - 40 : len(txPreimage) - 8]
            );  
        }
        require(Tx.checkPreimageOpt_(txPreimage));
    }
}

Diagram 19. Non-Fungible Token Pseudocode

Accounts

Definition: An "Account" is an object that manages a wallet balance while maintaining an addressable stable unique identifier.

One of the main difficulties in working with a UTXO-based blockchain is there is no protocol level concept of "wallet balance", and instead infrastructure providers and wallet services present a summary balance derived from the total value of all the individual outputs controlled by a key. Account-based blockchains also simplify specific types of problems and contracts, but trade-off performance and privacy to achieve it's aims.

We present a simple design pattern to emulate accounts using one or more outputs which gives the user and developer a stable unique identifier across transactions in the blockchain. It is built using the Non-Fungible Token design pattern, and demonstrates that UTXO-based blockchains are perfecty equipped to emulate accounts with the same level of control, but with much higher performance characteristics.

Recall that in the Non-Fungible Token design pattern, the stable identifier ContractId (also sometimes referred to as AssetId) is derived from the outpoint of the minting transaction. The same ContractId is used as the public account identifier and can be treated as a wallet balance. We present below pseudo code for a smart contract that implements all the method associated with accounts: deposit, withdraw, changeOwner, and close the account.

contract Account {

    bytes assetId;
    Ripemd160 currentOwnerAddress;
    bytes disallowAssetIdNotUsed;
 
    static function createSingletonOutput(
        SigHashPreimage txPreimage, 
        int amount, 
        bytes assetId,
        bytes address
    ): bool {
        bytes activeAssetId = (assetId == num2bin(0, 36) ? 
            txPreimage[ 68 : 104 ] : assetId);
        bytes lockingScript = SigHash.scriptCode(txPreimage);
        require(amount > 0);
        require(
                hash256(
                    // Add the deposit amount to the existing balance
                    num2bin(SigHash.value(txPreimage) + amount, 8) +
                    
                    b'fd' + num2bin(len(lockingScript), 2) +

                    // OP_PUSHINPUTREF <assetId>          
                    b'd0' + activeAssetId +    

                    // Address/owner (20 bytes)     
                    b'14' + address +

                    // OP_DISALLOWPUSHINPUTREFSIBLING <assetId>               
                    b'd3' + assetId +    

                    // Get entire locking script after the push vars           
                    // 95 = 1+36 + 1+20 + 1+36
                    lockingScript[95 : ]            
                )
                ==
                txPreimage[len(txPreimage) - 40 : len(txPreimage) - 8] // HashOuts
        );
        require(Tx.checkPreimageOpt_(txPreimage));
        return true;
    }

    // Deposit to account
    // Anyone can spend this input and deposit funds into 
    // the account, but only the owner can withdraw funds.
    public function deposit(
        SigHashPreimage txPreimage, 
        int amount
    ) {
        require(amount > 0);

        require(
            Account.createSingletonOutput(
                txPreimage, 
                SigHash.value(txPreimage) + amount, 
                this.assetId, 
                this.currentOwnerAddress
            )
        );
    }

    // Withdraw from account
    // The current owner can withdraw from the account
    // via any other outputs.
    public function withdraw(
        SigHashPreimage txPreimage, 
        int amount,
        Sig senderSig, 
        PubKey unlockKey
    ) {
        require(hash160(unlockKey) == this.currentOwnerAddress);
        require(checkSig(senderSig, unlockKey));

        require(
            Account.createSingletonOutput(
                txPreimage, 
                SigHash.value(txPreimage) - amount,
                this.assetId, 
                this.currentOwnerAddress
            )
        );
    }

    // Change the account owner
    // The current owner can assign the account to another 
    // address owner
    public function changeOwner(
        SigHashPreimage txPreimage, 
        bytes newOwnerAddress, 
        Sig senderSig, 
        PubKey unlockKey
    ) {
        require(hash160(unlockKey) == this.currentOwnerAddress);
        require(checkSig(senderSig, unlockKey));
        
        require(
            Account.createSingletonOutput(
                txPreimage, 
                SigHash.value(txPreimage), 
                this.assetId, 
                newOwnerAddress
            )
        );
    }

    // Close the account
    // The current owner of the account can permanently close
    // the account and withdraw any tokens via other outputs
    public function close(
        SigHashPreimage txPreimage, 
        Sig senderSig, 
        PubKey unlockKey
    ) {
        require(hash160(unlockKey) == this.currentOwnerAddress);
        require(checkSig(senderSig, unlockKey));
        
        bytes activeAssetId = (this.assetId == num2bin(0, 36) ?
            txPreimage[ 68 : 104 ] : this.assetId);
        
        bytes lockingScript = SigHash.scriptCode(txPreimage);

        // Ensure one of the outputs is unspendable OP_RETURN 
        // and uses the OP codes to prohibit passing on the 
        // reference.
        // OP_DISALLOWPUSHINPUTREF and 
        // OP_DISALLOWPUSHINPUTREFSIBLING which effectively 
        // means no output may contain the reference anymore, 
        // thereby ending the ability to carry on the assetId 
        // anywhere else forever.
        require(
            hash256(
                b'00000000000000004b6ad2' + activeAssetId + 
                // Hardcode len '4b' is 57 bytes (1 + 1 + 36 + 1 + 36)
                b'd3' + activeAssetId 
            )
            ==
            // HashOuts
            txPreimage[len(txPreimage) - 40 : len(txPreimage) - 8] 
        );
        require(Tx.checkPreimageOpt_(txPreimage));
    }
}

Diagram 20. Account contract pseudocode

Fungible Tokens (FT)

The Fungible Token design pattern allows the same class or type of object to have more than a quantity of one. The fungible tokens can be merged together, with their values summed up into a new output, or an output can be split into two or more outputs where the total sum of the outputs is equal to the input value amount. This design pattern is useful for simulating loyalty points, tokens, and more.

We present the solution:

contract SuperAssetR201 {
    // Do NOT provide a constructor as that will add unnecessary OP_0 OP_0 to the beginning of the contract
    bytes assetId;                      // Asset identifier
    Ripemd160 currentOwnerAddress;      // Current owner is the second push data
    // Notice that "disallowAssetIdNotUsed" is not used below. The reason is that we save space and also it should always be same as assetId
    bytes disallowAssetIdNotUsed;       // Disallow Asset from being used in any other output
    static const int MAX_RECEIVE = 6;
  
    static function buildOutputVector(
        int amount, 
        bytes assetId,
        bytes address,
        bytes outputScriptLen,
        bytes lockingScriptCodePart
    ): bytes {
        return 
            num2bin(amount, 8) +
            hash256(
                outputScriptLen +
            
                // OP_PUSHINPUTREF <assetId>          
                b'd0' + assetId +    

                // Address/owner (20 bytes)     
                b'14' + address +

                lockingScriptCodePart     
            ) +
            // One color for the output
            b'01000000' +
            hash256(assetId);
    }

    public function mint(SigHashPreimage txPreimage, int amount) {
        require(amount > 0);
        require(this.assetId == num2bin(0, 36));
        bytes lockingScript = SigHash.scriptCode(txPreimage);
        require(
                hash256(
                    num2bin(amount, 8) +
                    
                    b'fd' + num2bin(len(lockingScript), 2) +

                    // OP_PUSHINPUTREF <assetId>          
                    b'd0' + txPreimage[68 : 104]+    

                    // Address/owner (20 bytes)     
                    b'14' + this.currentOwnerAddress +

                    // Get entire locking script after the push vars           
                    // 95 = 1+36 + 1+20
                    lockingScript[58 : ]            
                )
                ==
                txPreimage[len(txPreimage) - 40 : len(txPreimage) - 8] // HashOuts
        );
        require(Tx.checkPreimageOpt_(txPreimage));
    }

    public function transfer(SigHashPreimage txPreimage, Ripemd160[6] recipients, int[6] amounts, bytes otherOutputs, Sig senderSig, PubKey unlockKey) {
        require(hash160(unlockKey) == this.currentOwnerAddress);
        require(checkSig(senderSig, unlockKey));

        int expectedRefColorSum = 1337;         // Placeholder for OP_INPUTREFVALUESUM
        int actualAccumulatedRefColorSum = 0;   // Used for counting the sum of the colors
        bool break = false;
        bytes expectedOutputVector = b'';
        bytes lockingScript = SigHash.scriptCode(txPreimage);
        // Length of the output script
        bytes outputScriptLen = b'fd' + num2bin(len(lockingScript), 2);
        bytes lockingScriptCodePart = lockingScript[58 : ];
        loop (MAX_RECEIVE) : i {
            if (!break) {
                if (amounts[i] <= 0) {
                    break = true;
                } else {
                    // There is a valid recipient...
                    // Get entire locking script after the push vars           
                    // 58 = 1+36 + 1+20
                    expectedOutputVector += SuperAssetR201.buildOutputVector(amounts[i], this.assetId, recipients[i], outputScriptLen, lockingScriptCodePart);
                    actualAccumulatedRefColorSum += amounts[i];
                }
            }
        }
        require(expectedRefColorSum > 0 && expectedRefColorSum == actualAccumulatedRefColorSum);
        require(
            hash256(expectedOutputVector + otherOutputs)
            ==
            // hashOutputsHashes
            txPreimage[len(txPreimage) - 72 : len(txPreimage) - 40] 
        );
    }
 
    public function melt(SigHashPreimage txPreimage, Sig senderSig, PubKey unlockKey) {
        require(hash160(unlockKey) == this.currentOwnerAddress);
        require(checkSig(senderSig, unlockKey));
        // Ensure one of the outputs is unspendable OP_RETURN and uses the OP codes to prohibit passing on the reference
        // OP_DISALLOWPUSHINPUTREF and OP_DISALLOWPUSHINPUTREFSIBLING which effectively means no output may contain
        // the reference anymore, thereby ending the ability to carry on the assetId anywhere else forever.
        require(
            hash256(
                // Hardcode len '4b' is 57 bytes (1 + 1 + 36 + 1 + 36)
                b'00000000000000004b6ad2' + this.assetId +  b'd3' + this.assetId
            )
            ==
            txPreimage[len(txPreimage) - 40 : len(txPreimage) - 8]  
        );  
        require(Tx.checkPreimageOpt_(txPreimage));
    }
}

Blockchain Network Details

Radiant is a peer-to-peer digital asset system with unbounded scaling as a UTXO-based blockchain with all the flexibility and power of account-based blockchains.

Network Name: Radiant Network Abbreviation: RXD Mining Algorithm: SHA512/256 Proof-of-work Block Time: 5 minutes Initial Block Size: 128 MB, designed to achieve 10GB+ Block Reward Schedule: 50,000 RXD per block Block Reward Halvening: 2 years Maximum Supply: 21,000,000,000 RXD Decimal Places: 8 Launch Date: 2022-06-21 02:42 UTC\

Conclusion

We have proposed a system for digital asset management without relying on trust. We started with the basic blockchain construction of coins made from digital signatures, which provides strong control of ownership. From the needed rules and incentives, we introduced two novel methods for authenticating and tracking digital assets in constant O(1) time and space. Both methods independently provide a general induction proof system which can encode any possible digital asset configuratio. The system is Turing Complete within and across transaction boundaries, with unbounded scale, and never any need for secondary layers. Additionally we have presented three contract design patterns: Non-Fungible Token (NFT), Fungible Token (FT) and Account which emulating account based blockchains, using the UTXO based processing model. Radiant is a breakthrough design which provides the performance and paralellism benefits of an unspent transaction output (UTXO) blockchain, and with the programming sophistication of account-based blockchains, while maintaining ultra low fees and unbounded scale.

References

About RXD

What is Radiant?

Radiant (or RXD) is a peer-to-peer electronic cash system. It uses a blockchain to distribute its ledger over a network of independent nodes so that there is no single point of failure, and no central control that might be compromised. It uses a consensus algorithm called Proof-of-Work that allows these independent nodes to approve correct transactions and reject malicious ones.

Basics

The blockchain is a data structure that is distributed over a number of independent nodes. It derives its name from the chain of blocks that it uses to store its data. All blocks include a block header with some metadata and the root of a Merkle tree - a special kind of tree that allows quick validation of data. This Merkle tree is then used to store the actual data inside these blocks. To make the chain resistant to manipulation, block headers also include a timestamp and a hash of the previous block.

Proof-of-Work

Radiant and many other public blockchains use a consensus algorithm called Proof-of-Work (PoW). This algorithm works by attaching a nonce to every block header and changing this nonce until the hash of the block header matches a certain prefix. This process is called mining, and is attempted by many nodes at the same time, until one of them has found a correct solution. One of the attributes of this algorithm is that mining is very expensive, but other nodes can verify the solution very quickly.

Mining is also the process by which new coins are introduced to the total monetary supply. Miners validate transactions and secure the network, for which they are paid new coins - called the block reward - in a special transaction called a coinbase transaction. The high cost of the mining process attaches a financial risk to incorrectly validating transactions. At the same time the block reward attaches a financial reward to correctly validating transactions. This process ensures that the mutually distrusting nodes can collaborate to validate transactions.

Transactions

Radiant transactions are created using chunks of RXD called transaction outputs. When these outputs are available, they are called Unspent Transaction Outputs (UTXOs). UTXOs are locked using a locking script (or scriptPubKey) that specifies the conditions to spend the UTXO. When attempting to spend a UTXO, an unlocking script (or scriptSig) is provided. These scripts are then executed together and the transaction is only valid if the scripts execute without errors and the resulting value is TRUE.

The most used locking/unlocking script pattern is called Pay-to-Public-Key-Hash (P2PKH), where the locking script contains the hash of a public key and expects the unlocking script to contain a public key and transaction signature. The locking script then checks that the provided public key matches the stored hash, and that the transaction signature is valid. This pattern is used in regular Radiant wallets. And the user's balance is simply the sum of all UTXOs that can be spent by the user's public keys.

UTXOs are used as inputs to Radiant transactions and produce new UTXOs as outputs. UTXOs need to be spent in their entirety within a transaction. So whenever the user wishes to use a 10 RXD UTXO to send someone 1 RXD, they need to send 9 RXD back to themselves. Realistically, part of the funds would be reserved for transaction fees as well.

Smart Contracts

Most smart contract innovation has happened on Ethereum, but other platforms like Bitcoin and Radiant have some support for smart contracts as well. Smart contracts on every platform work differently, and the main differences between smart contracts on Ethereum and Radiant is that smart contracts on Ethereum are stateful, while those on Radiant are stateless.

This means that Ethereum contracts can record and update variables, while the variables in Radiant contracts are immutable.

Radiant Script

The locking and unlocking scripts of regular transactions and smart contracts on Radiant are written using Radiant' transaction scripting language, creatively named Script. To avoid ambiguity, it can also be referred to as Bitcoin Script or Radiant Script. Script is a stack based assembly-like language that is intentionally not turing complete as its main use is the validation of programmable money, not general purpose computing.

Script is stateless, meaning it only uses the information contained within the locking and unlocking scripts themselves. This statelessness means that a Script can be deterministically validated on any machine. This gives increased performance and predictability, although it does limit the usefulness of the scripting language.

Radiant Contracts

TODO

Glyphs Protocol

Introduction

This is a guide to the Glyphs Protocol on the Radiant Blockchain.

Glyphs is a token standard utilizing Radiant's induction proof system and advanced scripting capabilities. Each token is identified by a Radiant reference (ref) and uses smart contracts for enforcing token transfer rules. Glyphs is a fully layer one protocol with all rules enforced by miners.

The Glyphs protocol operates efficiently with minimal indexing requirements, not requiring wallets or indexers to track each token transfer back to the mint. This allows wallets to efficiently verify tokens using SPV. This makes Glyphs a more secure, efficient and scalable system than layer two alternatives.

The protocol supports both fungible and non-fungible tokens. Fungible tokens use Radiant photons as the unit of account, with each fungible token backed by one photon. NFTs may be backed by any number of Radiant photons.

Token contracts are simple, concise and extensible, allowing composition with other custom contracts to support many complex use cases.

Glyphs is currently implemented in Photonic Wallet (Web App and Browser Extension) and Glyph Miner.

Overview

Glyphs are implemented using Radiant's UTXO model and operate in a similar way to native Radiant photons. Radiant photons are "colored" with a Radiant ref which acts like a contract identifier for the token. A token is minted by creating a UTXO containing an amount of photons, a standard token contract and a Radiant ref. The contract along with the ref is carried forward with each spend. The contract and ref remain the same for the lifetime of the token, until it is melted.

With refs enforced by miners we can be certain a token is authentic simply by verifying block headers in the same way as native photons.

Minting

Glyphs are minted using a commit/reveal procedure with token metadata contained in the reveal transaction's unlocking script. The token's ref is created from an input to the reveal transaction. Therefore a glyph's ref will always point to a commit outpoint.

The token payload in the reveal transaction contains data such as name, image, ticker, relationships to other tokens etc.

The protocol does not dictate how the mint transaction should be created. Developers may use any locking script for the commit/reveal procedure, as long as the token payload is correctly encoded in the reveal.

For each ref that is required an input must be prepared. Radiant singleton refs are used for NFTs, so each NFT requires a unique ref.

Verifying a token

When a wallet receives a token, it extracts the ref from the script and looks up the reveal transaction on the blockchain to obtain the token data.

For example, an NFT script:

OP_PUSHINPUTREFSINGLETON a32f3628a967ce64cca282262e63879136f9c3f4f8e1125345a85c71d90b10b401000000 OP_DROP
OP_DUP OP_HASH160 3a23c6b9055b533db3900e4755dd58d1234c8582 OP_EQUALVERIFY OP_CHECKSIG

The ref a32f3628a967ce64cca282262e63879136f9c3f4f8e1125345a85c71d90b10b401000000 refers to an outpoint in a commit transaction.

The reveal will be the transaction that spent this output, with a token payload contained in the unlocking script.

Radiant's implementation of ElectrumX has been improved to simplify the process of finding the reveal transaction.

Transferring a token

Transferring is done in much the same way as native Radiant photons. For NFTs, the UTXO is spent and the script is moved to a new output. Fungible tokens are similar, except since they use normal refs instead of singletons, token UTXOs may be split and merged.

Any number of different tokens may be provided as inputs and outputs of a transaction. Token transfer rules are all implemented in script, with token input and output values summed within script, ensuring token supply is enforced. Token UTXOs may be positioned anywhere in a transaction's inputs and outputs and there is no restriction on the number of token UTXOs. The Radiant opcodes used to sum token values operate over any number of inputs and outputs.

Encoding

Acquiring and holding RXD coins is a fundamental way to support the Radiant ecosystem. Your participation in the coin and token economy helps drive growth and stability. More holders and Radiant ecosystem participants can strengthen the lindy effect. Here's why buying RXD matters:

Payload structure

A glyph's token data is encoded in the reveal unlocking script, identified by the string "gly" followed by a CBOR encoded token payload:

OP_PUSH "gly"
OP_PUSH <CBOR Payload>

The Glyphs protocol defines standard top level fields shown below in CDDL:

payload = {
  p: [* (number / string)]       ; Array of protocol identifiers
  name: tstr,                    ; Name of the token
  ? type: tstr,                  ; Type of the token (e.g. container, user or other custom types)
  ? in: [*bstr],                 ; Array of container token refs the token is part of
  ? by: [*bstr],                 ; Array of author token refs the token is created by
  ? main: {                      ; Main file
    t: tstr,                     ; File mime type
    b: bstr                      ; File bytes
  },
  ? attrs: { * tstr => any }     ; Attributes as key value pairs (e.g. color, rarity, etc)
  ? loc: number / bytes / string ; Location for link tokens
}

Example CBOR payload:

{
    p: [1, 4],
    name: "My token",
    ticker: "XYZ",
    main: {
        t: "image/jpeg",
        b: <bytes>
    },
    in: [
        <containerRefBytes>
    ],
    by: [
        <authorRefBytes>
    ],
}

Protocols

The p property contains an array of protocols used by the token. Current protocol identifiers are as follows:

ID

Protocol

1

Fungible token

2

Non-fungible token

3

Data storage

4

Decentralized mint

5

Mutable

Some protocols may depend on other protocols also being implemented. For example for a mineable token, a p value of [1, 4] must be used, indicating the token implements the FT and dmint contracts.

File encoding

In addition to the main property other files can be added to the payload. These can be either embedded or remote.

Embed:

  embed = {
    t: tstr, ; File mime type
    b: bstr  ; File bytes
  }

Remote:

  remote = {
    t: tstr,   ; File mime type
    u: tstr    ; URL
    ? h: bstr  ; File hash
    ? hs: bstr ; HashStamp image (low resolution copy of image)
  }

Example:

{
    main: {
        t: "image/jpeg",
        b: <bytes> // File bytes embedded in transaction
    },
    alt: {
        t: "image/jpeg",
        u: "https://url/to/image.jpg", // Link to remote file
        h: <hashBytes>,
        hs: <hashStampBytes>
    }
}

If a link to a remote file is used, it is recommended to include a hash so the downloaded file can be verified.

HashStamp

For remote images a HashStamp may be provided in the hs property. This is a low resolution, highly compressed copy of the image in WebP format. This may be desirable for people who want to store data off-chain but still have some representation of the file on-chain. It is recommended to keep HashStamps within 64x64 pixels and quality 20 to keep size to a minimum.

HashStamps can be used by wallets to display a version of the image if downloading the file is deemed insecure, or used as a loading placeholder similar to a BlurHash.

Types

The Glyphs protocol uses types container and user, for allowing the creation of collections and associating tokens to a creator. Wallets should implement these types and render the tokens accordingly.

Custom types are also permitted.

Custom fields

Any custom fields may be added to the payload.

Fungible tokens

Fungible tokens must be created with the protocol p field set to 1. This informs wallets that the token uses a standard fungible token script.

The script is a P2PKH followed by a script to enforce fungible token rules:

// P2PKH
OP_DUP OP_HASH160 <address> OP_EQUALVERIFY OP_CHECKSIG

OP_STATESEPARATOR

// Fungible token ref
OP_PUSHINPUTREF <ref>

// Get count of ref used in outputs
OP_REFOUTPUTCOUNT_OUTPUTS OP_INPUTINDEX

// Get a hash of the script after state separator
OP_CODESCRIPTBYTECODE_UTXO OP_HASH256 OP_DUP

// Sum token value for all outputs
OP_CODESCRIPTHASHVALUESUM_UTXOS

// Sum token value for all inputs
OP_OVER OP_CODESCRIPTHASHVALUESUM_OUTPUTS

// Input sum must be greater than or equal to output sum
OP_GREATERTHANOREQUAL OP_VERIFY

// To prevent supply inflation, ref must be used within this script and ref count must be equal to contract count
OP_CODESCRIPTHASHOUTPUTCOUNT_OUTPUTS OP_NUMEQUALVERIFY

The script after the P2PKH must be unchanged for every transfer.

The protocol permits locking scripts other than P2PKH, such as multisig, time locks and other more complex scripts. The only requirement is that the script after the state separator is unchanged.

Transfer

Tokens are transferred in a similar way to regular RXD photons. The contract and ref are carried forward with each spend and UTXOs can be split and merged. Tokens may be at any position in the inputs or outputs. A transaction may also contain any number of different tokens.

Melt

The contract permits output value sum to be less than input value sum. Melting fungible tokens only requires making the output value less than input value. The RXD photons backing the tokens are then reclaimed.

Non-fungible tokens

Non-fungible tokens must be created with the protocol p field set to 2.

The script is simply a singleton ref followed by a P2PKH or other locking script:

OP_PUSHINPUTREFSINGLETON <ref> OP_DROP
OP_DUP OP_HASH160 <address> OP_EQUALVERIFY OP_CHECKSIG // P2PKH

Radiant rules enforce that there can only ever be one instance of a singleton ref.

Transfer

NFTs are transferred in a similar way to regular RXD photons. The singleton ref is carried forward with each spend and may be at any position in the transaction inputs or outputs.

Similar to fungible tokens, any number of different tokens may be present in a transaction.

Melt

Melting a non-fungible token can be done in two ways:

  • Omit the ref from the outputs

  • Spend the NFT to an unspendable op return script containing the ref

Relationships

The Glyph protocol provides in and by arrays in the token payload for defining relationships to other tokens, such as NFT collections. Relationships can also be defined in custom properties.

For a token to be related its ref must be present in the commit transaction. Wallets must verify this by checking each related token's ref is in an output of the commit. This prevents authorized users creating relationships, such as assigning an author or adding NFTs to a collection they don't control.

Delegates

Having a related token in a commit is not always practical if there are many thousands of mint transactions with the same relationship. For example, minting a large NFT collection with the same container and author tokens. To solve this a token delegate may be used.

The delegate is a temporary normal ref that links to one or more token refs such as a user or container token.

A delegate is created by a transaction containing the related tokens, and one or more outputs with the following script:

OP_PUSHINPUTREF <ref> OP_DROP
OP_DUP OP_HASH160 <address> OP_EQUALVERIFY OP_CHECKSIG // P2PKH

Any number of delegates may be created to simplify bulk mints.

Verifying related tokens

When a wallet does not find a related token's ref in the commit transaction, it must look for delegates within the commit script. Delegate refs must be required using a OP_REQUIREINPUTREF opcode in the commit script.

If the wallet finds required refs, it fetches the delegate creation transaction and looks for related tokens in its outputs. If the refs are found in the delegate creation transaction then the relationship is valid.

Custom relationships

Developers are free to define their own relationships separate to the in and by arrays.

Links

Tokens may be linked to any resource, either tokens or off-chain resources. This has many uses such as creating a record of a token mint or even building a decentralized redirect service.

Links are created using the loc field which is used by wallets in a similar way to the HTTP location header.

To create a link the loc field is set to either a number, bytes or string:

 location = number / bytes / string
  • number - Link to a token with given output index and transaction ID matching the link ref

  • bytes - Link to a token given as ref bytes

  • string - Link to any URI

Mutable tokens

Token payloads can be made mutable by using a mutability contract and adding protocol 5 to the p array in the token payload.

The mutability contract must be created in the mint transaction and assigned a singleton ref with an output index one higher than the token ref (token ref + 1).

The contract uses a "pay to token" script which does not contain a P2PKH or any signature checking opcodes. The contract requires the token to be present in the transaction in order to modify the payload.

Modify mod and seal sl operation are implemented. Seal is called to make the token immutable and enforces burning of the mutability contract.

Pay to token

With a "pay to token" script, the mutability contract is always controlled by the token owner. This means every time a token is transferred to a new owner, they automatically get access to updating the token's data. The mutability output never needs to be transferred, only called to update the data.

Fetching mutable data

If a wallet receives a mutable token, the most recent data can be obtained by looking up the most recent location of the mutability ref and fetching the transaction. Data is encoded similar to a token mint, with the Glyph token payload in the unlocking script.

Script

// SHA256 of payload must be provided
<payloadHash> OP_DROP

OP_STATESEPARATOR

// Mutable contract ref
OP_PUSHINPUTREFSINGLETON <mutableRef>

// Build token ref (mutable ref -1)
OP_DUP 20 OP_SPLIT OP_BIN2NUM OP_1SUB OP_4 OP_NUM2BIN OP_CAT

// Check token ref exists in token output at given refdatasummary index
OP_2 OP_PICK OP_REFDATASUMMARY_OUTPUT OP_4 OP_ROLL 24 OP_MUL OP_SPLIT OP_NIP 24 OP_SPLIT OP_DROP OP_EQUALVERIFY

// Compare ref + scriptsig hash in token output to this script's ref + scriptsig hash
OP_SWAP OP_STATESCRIPTBYTECODE_OUTPUT OP_ROT OP_SPLIT OP_NIP 45 OP_SPLIT OP_DROP OP_OVER 20 OP_CAT OP_INPUTINDEX OP_INPUTBYTECODE OP_SHA256 OP_CAT OP_EQUALVERIFY

// Modify operation
OP_2 OP_PICK 6d6f64 OP_EQUAL OP_IF

// Contract script must exist unchanged in output
OP_OVER OP_CODESCRIPTBYTECODE_OUTPUT OP_INPUTINDEX OP_CODESCRIPTBYTECODE_UTXO OP_EQUALVERIFY

// State script must contain payload hash
OP_OVER OP_STATESCRIPTBYTECODE_OUTPUT 20 OP_5 OP_PICK OP_HASH256 OP_CAT 75 OP_CAT OP_EQUALVERIFY OP_ELSE

// Seal operation
OP_2 OP_PICK 736c OP_EQUALVERIFY OP_OVER OP_OUTPUTBYTECODE d8 OP_2 OP_PICK OP_CAT 6a OP_CAT OP_EQUAL OP_OVER OP_REFTYPE_OUTPUT OP_0 OP_NUMEQUAL OP_BOOLOR OP_VERIFY OP_ENDIF

// Glyph header
OP_4 OP_ROLL ${glyphMagicBytesHex} OP_EQUALVERIFY OP_2DROP OP_2DROP OP_1

Unlocking script

In order to use a "pay to token" contract securely, the data must be signed. Without a signature check opcode in the contract, this must be done in a different way.

The contract requires the token owner to provide the token as an input. In the token's output it must include a hash of the mutability contract's unlocking script. This way, the token owner signs the updated data in the token output, the contract verifies it, and the transaction is not vulnerable to malleation. In addition to the unlocking script hash, the output must have an OP_REQUIREINPUTREF requiring the mutability contract as an input. This ref + hash pair is checked by the mutability contract.

To spend the contract the following parameters must be provided in the unlocking script

OP_PUSH gly
OP_PUSH mod // Modify operation
OP_PUSH <cbor payload> // Updated token payload
OP_PUSH <contract output index> // Location of mutability contract UTXO in outputs
OP_PUSH <ref+hash index in token output> // Index of the ref+hash in the token output, allowing multiple authorizations in a single transaction (zero for a single update)
OP_PUSH <ref index in token output data summary> // Position of token ref in OP_REFDATASUMMARY_OUTPUT (the sorted list of refs used in all outputs)
OP_PUSH <token output index> // Location of token UTXO in outputs

Authorizing an update

To authorize this update, the token output must have the hash of the above unlocking script following an OP_REQUIREINPUTREF. For example:

// Require the mutability contract (token ref + 1)
OP_REQUIREINPUTREF a32f3628a967ce64cca282262e63879136f9c3f4f8e1125345a85c71d90b10b402000000
// Hash of mutability contract's unlocking script
OP_PUSH a204a95c54bf73975256898be5f47d7844146845f9b749408b535d12e3ad85a9
// Drop ref and hash
OP_2DROP
// Following is a standard NFT contract
OP_PUSHINPUTREFSINGLETON a32f3628a967ce64cca282262e63879136f9c3f4f8e1125345a85c71d90b10b401000000 OP_DROP
OP_DUP OP_HASH160 3a23c6b9055b533db3900e4755dd58d1234c8582 OP_EQUALVERIFY OP_CHECKSIG

The mutability contract will verify that the hash of the provided unlocking script, along with the necessary OP_REQUIREINPUTREF are contained in the token UTXO.

Restoring token script

An optional final step is for the token owner to restore the token script to a standard NFT contract that can be recognized by wallets. The OP_REQUIREINPUTREF and hash are no longer required.

Decentralized mints

Decentralized mints (dmint) are mineable tokens that can be claimed by anyone using proof-of-work. These tokens are created with protocol [1, 4] and are locked in a layer one smart contract.

PoW algorithm

With a "pay to token" script, the mutability contract is always controlled by the token owner. This means every time a token is transferred to a new owMineable Glyphs use the follow proof-of-work algorithm:

hash = sha256(sha256(
    sha256(currentLocationTxid + contractRef) +
    sha256(anyInputHash + anyOutputHash) +
    nonce
))

Resulting hash must be below the target.

anyInputHash and anyOutputHash must be the hash of any input or output in the transaction. This allows work to be bound to the miner's address and prevents nonces being stolen. This will typically be a pay-to-public-key-hash but any script can be used. The contract will verify these scripts exist.

Further information

ElectrumX

Radiant's implementation of ElectrumX has modifications to support Glyphs.

Querying

ElectrumX indexes each script with the ref zeroed out. The zeroed ref acts as a wildcard meaning a wallet can fetch all transactions for an address by using a ref with 36 zero bytes in the token script.

For example:

OP_PUSHINPUTREFSINGLETON 000000000000000000000000000000000000000000000000000000000000000000000000 OP_DROP OP_DUP OP_HASH160 <address> OP_EQUALVERIFY OP_CHECKSIG

This script is hashed and can be used with listunspent and script hash subscriptions. This prevents having to perform many queries for each token ref or having to know token refs in advance.

Ref get

A glyph ref will point to an outpoint in the commit transaction, therefore this alone cannot be used to fetch the token payload. To simplify this task, the ref.get method will return the first and last locations of a ref.

The first location will be the reveal transaction. The last location is used to track the current location of a contract.

List unspent

The listunspent method is improved to return an array of all refs contained in the script. Since refs are zeroed when querying for tokens, the wallet does not know the token ref in the returned UTXOs and would need to query each UTXO individually to determine the token.

With refs returned as part of the listunspent response, the wallet can build scripts without the additional queries.


Radiant: un sistema de activos digitales electrónico peer-to-peer

Spanish version of the Radiant whitepaper

11 Agosto, 2022

radiantblockchain.org

Resumen. La red Radiant es un sistema de activos digitales entre pares que permite el intercambio directo de valor sin pasar por un ente central. El protocolo original de Bitcoin[1] proporciona lo necesario para crear un sistema de dinero electrónico entre pares, pero carece de la capacidad para verificar los historiales de las transacciones y, por tanto, no puede utilizarse para validar activos digitales. Las firmas digitales y las restricciones de salida proporcionan parte de la solución, pero las principales ventajas se pierden si se sigue necesitando un tercero de confianza para validar los activos digitales. Al igual que Bitcoin, la red Radiant requiere una estructura mínima, y marca el tiempo de las transacciones en una cadena de prueba de trabajo basada en hash. Introducimos dos técnicas para validar los activos digitales: referencias únicas y un sistema de prueba de inducción de propósito general, que funcionan en tiempo y espacio constantes de O(1). Es posible componer las salidas de cualquier manera, sin comprometer el paralelismo inherente y las características de rendimiento de una arquitectura basada en la salida de transacciones no gastadas (UTXO). Por lo tanto, los usuarios pueden salir y volver a entrar en la red Radiant a voluntad y estar seguros de la integridad y autenticidad de sus activos digitales.

1. Introducción

Los blockchains comerciales, o tecnología de libro mayor digital (DLT), se suelen basar en emisores y custodios que actúan como partes de confianza para autenticar los activos digitales. Si bien estos sistemas funcionan lo suficientemente bien para las transacciones similares a los pagos electrónicos, siguen adoleciendo de las debilidades inherentes al modelo basado en la confianza para usos avanzados. Las cadenas de bloques basadas en la máquina virtual de Ethereum (EVM) [2] son muy flexibles para todo tipo de programas, pero las elevadas tarifas hacen que su uso para aplicaciones con micropagos sea poco práctico.

Lo que se necesita es un sistema de pago electrónico que se pueda utilizar para el sistema de gestión de activos digitales con tarifas bajas, alto rendimiento y capacidades de programación avanzadas. En este documento, proponemos una solución al problema del escalado en blockchain y al uso de contratos inteligentes, utilizando dos técnicas novedosas que proporcionan referencias únicas y un sistema de prueba de inducción general, que hacen posibles los programas turing completo [3] a través de los límites de las transacciones. El sistema propuesto es descentralizado, utiliza un mecanismo de consenso de prueba de trabajo como Bitcoin, pero con un rendimiento significativamente mayor, al tiempo que proporciona la misma flexibilidad que los blockchains basados en EVM, pero con tarifas muy bajas.

2. Transacciones

Al igual que Bitcoin, definimos una moneda electrónica como una cadena de firmas digitales. La diferencia de las transacciones en Radiant es que cada propietario transfiere la moneda al siguiente firmando digitalmente un hash de la transacción anterior, además de los parámetros de entrada necesarios para desbloquear la moneda. Una transacción también crea nuevas restricciones de bloqueo de salida, que pueden incluir la clave pública del siguiente propietario, entre otras reglas definidas por el usuario.

Diagrama 1. Transacciones en Radiant.

Para verificar que no se ha producido un doble gasto, utilizamos un servidor de marcas de tiempo distribuido, que utiliza un sistema de prueba de trabajo basado en hash para organizar el historial canónico y determinar qué transacción llegó primero. Las transacciones se organizan en bloques. Por convención, la primera transacción, llamada "transacción coinbase", en un bloque es una transacción especial que inicia una nueva moneda propiedad del creador del bloque. Los bloques se encadenan y organizan las transacciones en un árbol de Merkle [4]. Todas las transacciones, a excepción de la primera, deben hacer referencia a una transacción anterior formando un gráfico acíclico dirigido (DAG) en el que todas las monedas acaban conectándose de nuevo a al menos una de las transacciones especiales del principio de un bloque.

Diagrama 2. Estructura de Bloques; las transacciones se organizan en un árbol de Merkle.

El problema de este diseño, en el contexto de los activos digitales, es que sólo hay un tipo de moneda, o activo digital, y ningún concepto de monedas definidas por el usuario (o tipos de activos digitales). El diseño funciona lo suficientemente bien para las transacciones similares a los pagos electrónicos en la unidad de cuenta nativa, sin embargo, no se presta inmediatamente a ser utilizado para otros tipos de monedas o activos digitales. Una solución común es introducir un servicio, como un indexador de transacciones, que supervise las transacciones en busca de secuencias de datos especiales que signifiquen la creación de un activo digital. El problema de esta solución es que depende de la empresa que gestiona el servicio, y la autenticidad de los activos digitales debe ser de confianza, al igual que cualquier otro servicio en la web.

Necesitamos una forma para que los usuarios indiquen la creación de tipos de moneda personalizados, pero que no dependan de un servicio de confianza para la presentación de datos.

3. Activos Digitales

Definimos una moneda electrónica personalizada, o activo digital, como una cadena de firmas digitales. Un activo digital es un tipo de moneda definido por el usuario que utiliza un marcador de transacción especial, llamado transacción "assetbase", para crear o acuñar un activo digital. De forma similar a las transacciones de coinbase, que inyectan nuevas monedas en el sistema, la transacción de "assetbase" colorea o etiqueta la moneda electrónica con un identificador único de 36 bytes para toda su vida. La moneda electrónica personalizada se superpone al tipo de moneda base y funciona de manera similar. La transacción de assetbase puede aparecer en cualquier parte del bloque y puede aplicar cualquier regla y restricción personalizada que se decida por adelantado.

Diagrama 3. Transacciones que representan tipos de moneda definidos por el usuario — o activos digitales.

Para lograrlo, necesitamos crear un identificador único estable y un mecanismo de transacción para rastrear la autenticidad del tipo de moneda (activo digital). Los usuarios del sistema necesitan tener pruebas de que los tipos de moneda personalizados no son falsificaciones y representan con exactitud los activos digitales.

Diagrama 4. Los tipos de moneda definidos por el usuario se definen a partir de una transacción especial de la Fábrica de Moneda. Se utiliza un identificador único para clasificar el tipo de moneda.

4. Identificadores Únicos

Para implementar un identificador único para un tipo de moneda, utilizamos una transacción marcadora especial, llamada "transacción de base de activos", que actúa como el inicio (ceca) de la cadena de firmas digitales. En lugar de requerir una nueva estructura de datos para el identificador único, reutilizamos el identificador de la transacción y el índice de salida, llamado "outpoint", como identificador único para el tipo de moneda. Se garantiza que los puntos de salida (36 bytes) son aleatorios y globalmente únicos.

Una instrucción de programación, llamada OP_PUSHINPUTREF, se utiliza para adjuntar una referencia a una salida. La instrucción acepta exactamente un parámetro de 36 bytes que debe coincidir con 1) el punto de salida de una de las salidas que se están gastando, o 2) el mismo valor de 36 bytes que ya aparece en un OP_PUSHINPUTREF previamente especificado en una de las salidas que se están gastando. La única manera de que un valor determinado aparezca en la salida de una transacción es que, a través de alguna transacción antecesora, coincida con el punto de salida de la transacción inicial de assetbase. Las transacciones que especifican un valor que no cumple ninguna de las dos condiciones no son válidas.

Diagrama 5. Los identificadores únicos se inician al coincidir con un punto de salida de una de las salidas que se gastan, y luego se mantienen mientras al menos una de las salidas que se gastan contenga el mismo identificador único en el cuerpo del script.

Esta sencilla instrucción de programación proporciona un identificador único que puede utilizarse como referencia estable para crear reglas avanzadas. Por ejemplo, diferentes tipos de monedas, activos digitales, pueden ahora depender de otros tipos de monedas. Dado que todos los datos son locales a la transacción, a través de sus transacciones de entrada inmediatas, es fácil para los clientes y servicios validar la autenticidad de un activo digital en O(1) tiempo y espacio constantes, evitando la necesidad de un tercero de confianza.

5. Pruebas por inducción

Es posible crear identificadores únicos de forma alternativa y también proporcionar un mecanismo para pruebas de inducción matemática [5] utilizando un algoritmo de hash de transacciones modificado. Permitiendo que los scripts de entrada acepten que se gaste la transacción padre, las reglas pueden verificar que el padre, y su abuelo, se ajustan a las reglas requeridas. El problema obvio es que a medida que se incrusta cada copia completa de las transacciones padre, se produce una explosión de tamaño exponencial que impide el uso práctico de la técnica. Lo que se necesita es una forma de comprimir la transacción, de modo que se pueda utilizar una estructura de datos de tamaño fijo para derivar el hash de la transacción, en lugar de requerir el contenido completo de la transacción.

Diagrama 6. Validación de la transacción completa de los padres, prueba de inducción matemática mediante la incorporación de las transacciones completas de los padres en las entradas, lo que resulta en un aumento exponencial del tamaño de la transacción.

Podemos lograrlo modificando el algoritmo de hash de transacciones utilizado en Bitcoin, en el que se calcula un doble hash de sha-256 a partir de la transacción serializada, en una nueva versión que primero resume el contenido de la transacción para derivar el hash. Introducimos la versión 3 del algoritmo de hash de transacciones, para distinguirlo del uso de la versión 1 y la versión 2 en Bitcoin. El proceso consiste en hacer un hash de cada campo, o componente de una transacción, a un hash intermedio, que puede ser utilizado como una entrada de tamaño fijo y así evitar el crecimiento exponencial del tamaño de la transacción.

Utilizamos la siguiente estructura de datos de 112 bytes, en lugar de los bytes completos de la transacción serializada, que a su vez se somete a un doble hash sha-256 para obtener finalmente el hash de la transacción.

Campos de preimagen Hash de la transacción versión 3:

  1. nVersion(=3) de la transacción (4 byte little endian)

  2. nTotalInputs (4 byte little endian)

  3. hashPrevoutInputs (32 byte hash)

  4. hashSequence (32 byte hash)

  5. nTotalOutputs (4 byte little endian)

  6. hashOutputHashes (32 byte hash)

  7. nLocktime de la transacción (4 byte little endian)

Al utilizar el algoritmo de hash de transacciones de la versión 3, podemos incrustar la transacción del padre y del abuelo en cada paso de una prueba de inducción matemática para evitar el aumento del tamaño de la transacción, y podemos aplicar cualquier regla necesaria.

Diagrama 7. Validación comprimida de la transacción de los padres, prueba de inducción matemática mediante la incrustación de la estructura de datos de preimagen de la versión 3 del hash de la transacción de los padres y los abuelos para hacer cumplir reglas y restricciones arbitrarias.

6. Red

La topología de la red es un grafo casi completo, en el que cada Nodo Minero está conectado a cualquier otro Nodo Minero. Los pasos para hacer funcionar la red son los mismos que en Bitcoin, con algunas distinciones para los diferentes tipos de nodos: Nodos Mineros, Nodos Agentes y Nodos de Archivo. Los Nodos Mineros son los que publican activamente los bloques y mantienen el consenso con todos los demás nodos, los Nodos de Archivo sirven los datos históricos de los bloques, y los Nodos de Agente están diseñados para filtrar los bloques y seguir las transacciones de interés para las aplicaciones a las que sirven. Los Nodos de Archivo y los Nodos Agentes pueden operar en la misma red peer-to-peer y, sin embargo, no producen bloques. Los nodos no mineros, como los de archivo y los de agente, se denominan a veces "nodos oyentes" para distinguir su función en la red.

Diagrama 8. Los nodos de minería están bien conectados y construyen sobre los bloques de los demás. Los nodos de archivo almacenan bloques completos para fines de análisis histórico y de arranque. Los nodos de agente son nodos de escucha que filtran y almacenan transacciones para servir a los clientes.

Los nodos mineros están bien conectados en un gráfo casi completo entre otros nodos mineros. Su trabajo es construir sobre los bloques de los demás y mantener el consenso para los últimos cientos de bloques, y mantener el conjunto UTXO para la prevención del doble gasto.

Los Nodos Agente sólo necesitan almacenar un subconjunto de transacciones, por ejemplo, un tipo de moneda o activo digital específico. Incluso con bloques grandes, un nodo agente puede filtrar rápidamente las transacciones por referencias o secuencias de bytes específicas, y luego almacenar esas transacciones para servirlas a través de una interfaz de programador de aplicaciones. Entre los agentes que cooperan, se puede anunciar públicamente el hash de la raíz del árbol de Merkle para las transacciones de cada bloque que coincidan con un patrón predeterminado para señalar a otros Agentes y consumidores con transacciones que ha procesado ese Agente

Los nodos de archivo se utilizan para crear copias de seguridad de bloques enteros para diversas aplicaciones, como el almacenamiento de datos, el análisis y el aprendizaje automático. Como los nodos de archivo no participan directamente en la minería, no tienen los mismos requisitos de rendimiento y ancho de banda en tiempo real que los nodos de minería o de agente.

7. Cálculos

Consideramos el escenario en el que la red Radiant sigue creciendo y lo que supone para los requisitos de procesamiento de los nodos de minería, archivo y agente. A modo de comparación, en el momento de escribir este artículo, hay unos 83 millones de salidas de transacciones sin gastar para el blockchain de Bitcoin, lo que supone un total de unos 6 GB de datos necesarios para evitar el doble gasto. Sólo es necesario que los Nodos Mineros conserven los cientos de bloques más recientes, estando los bloques más antiguos disponibles en los Nodos de Archivo. Para los Nodos Agentes, es necesario mantener la partición relevante de los resultados de las transacciones no gastadas que pertenecen a las aplicaciones que sirven, el escalado es una función del ancho de banda y no de los requisitos de almacenamiento.

Para nuestros propósitos, suponemos que habrá bloques de 3 GB de tamaño cada 5 minutos que se marcarán con el tiempo y se distribuirán por la red, o unas 20.000 transacciones por segundo con un tamaño medio de transacción de 500 bytes, o unos 6.000.000 de transacciones por bloque. Demostramos que para cada tipo de nodo, la red es capaz de escalar adecuadamente para satisfacer la demanda global. Esto equivale a una transacción cada 5 días para cada uno de los 8.000 millones de habitantes del planeta.

Nodos mineros

Los nodos mineros son el único tipo de nodo que construye sobre los bloques de los demás. Para mantener el consenso, basta con sincronizar el conjunto de transacciones no gastadas (UTXO) y mantener sólo unos cien bloques. En el momento de escribir este artículo, las unidades de estado sólido de alto rendimiento son capaces de alcanzar más de 120.000 IOPS, con un coste de unos 500 dólares por 280 GB, y por lo tanto pueden manejar unas 20.000 transacciones por segundo (suponiendo que cada transacción tiene 2 entradas y 2 salidas). Hay 2 lecturas para las entradas, 2 actualizaciones para las entradas y 2 escrituras para la nueva salida: 120.000 / 6 = 20.000 transacciones/segundo.

Nodos de archivo

Los Nodos de Archivo proporcionan datos históricos en bloque y son adecuados para aplicaciones de aprendizaje automático, análisis y almacenamiento de datos. Los nodos de archivo pueden complementar el arranque de los nodos de minería y ejecutar comprobaciones periódicas de consistencia en el conjunto UTXO. En el momento de escribir estas líneas, se pueden adquirir discos duros básicos de 18 TB por unos 350 dólares. Suponiendo 3 GB de datos cada 5 minutos, se necesitan 732 GB de almacenamiento de datos al día, o unos 22 TB al mes. El coste de hardware para un año es de 15 discos duros, con 18 TB de capacidad, por un coste incremental anual de 5.000 USD.

Nodos Agente

Los Nodos Agentes son los que escalan más fácilmente entre los tipos de nodos porque sólo procesan los tipos de transacciones relevantes para las aplicaciones a las que sirven. Como resultado, los Nodos Agentes pueden ser desde un servidor web hasta un dispositivo IoT ligero con capacidades limitadas de procesamiento y almacenamiento, y aún así mantener la estabilidad con bloques de 3 GB, o 20.000 transacciones por segundo. Por ejemplo, una empresa puede querer hacer un seguimiento del uso de sus puntos de fidelidad, creados como un activo digital, y por lo tanto sólo necesita seleccionar un pequeño subconjunto de actualizaciones de transacciones de cada bloque que coincidan con el identificador único para ese tipo de moneda.

En el momento de escribir este artículo, un dispositivo informático comercial, Raspberry Pi 4, se vende por unos 275 dólares y tiene un procesador de cuatro núcleos a 1,5 GHZ y 4 GB de RAM, que puede utilizarse para filtrar rápidamente y descartar las transacciones irrelevantes, a un ritmo de 5.000 transacciones por núcleo. Por supuesto, esto es sólo un ejemplo de lo razonable que es procesar grandes bloques, en una aplicación web típica puede haber muchos más núcleos disponibles.

La velocidad media del ancho de banda de los 25 principales países supera los 100 MBPS, es decir, unos 10 MB/segundo de descarga, y muchos proveedores de servicios de Internet ofrecen descargas ilimitadas. Los requisitos de ancho de banda para bloques de 3 GB cada 5 minutos son de unos 10 MB/segundo para un total de 22 TB al mes. También se pueden crear jerarquías de Nodos Agentes para filtrar los requisitos totales de ancho de banda para los Nodos Agentes con menor capacidad de ancho de banda.

8. Conclusión

Hemos propuesto un sistema de gestión de activos digitales sin depender de la confianza. Empezamos con los bloques de construcción básicos de monedas hechas de firmas digitales, lo que proporciona un fuerte control de la propiedad. A partir de las reglas e incentivos necesarios, introducimos dos métodos novedosos para autenticar y rastrear los activos digitales en tiempo y espacio constantes de O(1). Ambos métodos proporcionan de forma independiente un sistema general de pruebas de inducción matemática que puede codificar cualquier configuración posible de activos digitales. El sistema es Turing completo dentro y a través de los límites de las transacciones, sin necesidad de capas secundarias. Radiant tiene un diseño innovador que proporciona las ventajas de rendimiento y paralelismo de una blockchain de salida de transacciones no gastadas (UTXO), pero con la capacidad de crear contratos inteligentes de las blockchains basadas en cuentas y basadas en la máquina virtual de Ethereum (EVM).

Referencias

[1] Satoshi Nakamoto, "Bitcoin: A Peer-to-Peer Electronic Cash System" URL https://bitcoin.org/bitcoin.pdf, 2009.

[2] Vitalik Buterin, "Ethereum: A Next-Generation Smart Contract and Decentralized Application Platform." URL https://ethereum.org/en/whitepaper/, 2014.

[3] Wikipedia contributors. "Turing completeness." Wikipedia, The Free Encyclopedia. Wikipedia, The Free Encyclopedia, URL https://en.wikipedia.org/wiki/Turing_completeness, 21 Jul. 2022

[4] R.C. Merkle, "Protocols for public key cryptosystems," In Proc. 1980 Symposium on Security and Privacy, IEEE Computer Society, pages 122-133, April 1980.

[5] Britannica, T. Editors of Encyclopaedia. "mathematical induction." Encyclopedia Britannica, URL https://www.britannica.com/science/mathematical-induction, 2018

RadiantScript

What is RadiantScript?

CashScript is a high-level programming language for smart contracts on Radiant. It offers a strong abstraction layer over Radiant' native virtual machine, RadiantScript. Its syntax is based on Ethereum's smart contract language Solidity, but its functionality is very different since smart contracts on Radiant differ greatly from smart contracts on Ethereum. For a detailed comparison of them, refer to the blog post.

Command Line Interface

Installation

You can use npm to install the cashc command line tool globally.

Usage

The cashc CLI tool can be used to compile .cash files to JSON artifact files.

Token-Controlled Contracts on Radiant (Pay to Token)

Token-controlled contracts revolutionize contract composition and management on Radiant, facilitating the separation of contracts into multiple UTXOs, each with distinct functionalities. With token ownership housed in its UTXO and additional UTXOs for data storage and specialized functionalities, developers can seamlessly integrate custom functionalities with standard token scripts, extending contract capabilities effortlessly.

Script Details

Token Input

A standard singleton + P2PKH token script:

Token Output

The token output features a state script binding it to the contract input using ´OP_REQUIREINPUTREF´ , followed by the contract's script signature hash:

Script Sig

Script Pub Key

This script empowers complex contracts composed of multiple UTXOs, each with unique responsibilities. Token scripts maintain standard scripts easily understood by wallets but can be associated with custom contracts. Pay to token contracts can be deployed at any time to expand token functionality.

Advantages

  • Enhanced Functionality: Contracts can be extended with custom functionalities effortlessly.

  • Streamlined Updates: Token transfers and data updates are segregated, simplifying history tracking.

  • Reduced Load: Wallets can focus on relevant transaction histories, improving efficiency.

  • This script can be used to break a contract up into multiple parts. For example a complex NFT contract, split into a token contract and ownership contract:

Token-controlled contracts pave the way for more flexible and manageable blockchain contracts, fostering innovation and scalability in decentralized applications.

Guides

Getting Started

Installing the CashScript compiler

The command line CashScript compiler cashc can be installed from NPM.

Installing the JavaScript SDK

The JavaScript SDK can be installed into your project with NPM.

Caution: CashScript only offers a JavaScript SDK, but CashScript contracts can be integrated into other languages as well. Because there are no ready-to-use SDKs available for them, this is considered advanced usage, and it is recommended to use the JavaScript SDK.

Writing your first smart contract

Integrating into JavaScript

While more detailed examples are available on GitHub, we show an integration of the TransferWithTimeout contract in a JavaScript project.

After compiling the contract file to an artifact JSON with cashc, it can be imported into the CashScript SDK.

Language

Syntax Highlighting

When developing smart contracts for CashScript it is useful to have the proper syntax highlighting in your code editor / IDE. If you use Visual Studio Code, there is a dedicated CashScript extension. For other editors it is recommended to install a Solidity highlighting plugin and associate it with .cash files in your editor, since the syntaxes of the two languages are very similar.

Visual Studio Code (Recommended)

Because of the first-class CashScript support, Visual Studio Code together with this CashScript extension is the recommended way to develop CashScript contracts.

Sublime Text

View -> Syntax -> Open all with current extension as ... -> Solidity

This associates .cash files with Solidity, and enables syntax highlighting for your CashScript files.

Atom

```yaml title="~/.atom/config.cson" core: customFileTypes: "source.solidity": ["cash"]

This associates .cash files with Solidity, and enables syntax highlighting for your CashScript files.

GitHub & GitLab

GitHub and GitLab have syntax highlighting for Solidity built in. To associate .cash files with Solidity highlighting, add a .gitattributes file to your repository with the following contents:

python title=".gitattributes" *.cash linguist-language=Solidity # GitHub *.cash gitlab-language=solidity # GitLab

Others

If your editor is not mentioned above, the steps are likely very similar. Try to find a Solidity syntax highlighting plugin for your editor of choice and find a method to associate .cash files with this Solidity highlighting.

Writing Covenants & Introspection

Covenants are all the rage in Bitcoin Cash smart contracts. But what are they, and how do you use them? In one sentence: a covenant is a constraint on how money can be spent. A simple example is creating a smart contract that may only send money to one specific address and nowhere else. The term Covenant originates in property law, where it is used to constrain the use of any object - or in the case of BCH, the use of money.

Accessible introspection data

When using CashScript, you can access a lot of introspection data that can be used to inspect and constrain transaction details, such as inputs and outputs.

  • int this.activeInputIndex - Index of the input that is currently under evaluation during transaction validation.

  • bytes this.activeBytecode - Contract bytecode of the input that is currently under evaluation during transaction validation.

  • int tx.version - Version of the transaction.

  • int tx.locktime - nLocktime field of the transaction.

  • int tx.inputs.length - Number of inputs in the transaction.

  • int tx.inputs[i].value - Value of a specific input (in satoshis).

  • bytes tx.inputs[i].lockingBytecode - Locking bytecode (scriptPubKey) of a specific input.

  • bytes tx.inputs[i].unlockingBytecode - Unlocking bytecode (scriptSig) of a specific input.

  • bytes32 tx.inputs[i].outpointTransactionHash - Outpoint transaction hash of a specific input.

  • int tx.inputs[i].outpointIndex - Outpoint index of a specific input.

  • int tx.inputs[i].sequenceNumber - nSequence number of a specific input.

  • int tx.outputs.length - Number of outputs in the transaction.

  • int tx.outputs[i].value - Value of a specific output (in satoshis).

  • bytes tx.outputs[i].lockingBytecode - Locking bytecode (scriptPubKey) of a specific output.

Using introspection data

While we know the individual data fields, it's not immediately clear how this can be used to create useful smart contracts on Bitcoin Cash. But there are several constraints that can be created using these fields, most important of which are constraints on the recipients of funds, so that is what we discuss.

Restricting P2PKH recipients

The contract starts by doing some checks to make sure the transaction is signed by the arbiter. Next up it checks that the full contract balance (tx.inputs[this.activeInputIndex].value) is sent to the first output by accessing tx.outputs[0].value. Finally it checks that the receiver of that money is either the buyer or the seller using LockingBytecodeP2PKH and tx.outputs[0].lockingBytecode. Note that we use a hardcoded fee as it is difficult to calculate the exact transaction fee inside the smart contract.

Restricting P2SH recipients

Besides sending money to P2PKH addresses, it is also possible to send money to a smart contract (P2SH) address. This can be used in the same way as a P2PKH address if the script hash is known beforehand, but this can also be used to make sure that money has to be sent back to the current smart contract.

This is especially effective when used together with time constraints. An example is the Licho's Last Will contract. This contract puts a dead man's switch on the contract's holdings, and requires the owner to send a heartbeat to the contract every six months. If the contract hasn't received this heartbeat, an inheritor can claim the funds instead.

This contract has three functions, but only the refresh() function uses a covenant. Again it performs necessary checks to verify that the transaction is signed by the owner, after which it checks that the entire contract balance is sent. It then uses tx.inputs[this.activeInputIndex].lockingBytecode to access its own locking bytecode, which can be used as the locking bytecode of this output. Sending the full value back to the same contract effectively resets the tx.age counter, so the owner of the contract needs to do this every 180 days.

Restricting P2PKH and P2SH

The earlier examples showed sending money to only a single output of either P2PKH or P2SH. But there nothing preventing us from writing a contract that can send to multiple outputs, including a combination of P2PKH and P2SH outputs. A good example is the Licho's Mecenas contract that allows you to set up recurring payments where the recipient is able to claim the same amount every month, while the remainder has to be sent back to the contract.

This contract applies similar techniques as the previous two examples to verify the signature, although in this case it does not matter who the signer of the transaction is. Since the outputs are restricted with covenants, there is no way someone could call this function to send money anywhere but to the correct outputs.

Simulating state

A more advanced use case of restricting recipients is so-called simulated state. This works by restricting the recipient to a slightly amended version of the current contract. This can be done when the changes to the contract are only to its constructor parameters and when these parameters are of a known size (like bytes20 or bytes4).

To demonstrate this we consider the Mecenas contract again, and focus on a drawback of this contract: you have to claim the funds at exactly the right moment or you're leaving money on the table. Every time you claim money from the contract, the tx.age counter is reset, so the next claim is possible 30 days after the previous claim. So if we wait a few days to claim, these days are basically wasted.

Instead of having a pledge per 30 day period, we define a pledge per block. At any point in time we can calculate how much money the recipient has earned. Then the covenant enforces that this amount is withdrawn from the contract. The remainder is sent to a new stream that starts at the end of of the previous one. The bytecode of this new stream is computed by "cutting out" some of the existing constructor parameters in the this.activeBytecode field and replacing them with new values. This process can be applied to the new stream until the money in the stream runs out.

Restricting OP_RETURN outputs

In this contract we construct an "announcement" OP_RETURN output, we reserve a part of value for the miner fee, and finally we send the remainder back to the contract.

Conclusion

Non-Fungible Tokens on Radiant

Radiant's singleton references are ideal for the creation of NFTs. A singleton ref is a type of reference which can only ever exist in a single UTXO. A transaction is invalid if a singleton ref is pushed to more than one output. References cannot change type, so a singleton ref must be created as a singleton. Therefore we can be sure that there has only ever been one instance of a singleton ref.

The opcode for pushing a singleton ref is OP_PUSHINPUTREFSINGLETON.

A simple NFT contract

We will define a contract for an immutable NFT by adding a singleton ref to a standard P2PKH script:

Following Radiant's ref rules, the ref must match an outpoint or a ref of the same type in an input. If the ref matches an outpoint then this is the "mint" transaction. OP_PUSHINPUTREFSINGLETON leaves the ref on the stack, and since we don't need it later in the script it is immediately dropped.

DIAGRAM

As seen in the diagram above, an outpoint in the first transaction is used to create a singleton ref. The referenced output also contains an "immutable token payload" which can be stored as a push data. This is a simple way of associating data with our token, where the token data can be fetched from the referenced output. The payload could be an image or more complex data structure. Token UTXOs are kept light weight by not carrying the payload.

This token protocol can be used for a wide variety of use cases such as tickets, coupons and collectibles. Wallet integration is simple since the script only requires a singleton ref in addition to a standard P2PKH script.

Melting a token

Melting a token only requires that the token be spent and the singleton ref not pushed forward. Once the singleton does not exist in a UTXO it cannot be recreated.

Conclusion

Singleton refs are a simple yet effective way of creating an NFT protocol. We have demonstrated how a token protocol can be created with only a small change to a standard P2PKH script. In future articles we will explore how to extend this contract to support mutable data.

Radiant: Sistem Aset Digital Peer-to-Peer

Indonesian version of the Radiant whitepaper

Para Pengembang Radiant

11 Agustus, 2022

radiantblockchain.org

Abstrak. Jaringan Radiant adalah sistem aset digital peer-to-peer yang memungkinkan pertukaran nilai secara langsung tanpa melalui pihak pusat. Protokol asli Bitcoin[1] menyediakan apa yang diperlukan untuk membuat sistem kas elektronik peer-to-peer, tetapi tidak memiliki kemampuan untuk memverifikasi riwayat transaksi dan oleh karena itu, tidak dapat digunakan untuk memvalidasi aset digital. Tanda tangan digital dan batasan keluaran memberikan bagian dari solusi, tetapi manfaat utama hilang jika pihak ketiga yang tepercaya masih diperlukan untuk memvalidasi aset digital. Mirip dengan Bitcoin, jaringan Radiant membutuhkan struktur minimal, dan mencatat waktu transaksi ke dalam rantai bukti kerja berbasis hash yang sedang berlangsung. Kami memperkenalkan dua teknik untuk memvalidasi aset digital: referensi unik dan sistem bukti induksi tujuan umum yang keduanya beroperasi dalam ruang dan waktu O(1) konstan. Dimungkinkan untuk menyusun output dengan cara apa pun, tanpa mengorbankan karakteristik paralelisme dan kinerja yang melekat dari arsitektur berbasis output transaksi yang tidak terpakai (UTXO). Oleh karena itu, pengguna dapat keluar dan bergabung kembali dengan jaringan Radiant sesuka hati dan yakin akan integritas dan keaslian aset digital mereka.

1. Perkenalan

Perdagangan dengan blockchain, atau teknologi buku besar digital (DLT), dalam banyak kasus bergantung pada penerbit dan kustodian yang berfungsi sebagai pihak tepercaya untuk mengautentikasi aset digital. Sementara sistem tersebut bekerja cukup baik untuk transaksi seperti pembayaran elektronik, mereka masih menderita kelemahan yang melekat pada model berbasis kepercayaan untuk tingkat lanjut menggunakan. Blockchain berbasis Ethereum Virtual Machine (EVM) [2] sangat fleksibel untuk semua jenis program, tetapi biaya tinggi membuat penggunaan aplikasi pembayaran mikro menjadi tidak praktis

Yang dibutuhkan adalah sistem pembayaran elektronik yang dapat digunakan untuk sistem manajemen aset digital dengan biaya rendah, kinerja tinggi, dan kemampuan pemrograman tingkat lanjut. Dalam makalah ini, kami mengusulkan solusi untuk masalah penskalaan dan kontrak blockchain menggunakan dua teknik baru yang memberikan referensi unik dan sistem bukti induksi umum, yang membuat program Turing Complete [3] melintasi batas transaksi menjadi mungkin. Sistem yang diusulkan terdesentralisasi, menggunakan mekanisme konsensus proof-of-work seperti Bitcoin, tetapi dengan tingkat throughput yang jauh lebih tinggi, sambil memberikan fleksibilitas yang sama dari blockchain berbasis EVM, dengan biaya yang sangat rendah.

2. Transaksi

Mirip dengan Bitcoin, kami mendefinisikan koin elektronik sebagai rangkaian tanda tangan digital. Di mana transaksi di Radiant berbeda adalah bahwa setiap pemilik mentransfer koin ke yang berikutnya dengan menandatangani hash secara digital dari transaksi sebelumnya selain parameter input yang diperlukan untuk membuka kunci koin. Sebuah transaksi juga menciptakan kendala penguncian keluaran baru, yang mungkin termasuk kunci publik dari pemilik berikutnya, di antara aturan lain yang ditentukan oleh pengguna.

Diagram 1. Transaksi Radiant.

Untuk memverifikasi bahwa pembelanjaan ganda tidak terjadi, kami menggunakan server timestamp terdistribusi, menggunakan sistem proof-of-work berbasis hash untuk mengatur riwayat kanonis guna menentukan transaksi mana yang tiba lebih dulu. Transaksi diatur ke dalam blok. Sesuai kesepakatan, transaksi pertama, yang disebut "transaksi berbasis koin", dalam sebuah blok adalah transaksi khusus yang memulai koin baru yang dimiliki oleh pembuat blok. Blok dirantai bersama dan mengatur transaksi menjadi Pohon Merkle [4]. Semua transaksi, kecuali yang pertama, harus mengacu pada transaksi sebelumnya yang membentuk grafik asiklik terarah (DAG) di mana semua koin pada akhirnya terhubung kembali ke setidaknya satu transaksi khusus di awal blok.

Diagram 2. Struktur Blok; transaksi diatur ke dalam Pohon Merkle.

Masalah dengan desain ini, dalam konteks aset digital, adalah hanya ada satu jenis koin, atau aset digital, dan tidak ada konsep koin yang ditentukan pengguna (atau jenis aset digital). Desainnya berfungsi cukup baik untuk transaksi seperti pembayaran elektronik di unit akun asli, namun tidak langsung cocok untuk digunakan untuk jenis koin atau aset digital lainnya. Solusi umum adalah dengan memperkenalkan layanan seperti pengindeks transaksi yang memantau transaksi untuk urutan data khusus untuk menandai pembuatan aset digital. Masalah dengan solusi ini adalah bergantung pada perusahaan yang menjalankan layanan, dengan keaslian aset digital yang perlu dipercaya, sama seperti layanan lain di web. Kami membutuhkan cara bagi pengguna untuk menunjukkan pembuatan jenis koin khusus, tetapi tidak bergantung pada layanan tepercaya yang akan digunakan untuk presentasi data.

3. Aset Digital

Kami mendefinisikan koin elektronik khusus, atau aset digital, sebagai rangkaian tanda tangan digital. Aset digital adalah jenis koin yang ditentukan pengguna menggunakan penanda transaksi khusus, yang disebut "transaksi basis aset", untuk membuat atau mencetak aset digital. Mirip dengan transaksi coinbase, yang menyuntikkan koin baru ke dalam sistem, transaksi assetbase mewarnai atau menandai koin elektronik dengan pengidentifikasi 36-byte unik untuk seumur hidup. Koin elektronik kustom dilapisi di atas jenis koin dasar dan berfungsi dengan cara yang sama. Transaksi basis aset dapat muncul di mana saja di blok dan dapat memberlakukan aturan dan batasan khusus apa pun yang diputuskan di muka.

Diagram 3. Transaksi yang mewakili jenis koin yang ditentukan pengguna — atau digital asset

Untuk melakukannya, kita perlu membuat pengidentifikasi unik yang stabil dan mekanisme transaksi untuk melacak keaslian jenis koin (aset digital). Pengguna sistem perlu memiliki bukti bahwa jenis koin khusus bukan pemalsuan dan secara akurat mewakili aset digital.

Diagram 4. Jenis koin khusus yang ditentukan pengguna ditentukan dari transaksi mint khusus. Pengidentifikasi unik digunakan untuk mengklasifikasikan jenis koin.

4. Pengidentifikasi Unik

Untuk menerapkan pengidentifikasi unik untuk jenis koin, kami menggunakan transaksi penanda khusus, yang disebut "transaksi basis aset", yang bertindak sebagai awal (mint) rantai tanda tangan digital. Daripada memerlukan struktur data baru untuk pengenal unik, kami menggunakan kembali pengenal transaksi dan indeks keluaran, yang disebut "outpoint", sebagai pengenal unik untuk jenis koin. Dipastikan bahwa outpoints (36-bytes) bersifat acak dan unik secara global.

Instruksi pemrograman, disebut OP_PUSHINPUTREF, digunakan untuk melampirkan referensi ke output. Instruksi menerima tepat satu parameter 36-byte yang harus cocok dengan 1) titik keluar dari salah satu output yang digunakan, atau 2) nilai 36-byte yang sama sudah muncul di OP_PUSHINPUTREF yang ditentukan sebelumnya di salah satu output yang digunakan . Satu-satunya cara agar nilai tertentu muncul dalam keluaran transaksi adalah melalui beberapa transaksi leluhur, nilai tersebut cocok dengan titik keluar dari transaksi basis aset pencetakan awal. Transaksi yang menentukan nilai yang tidak memenuhi salah satu syarat tidak valid.

Diagram 5. Pengidentifikasi unik diinisialisasi dengan mencocokkan titik keluar dari salah satu keluaran yang digunakan, dan kemudian dipertahankan selama setidaknya salah satu keluaran yang dikeluarkan berisi pengenal unik yang sama di badan skrip.

Instruksi pemrograman sederhana ini memberikan pengidentifikasi unik yang dapat digunakan sebagai referensi stabil untuk membuat aturan lanjutan. Misalnya, berbagai jenis koin, aset digital, kini dapat bergantung pada jenis koin lainnya. Karena semua data bersifat lokal untuk transaksi, melalui transaksi masukan induk langsung, mudah bagi klien dan layanan untuk memvalidasi keaslian aset digital dalam O(1) waktu dan ruang yang konstan, menghindari kebutuhan akan layanan tepercaya .

5. Pembuktian Dengan Induksi

Dimungkinkan untuk membuat pengidentifikasi unik dengan cara alternatif dan juga menyediakan mekanisme untuk pembuktian induksi matematika [5] menggunakan algoritma hash transaksi yang dimodifikasi. Dengan mengizinkan skrip input untuk menerima transaksi induk yang dibelanjakan, aturan dapat memverifikasi bahwa induk, dan kakeknya sesuai dengan aturan yang diperlukan. Masalah yang jelas adalah bahwa karena setiap salinan lengkap dari transaksi induk disematkan, ledakan ukuran eksponensial terjadi dan mencegah penggunaan teknik secara praktis. Apa yang dibutuhkan adalah cara untuk mengompresi transaksi, sehingga struktur data berukuran tetap dapat digunakan sebagai gantinya untuk memperoleh hash transaksi, alih-alih membutuhkan konten transaksi penuh.

Diagram 6. Validasi full parent transaction, pembuktian induksi matematis dengan memasukkan full parent transaction ke dalam input yang menghasilkan peningkatan ukuran transaksi secara eksponensial.

Kita dapat melakukannya dengan memodifikasi algoritme hash transaksi yang digunakan dalam Bitcoin, di mana intisari sha-256 ganda dihitung dari transaksi berseri, menjadi versi baru yang terlebih dahulu meringkas konten transaksi untuk memperoleh hash. Kami memperkenalkan algoritma hash transaksi versi 3, untuk membedakannya dari penggunaan versi 1 dan versi 2 di Bitcoin. Prosesnya adalah mencirikan setiap bidang, atau komponen transaksi, ke hash perantara, yang dapat digunakan sebagai input ukuran tetap dan dengan demikian menghindari pertumbuhan ukuran transaksi eksponensial.

Kami menggunakan struktur data 112-byte berikut, alih-alih byte transaksi berseri lengkap, yang pada gilirannya di-hash sha-256 ganda untuk akhirnya mendapatkan hash transaksi.

Versi Transaksi 3 Kolom Hash Preimage:

  1. nVersion(=3) transaksi (4 byte little endian)

  2. nTotalInputs (4 byte little endian)

  3. hashPrevoutInputs (hash 32 byte)

  4. hashSequence (hash 32 byte)

  5. nTotalOutputs (4 byte little endian)

  6. hashOutputHash (hash 32 byte)

  7. nLocktime transaksi (4 byte little endian)

Dengan menggunakan algoritme hash transaksi untuk transaksi versi 3, kami dapat menyematkan transaksi induk dan kakek-nenek di setiap langkah bukti induksi matematis untuk mencegah peningkatan ukuran transaksi, dan dapat menerapkan aturan apa pun yang diperlukan.

Diagram 7. Validasi transaksi induk terkompresi, bukti induksi matematis dengan menyematkan struktur data preimage hash transaksi versi 3 dari induk dan kakek-nenek untuk menegakkan aturan dan batasan yang sewenang-wenang

6. Jaringan

Topologi jaringan adalah grafik yang hampir lengkap, di mana setiap Node Penambangan terhubung ke setiap Node Penambangan lainnya. Langkah-langkah untuk menjalankan jaringan sama dengan Bitcoin, dengan beberapa perbedaan untuk jenis node yang berbeda: Node Penambangan, Node Agen, Node Arsip. Mining Nodes adalah penerbit blok aktif dan mempertahankan konsensus dengan semua node lain, Node Arsip melayani data blok historis, dan Node Agen dirancang untuk memfilter blok dan melacak transaksi yang menarik ke aplikasi yang mereka layani. Arsip dan Node Agen dapat beroperasi pada jaringan peer-to-peer yang sama namun tidak menghasilkan blok. Non-Mining Nodes seperti Archive dan Agent Nodes terkadang disebut sebagai "node pendengar" untuk membedakan peran mereka dalam jaringan.

Diagram 8. Node Penambangan terhubung dengan baik dan dibangun di atas blok masing-masing. Node arsip menyimpan blok lengkap untuk analisis historis dan tujuan bootstrap. Node agen adalah node pendengar yang memfilter dan menyimpan transaksi untuk melayani klien.

Node Penambangan terhubung dengan baik ke dalam grafik yang hampir lengkap antara Node Penambangan lainnya. Tugas mereka adalah membangun di atas blok satu sama lain dan mempertahankan konsensus untuk beberapa ratus blok terbaru, dan mempertahankan set UTXO untuk pencegahan pengeluaran ganda.

Agen Node hanya perlu menyimpan subset transaksi, misalnya jenis koin tertentu atau aset digital. Bahkan dengan blok besar, Agen Node dapat dengan cepat memfilter transaksi dengan referensi atau urutan byte tertentu, lalu menyimpan transaksi tersebut untuk ditayangkan melalui antarmuka pemrogram aplikasi. Di antara agen yang bekerja sama, root hash Merkle Tree dapat diumumkan secara publik untuk transaksi di setiap blok yang cocok dengan pola yang telah ditentukan sebelumnya untuk memberi sinyal kepada Agen dan konsumen lain tentang transaksi mana yang telah diproses oleh Agen.

Node Arsip digunakan untuk membuat salinan cadangan seluruh blok untuk berbagai aplikasi termasuk pergudangan data, analitik, dan pembelajaran mesin. Karena Archive Nodes tidak terlibat langsung dalam penambangan, mereka tidak memiliki persyaratan kinerja dan bandwidth real-time yang sama seperti Mining atau Agent Nodes.

7. Kalkulasi

Kami mempertimbangkan skenario di mana jaringan Radiant terus tumbuh dan apa yang diperlukan untuk persyaratan pemrosesan Mining, Archive, dan Agent Nodes. Sebagai perbandingan, pada saat penulisan, ada sekitar 83 juta keluaran transaksi yang tidak terpakai untuk blockchain Bitcoin, dengan total sekitar 6 GB data yang diperlukan untuk mencegah pengeluaran ganda. Hanya perlu bahwa beberapa ratus blok terbaru disimpan oleh Mining Nodes, dengan blok yang lebih lama tersedia dari Archive Nodes. Untuk Agent Nodes, penting untuk menjaga partisi yang relevan dari output transaksi yang tidak terpakai yang berkaitan dengan aplikasi yang mereka layani, penskalaan adalah fungsi bandwidth dan bukan kebutuhan penyimpanan.

Untuk tujuan kami, kami berasumsi bahwa akan ada blok berukuran 3 GB setiap 5 menit untuk diberi stempel waktu dan didistribusikan ke seluruh jaringan, atau sekitar 20.000 transaksi per detik dengan ukuran transaksi rata-rata 500 byte, atau sekitar 6.000.000 transaksi per blok. Kami menunjukkan bahwa untuk setiap jenis node, jaringan dapat diskalakan secara memadai untuk memenuhi permintaan global. Ini sama dengan sekitar 1 transaksi setiap 5 hari untuk masing-masing dari 8 miliar orang di planet ini.

Node Penambangan

Mining Nodes adalah satu-satunya tipe node yang dibangun di atas blok masing-masing. Untuk mempertahankan konsensus, cukup menyinkronkan kumpulan output transaksi yang tidak terpakai (UTXO), dan hanya mempertahankan sekitar seratus blok terakhir. Pada saat penulisan, solid-state drive komoditas kinerja tinggi mampu mencapai lebih dari 120.000 IOPS, dengan biaya sekitar $500 USD untuk 280 GB, dan karenanya dapat menangani sekitar 20.000 transaksi per detik (dengan asumsi setiap transaksi memiliki 2 input dan 2 output) . Ada 2 pembacaan untuk input, 2 pembaruan input, dan 2 penulisan untuk output baru: 120.000/6 = 20.000 transaksi/detik.

Node Arsip

Node Arsip menyediakan data blok historis dan cocok untuk pembelajaran mesin, analitik, dan aplikasi pergudangan data. Node Arsip dapat melengkapi bootstrap Node Penambangan dan untuk menjalankan pemeriksaan konsistensi berkala pada set UTXO. Pada saat penulisan, hard disk komoditas 18 TB tersedia dengan harga sekitar $350 USD. Dengan asumsi 3 GB data setiap 5 menit sama dengan 732 GB kebutuhan penyimpanan data per hari, atau sekitar 22 TB per bulan. Biaya perangkat keras selama setahun adalah 15 hard disk, dengan kapasitas 18 TB, dengan biaya tambahan tahunan sebesar $5.000 USD.

Node Agen

Skala Agen Nodes paling mudah di antara jenis node karena mereka hanya memproses jenis transaksi yang relevan untuk aplikasi yang mereka layani. Hasilnya, Agent Nodes dapat berkisar dari server web hingga perangkat IoT ringan dengan kemampuan pemrosesan dan penyimpanan terbatas, namun tetap tenang dengan blok 3 GB, atau 20.000 transaksi per detik. Misalnya, perusahaan mungkin ingin melacak penggunaan poin loyalitasnya, dibuat sebagai aset digital, dan oleh karena itu hanya perlu memilih sebagian kecil pembaruan transaksi dari setiap blok yang cocok dengan pengidentifikasi unik untuk jenis koin tersebut.

Pada saat penulisan, perangkat komputasi komersial, Raspberry Pi 4, dijual seharga sekitar $275 USD yang memiliki prosesor quadcore 1,5 GHZ dan RAM 4 GB, yang dapat digunakan untuk memfilter dengan cepat, dan membuang transaksi yang tidak relevan, dengan kecepatan 5.000 transaksi per inti. Tentu saja, ini hanyalah contoh betapa masuk akal untuk memproses blok besar, dalam aplikasi web biasa mungkin ada lebih banyak inti yang tersedia.

Kecepatan bandwidth rata-rata dari 25 negara teratas melebihi 100 MBPS, atau sekitar 10 MB/detik unduhan, dengan banyak penyedia layanan internet menawarkan unduhan tanpa batas. Persyaratan bandwidth untuk blok 3 GB setiap 5 menit adalah sekitar 10 MB/detik dengan total 22 TB per bulan. Hierarki Node Agen juga dapat dibuat untuk memfilter kebutuhan total bandwidth untuk Node Agen dengan kapasitas bandwidth yang lebih rendah.

8. Kesimpulan

Kami telah mengusulkan sistem manajemen aset digital tanpa mengandalkan kepercayaan. Kami mulai dengan blok bangunan dasar koin yang terbuat dari tanda tangan digital, yang memberikan kontrol kepemilikan yang kuat. Dari aturan dan insentif yang diperlukan, kami memperkenalkan dua metode baru untuk mengautentikasi dan melacak aset digital dalam ruang dan waktu O(1) yang konstan. Kedua metode secara independen menyediakan sistem bukti induksi matematika umum yang dapat menyandikan konfigurasi aset digital yang memungkinkan. Sistem Turing Lengkap di dalam dan melintasi batas transaksi, tanpa perlu lapisan sekunder. Radiant adalah desain terobosan yang memberikan manfaat kinerja dan paralelisme dari blockchain output transaksi yang tidak terpakai (UTXO), tetapi dengan kemampuan kontrak dari blockchain berbasis akun berdasarkan Ethereum Virtual Machine (EVM).

Referensi

[1] Satoshi Nakamoto, URL "Bitcoin: Sistem Uang Elektronik Peer-to-Peer" https://bitcoin.org/bitcoin.pdf, 2009.

[2] Vitalik Buterin, "Ethereum: Kontrak Cerdas Generasi Berikutnya dan Platform Aplikasi Terdesentralisasi." URL https://ethereum.org/en/whitepaper/, 2014.

[3] Kontributor Wikipedia. "Turing kelengkapan." Wikipedia, ensiklopedia gratis. Wikipedia, Ensiklopedia Gratis, URL https://en.wikipedia.org/wiki/Turing_completeness, 21 Juli 2022

[4] RC Merkle, "Protokol untuk kriptosistem kunci publik," Dalam Proc. 1980 Symposium on Security and Privacy, IEEE Computer Society, halaman 122-133, April 1980.

[5] Britannica, T. Editor Ensiklopedia. "induksi matematika." Ensiklopedia Britannica, URL https://www.britannica.com/science/mathematical-induction, 2018

Radiant: 一种点对点数字资产系统

Chinese version of the Radiant whitepaper

The Radiant Developers

2022年8月11日

radiantblockchain.org

摘要: Radiant网络是一种点对点数字资产系统,它使得价值的直接交换不需要经过中央方。原始的比特币[1]协议提供了创建点对点电子现金系统所需的内容,但缺乏验证交易历史记录的能力,因此无法用于验证数字资产。数字签名和输出约束提供了部分解决方案,但如果仍需要信任第三方来验证数字资产,则主要优势将会丧失。与比特币类似,Radiant网络需要最少的结构,并将交易时间戳为基于哈希的工作证明链。我们引入了两种技术来验证数字资产:唯一引用和通用归纳证明系统,两者都在恒定的O(1)时间和空间内运行。可以以任何方式组合输出,而不会影响未使用交易输出(UTXO)基础架构的固有并行性和性能特征。因此,用户可以随意离开和重新加入Radiant网络,并确保其数字资产的完整性和真实性。

1. 引言

区块链或数字账本技术(DLT)的商业应用往往依赖于发行人和保管人作为可信第三方来验证数字资产。虽然这些系统对于类似电子支付的交易足够有效,但它们仍然存在基于信任模型的固有弱点,因此不适用于高级用途。基于以太坊虚拟机(EVM)[2]的区块链非常灵活,适用于各种程序,但高额的费用使得微支付应用不切实际。

我们需要的是一个电子支付系统,它可以用作具有低费用、高性能和先进编程能力的数字资产管理系统。在本文中,我们提出了一种解决区块链扩展和合约问题的方案,使用两种新颖的技术提供唯一引用和通用归纳证明系统,使跨交易边界的图灵完备[3]程序成为可能。所提出的系统是去中心化的,使用类似比特币的工作量证明共识机制,但具有显著更高的吞吐量水平,同时提供与基于EVM的区块链相同的灵活性,费用非常低。

2. 交易

与比特币类似,我们将电子硬币定义为一串数字签名。Radiant中的交易不同之处在于,每个所有者通过数字签名上一个交易的哈希以及解锁硬币所需的输入参数,将硬币转移到下一个所有者。交易还可以创建新的输出锁定约束,其中可能包括下一个所有者的公钥,以及用户定义的任何其他规则。

图1 Radiant交易

为了验证双花,我们使用分布式时间戳服务器,使用基于哈希的工作量证明系统来组织规范历史,以确定哪个交易先到达。交易被组织成块。按照惯例,块中的第一个交易,称为“coinbase交易”,是一种特殊的交易,属于出块矿工挖到的新币。块被链接在一起,并将交易组织成Merkle树[4]。除了第一个交易外,所有交易都必须引用前一个交易,形成一个有向无环图(DAG),其中所有硬币最终都会连接回至少一个块开头的特殊交易。

图2 区块结构;交易组织成Merkle树

这种设计在数字资产的背景下存在的问题是,只有一种数字货币或数字资产,没有用户定义数字货币(或数字资产类型)的概念。这种设计对于以币本位计价单位进行电子支付类交易足够好,但并不立即适用于其他类型的数字货币或数字资产。一个常见的解决方案是引入一种服务,如交易索引器,它监视交易中的特殊数据序列以表示数字资产的创建。这种解决方案的问题在于它依赖于运行该服务的公司,需要像网络上的任何其他服务一样信任数字资产的真实性。

我们需要一种方法让用户指示创建自定义数字货币类型,但不依赖于可信服务用于数据呈现。

3. 数字资产

我们将自定义电子硬币或数字资产定义为一串数字签名。数字资产是一种使用特殊交易标记(称为“assetbase交易”)创建或铸造数字资产的用户自定义币类型。类似于coinbase交易,它将新币注入系统,assetbase交易用唯一的36字节标识符为其生命周期着色或标记硕子货币。自定义数字货币覆盖在基础数字货币类型之上,并以类似的方式运行。assetbase交易可以出现在块的任何位置,并且可以强制执行用户事先决定的任何自定义规则和约束。

图 3 代表用户定义币种类型或数字资产的交易

为了实现这一点,我们需要创建一个稳定的唯一标识符和一个交易机制来跟踪数字货币类型(数字资产)的真实性。系统的用户需要有证据证明自定义数字货币类型不是伪造的,并准确地代表数字资产。

图 4 自定义用户定义币种类型由特殊的铸币交易定义,使用唯一标识符来分类币种类型。

4. 唯一标识符

为了实现硬币类型的唯一标识符,我们使用一种特殊的标记交易,称为“assetbase交易”,它充当数字签名链的开始(铸造)。与需要新数据结构用于唯一标识符不同,我们重用交易标识符和输出索引(称为“outpoint”)作为硬币类型的唯一标识符。可以保证outpoints(36字节)是随机且全局唯一的。

一种名为OP_PUSHINPUTREF的编程指令用于附加对输出的引用。该指令接受一个36字节的参数,该参数必须与1)正在使用的输出的outpoint之一匹配,或2)在正在使用的输出之一中先前指定的OP_PUSHINPUTREF中已经出现相同的36字节值。任何给定值出现在交易输出中的唯一方法是,通过某个祖先交易,它与初始铸造assetbase交易的outpoint匹配。指定不符合任一条件的值的交易无效。

图 5. 唯一标识符通过匹配一个被花费的输出的输出点来初始化,并在至少一个被花费的输出在脚本体中包含相同的唯一标识符时维护。

这种简单的编程指令提供了一个唯一标识符,可以用作稳定引用来创建高级规则。例如,不同的数字货币类型、数字资产现在可以依赖于其他数字货币类型。由于所有数据都是本地交易,通过其直接父输入交易,客户端和服务很容易在O(1)常数时间和空间内验证数字资产的真实性,避免了对可信服务的需求。

5. 数学归纳法

可以通过另一种方式创建唯一标识符,并且还提供了一种数学归纳证明机制[5],即使用修改后的交易哈希算法。通过允许输入脚本接受正在使用的父交易,规则可以验证父交易及其祖父交易符合所需规则。明显的问题是,随着每个完整的父交易被嵌入,指数大小爆炸发生并阻止了该技术的实际使用。我们需要一种方法来压缩交易,以便可以使用固定大小的数据结构来代替派生交易哈希,而不是需要完整的交易内容。

图 6 完整的父交易验证,通过将完整的父交易嵌入到输入中进行数学归纳证明,导致事务大小呈指数级增加。

我们可以通过修改比特币中使用的交易哈希算法来实现这一点,其中从序列化交易计算双sha-256摘要,转换为新版本,该版本首先总结交易内容以派生哈希。我们引入了交易哈希算法版本3,以将其与比特币中使用的版本1和版本2区分开来。该过程是对交易的每个字段或组件进行哈希,以获得中间哈希,该中间哈希可用作固定大小的输入,从而避免了指数交易大小增长。

我们使用以下112字节的数据结构,而不是完整的序列化交易字节,然后对其进行双sha-256哈希,最终获得交易哈希。

交易版本3的哈希前像字段包括以下组成部分:

  1. nVersion(=3) of the transaction (4 byte little endian)

  2. nTotalInputs (4 byte little endian)

  3. hashPrevoutInputs (32 byte hash)

  4. hashSequence (32 byte hash)

  5. nTotalOutputs (4 byte little endian)

  6. hashOutputHashes (32 byte hash)

  7. nLocktime of the transaction (4 byte little endian)

通过使用版本3交易的交易哈希算法,我们能够在每个数学归纳证明的步骤中嵌入父交易和祖父交易,以防止交易大小增加,并可以强制执行任何所需的规则。

图 7 压缩的父交易验证,通过嵌入父交易和祖父交易的事务哈希版本3的前像数据结构来进行数学归纳证明,以强制执行任意规则和约束。

6. 网络

网络拓扑是一个近乎完全图,其中每个挖矿节点都与其他挖矿节点相连。运行网络的步骤与比特币相同,但对于不同的节点类型有所区别:挖矿节点、代理节点、存档节点。挖矿节点是块的活跃发布者,并与所有其他节点保持一致性,存档节点提供历史块数据,代理节点旨在过滤块并跟踪它们服务的应用程序感兴趣的交易。存档和代理节点可以在同一对等网络上运行,但不产生块。非挖矿节点(如存档和代理节点)有时被称为“监听器节点”,以区分它们在网络中的角色。

图 8 挖矿节点是良好连接的,并且基于彼此的区块构建。存档节点存储完整的区块以进行历史分析和引导。代理节点是监听节点,过滤和存储交易以为客户提供服务。

挖矿节点之间相互连接,形成一个近乎完全图。它们的工作是在彼此的块上建立并维护最近几百个块的共识,并维护UTXO集以防止双重支付。

代理节点只需要存储子集交易,例如特定的硬币类型或数字资产。即使对于大型块,代理节点也可以快速通过引用或特定字节序列过滤交易,然后将这些交易存储以通过应用程序编程接口提供。在合作代理之间,可以公开宣布每个块中与预定模式匹配的交易的Merkle树根哈希,以向其他代理和消费者发出信号,表明该代理已处理了哪些交易。

存档节点用于为各种应用程序创建整个块的备份副本,包括数据仓储、分析和机器学习。存档节点可以补充挖矿节点的引导,并对UTXO集运行定期一致性检查。在撰写本文时,18 TB的商品硬盘售价约为350美元。假设每5分钟有3 GB的数据需要时间戳并分布在网络上,即每天需要732 GB的数据存储量,或每月约22 TB。硬件成本为一年15个硬盘,容量为18 TB,年度增量成本为5,000美元。

7. 计算

我们考虑的情景是Radiant网络继续增长,并对挖矿、存档和代理节点的处理需求产生了哪些影响。为了比较,就比特币区块链而言,在撰写本文时,大约有8300万个未使用交易输出(unspent transaction outputs),总计约占据6GB的数据用于防止双重支付。挖矿节点只需要保留最近的几百个区块,而旧区块可以从存档节点中获取。对于代理节点来说,必须保留与其所服务应用程序相关的未使用交易输出的相应部分,扩展性是基于带宽而不是存储需求的函数。

根据我们的假设,每5分钟会产生大小为3GB的区块,这些区块将被时间戳并分布到整个网络中,即每秒大约有20,000笔交易,每笔交易的平均大小为500字节,或者每个区块大约有6,000,000笔交易。对于每种类型的节点,网络能够充分扩展以满足全球需求。这相当于地球上80亿人每5天进行一笔交易。

挖矿节点

挖矿节点是唯一构建在其他节点区块之上的节点类型。为了保持共识,只需同步未使用交易输出(UTXO)集,并仅保留最近的大约一百个区块。在撰写本文时,高性能通用固态驱动器可以实现超过120,000 IOPS,价格约为500美元,容量为280GB,因此可以处理大约每秒20,000笔交易(假设每个交易有2个输入和2个输出)。对于每笔交易,有2次读取输入、2次更新输入和2次写入新输出:120,000 / 6 = 20,000笔交易/秒。

存档节点

存档节点提供历史区块数据,适用于机器学习、分析和数据仓库应用。存档节点可以为挖矿节点提供引导,并对UTXO集进行定期一致性检查。在撰写本文时,容量为18TB的通用硬盘售价约为350美元。假设每5分钟产生3GB的数据,每天需要732GB的数据存储空间,每月约为22TB。一年的硬件成本是购买15个18TB容量的硬盘,每年的增量成本约为5,000美元。

代理节点

在节点类型中,代理节点最容易扩展,因为它们只处理与其所服务应用程序相关的交易类型。因此,代理节点可以从Web服务器扩展到具有有限处理和存储能力的轻量级物联网设备,同时仍能适应3GB的区块或每秒20,000笔交易。例如,一家公司可能希望跟踪其作为数字资产创建的忠诚度积分的使用情况,因此只需要从每个区块中选择与该币种唯一标识符匹配的一小部分交易更新。

在撰写本文时,一种商用计算设备Raspberry Pi 4售价约为275美元,它配备了四核1.5 GHz处理器和4GB的RAM,可用于快速过滤和丢弃不相关的交易,每个核心的处理速度可达5,000笔交易。当然,这只是一个例子,说明处理大区块是多么合理,而在典型的Web应用程序中可能还会有更多可用的核心。

排名前25位的国家中位带宽速度超过100 MBPS,即每秒下载约为10 MB,许多互联网服务提供商还提供无限下载服务。每5分钟3 GB区块的带宽需求约为每秒10 MB,总计每月22 TB。还可以创建代理节点的层次结构,以降低带宽容量较低的代理节点的总带宽需求。

8. 结论

我们提出了一种在不依赖信任的情况下进行数字资产管理的系统。我们从由数字签名生成的硬币这一基本构建模块开始,这提供了对所有权的强大控制。基于所需的规则和激励机制,我们引入了两种新颖的方法来在常数O(1)的时间和空间内对数字资产进行身份验证和跟踪。这两种方法都独立地提供了一个通用的数学归纳证明系统,可以编码任何可能的数字资产配置。该系统在交易边界内部和跨越交易边界具有图灵完备性,无需依赖第二层协议。Radiant是一种突破性的设计,它既具备了未使用交易输出(UTXO)区块链的性能和并行性优势,又具备了基于以太坊虚拟机(EVM)的基于账户的区块链的合约能力。

引用

[1] Satoshi Nakamoto, "Bitcoin: A Peer-to-Peer Electronic Cash System" URL https://bitcoin.org/bitcoin.pdf, 2009.

[2] Vitalik Buterin, "Ethereum: A Next-Generation Smart Contract and Decentralized Application Platform." URL https://ethereum.org/en/whitepaper/, 2014.

[3] Wikipedia contributors. "Turing completeness." Wikipedia, The Free Encyclopedia. Wikipedia, The Free Encyclopedia, URL https://en.wikipedia.org/wiki/Turing_completeness, 21 Jul. 2022

[4] R.C. Merkle, "Protocols for public key cryptosystems," In Proc. 1980 Symposium on Security and Privacy, IEEE Computer Society, pages 122-133, April 1980.

[5] Britannica, T. Editors of Encyclopaedia. "mathematical induction." Encyclopedia Britannica, URL https://www.britannica.com/science/mathematical-induction, 2018

रेडियंट: एक सहकर्मी से सहकर्मी डिजिटल संपत्ति प्रणाली

Hindi version of the Radiant whitepaper

रेडियंट डेवलपर्स

August 11, 2022

radiantblockchain.org

अमूर्त. रेडिएंट नेटवर्क एक पीयर-टू-पीयर डिजिटल एसेट सिस्टम है जो केंद्रीय पार्टी के माध्यम से बिना मूल्य के प्रत्यक्ष विनिमय को सक्षम बनाता है। मूल बिटकॉइन [1] प्रोटोकॉल प्रदान करता है जो एक सहकर्मी से सहकर्मी इलेक्ट्रॉनिक कैश सिस्टम बनाने के लिए आवश्यक है, लेकिन लेनदेन इतिहास को सत्यापित करने की क्षमता का अभाव है और इसलिए, डिजिटल संपत्ति को मान्य करने के लिए उपयोग नहीं किया जा सकता है। डिजिटल हस्ताक्षर और आउटपुट बाधाएं समाधान का हिस्सा प्रदान करती हैं, लेकिन अगर किसी विश्वसनीय तृतीय पक्ष को अभी भी डिजिटल संपत्ति को मान्य करने की आवश्यकता है तो मुख्य लाभ खो जाते हैं। बिटकॉइन के समान, रेडियंट नेटवर्क को न्यूनतम संरचना की आवश्यकता होती है, और प्रूफ-ऑफ़-वर्क की चल रही हैश-आधारित श्रृंखला में टाइमस्टैम्प लेनदेन होता है। हम डिजिटल संपत्ति को मान्य करने के लिए दो तकनीकों का परिचय देते हैं: अद्वितीय संदर्भ और एक सामान्य उद्देश्य इंडक्शन प्रूफ सिस्टम, जो दोनों निरंतर O(1) समय और स्थान में काम करते हैं। एक अप्रयुक्त लेनदेन आउटपुट (यूटीएक्सओ) आधारित आर्किटेक्चर की अंतर्निहित समांतरता और प्रदर्शन विशेषताओं से समझौता किए बिना, किसी भी तरीके से आउटपुट बनाना संभव है। इसलिए, उपयोगकर्ता अपनी इच्छानुसार रेडियंट नेटवर्क को छोड़ सकते हैं और फिर से जुड़ सकते हैं और अपनी डिजिटल संपत्ति की अखंडता और प्रामाणिकता का आश्वासन दे सकते हैं।

1. परिचय

ब्लॉकचेन, या डिजिटल लेज़र तकनीक (डीएलटी) के साथ वाणिज्य, कई मामलों में डिजिटल संपत्ति को प्रमाणित करने के लिए विश्वसनीय पार्टियों के रूप में सेवा करने वाले जारीकर्ताओं और संरक्षकों पर निर्भर करता है। हालांकि ये प्रणालियां इलेक्ट्रॉनिक भुगतान जैसे लेनदेन के लिए काफी अच्छी तरह से काम करती हैं, फिर भी वे उन्नत उपयोगों के लिए विश्वास आधारित मॉडल की अंतर्निहित कमजोरियों से ग्रस्त हैं। एथेरियम वर्चुअल मशीन (ईवीएम) [2] आधारित ब्लॉकचेन सभी प्रकार के कार्यक्रमों के लिए बहुत लचीले हैं, लेकिन उच्च शुल्क माइक्रोपेमेंट अनुप्रयोगों के लिए अव्यावहारिक है।

हमें एक इलेक्ट्रॉनिक भुगतान प्रणाली की आवश्यकता है जिसका उपयोग कम शुल्क, उच्च प्रदर्शन और उन्नत प्रोग्रामिंग क्षमताओं के साथ डिजिटल संपत्ति प्रबंधन प्रणाली के लिए किया जा सके। इस पत्र में, हम दो नई तकनीकों का उपयोग करके ब्लॉकचैन स्केलिंग और अनुबंध की समस्या का समाधान प्रस्तावित करते हैं जो अद्वितीय संदर्भ प्रदान करते हैं और एक सामान्य इंडक्शन प्रूफ सिस्टम है, जो ट्यूरिंग पूर्ण [3] प्रोग्राम को लेन-देन की सीमाओं में संभव बनाता है। प्रस्तावित प्रणाली विकेंद्रीकृत है, बिटकॉइन जैसे प्रूफ-ऑफ-वर्क सर्वसम्मति तंत्र का उपयोग करती है, लेकिन काफी उच्च स्तर के थ्रूपुट के साथ, ईवीएम-आधारित ब्लॉकचेन की समान लचीलापन प्रदान करते हुए, बहुत कम शुल्क के साथ।

2. लेनदेन

बिटकॉइन के समान, हम एक इलेक्ट्रॉनिक सिक्के को डिजिटल हस्ताक्षरों की एक श्रृंखला के रूप में परिभाषित करते हैं। जहां रेडिएंट में लेन-देन भिन्न होता है, वह यह है कि प्रत्येक मालिक सिक्के को अनलॉक करने के लिए आवश्यक इनपुट पैरामेट्स के अलावा पिछले लेनदेन के हैश पर डिजिटल रूप से हस्ताक्षर करके सिक्के को स्थानांतरित करता है। लेन-देन नई आउटपुट लॉकिंग बाधाएँ भी बनाता है, जिसमें उपयोगकर्ता द्वारा परिभाषित किसी भी अन्य नियमों के बीच अगले मालिक की सार्वजनिक कुंजी शामिल हो सकती है।

Diagram 1. रेडियंट लेनदेन.

यह सत्यापित करने के लिए कि दोहरा खर्च नहीं हुआ है, हम एक वितरित टाइमस्टैम्प सर्वर का उपयोग करते हैं, एक हैश-आधारित प्रूफ-ऑफ़-वर्क सिस्टम का उपयोग करके यह निर्धारित करने के लिए कि कौन सा लेन-देन पहले हुआ है, प्रामाणिक इतिहास को व्यवस्थित करने के लिए। लेनदेन ब्लॉकों में आयोजित किए जाते हैं। परंपरा के अनुसार, पहला लेन-देन, जिसे "कॉइनबेस ट्रांजेक्शन" कहा जाता है, एक ब्लॉक में एक विशेष लेनदेन होता है जो ब्लॉक के निर्माता के स्वामित्व वाले एक नए सिक्के को शुरू करता है। ब्लॉक एक साथ जंजीर से बंधे होते हैं और लेन-देन को एक मर्कल ट्री [4] में व्यवस्थित करते हैं। सभी लेन-देन, पहले के अपवाद के साथ, एक निर्देशित विश्वकोश ग्राफ (DAG) बनाने वाले पिछले लेन-देन का संदर्भ देना चाहिए, जहां सभी सिक्के अंततः एक ब्लॉक की शुरुआत में कम से कम एक विशेष लेनदेन से जुड़ते हैं।

Diagram 2. ब्लॉक संरचना; लेन-देन मर्कल ट्री में व्यवस्थित होते हैं.

डिजिटल संपत्ति के संदर्भ में इस डिजाइन के साथ समस्या यह है कि केवल एक प्रकार का सिक्का या डिजिटल संपत्ति है, और उपयोगकर्ता-परिभाषित सिक्कों (या डिजिटल संपत्ति प्रकार) की कोई अवधारणा नहीं है। खाते की मूल इकाई में इलेक्ट्रॉनिक भुगतान जैसे लेनदेन के लिए डिजाइन काफी अच्छी तरह से काम करता है, हालांकि यह अन्य प्रकार के सिक्कों या डिजिटल संपत्ति के लिए इस्तेमाल होने के लिए तुरंत खुद को उधार नहीं देता है। एक सामान्य समाधान एक लेनदेन इंडेक्सर जैसी सेवा शुरू करना है जो डिजिटल संपत्ति के निर्माण को दर्शाने के लिए विशेष डेटा अनुक्रमों के लिए लेनदेन की निगरानी करता है। इस समाधान के साथ समस्या यह है कि यह सेवा चलाने वाली कंपनी पर निर्भर करता है, वेब पर किसी भी अन्य सेवा की तरह, डिजिटल संपत्ति की प्रामाणिकता पर भरोसा करने की आवश्यकता होती है।

हमें उपयोगकर्ताओं के लिए कस्टम सिक्का प्रकारों के निर्माण का संकेत देने के लिए एक तरीका चाहिए, लेकिन डेटा प्रस्तुति के लिए उपयोग की जाने वाली विश्वसनीय सेवा पर भरोसा नहीं करना चाहिए।

3. डिजिटल संपत्ति

हम एक कस्टम इलेक्ट्रॉनिक सिक्के या डिजिटल संपत्ति को डिजिटल हस्ताक्षर की एक श्रृंखला के रूप में परिभाषित करते हैं। एक डिजिटल संपत्ति एक डिजिटल संपत्ति बनाने या बनाने के लिए एक विशेष लेनदेन मार्कर का उपयोग करके एक उपयोगकर्ता-परिभाषित सिक्का प्रकार है, जिसे "एसेटबेस लेनदेन" कहा जाता है। कॉइनबेस लेन-देन के समान, जो सिस्टम में नए सिक्कों को इंजेक्ट करता है, एसेटबेस ट्रांजेक्शन रंग या इलेक्ट्रॉनिक सिक्के को उसके जीवनकाल के लिए एक अद्वितीय 36-बाइट पहचानकर्ता के साथ टैग करता है। कस्टम इलेक्ट्रॉनिक सिक्के को बेस कॉइन प्रकार के शीर्ष पर ओवरले किया जाता है और समान तरीके से कार्य करता है। एसेटबेस लेन-देन ब्लॉक में कहीं भी दिखाई दे सकता है और किसी भी कस्टम नियम और बाधाओं को पहले से तय कर सकता है।

Diagram 3. उपयोगकर्ता परिभाषित सिक्का प्रकारों का प्रतिनिधित्व करने वाले लेनदेन — या डिजिटल संपत्ति.

इसे पूरा करने के लिए हमें सिक्के के प्रकार (डिजिटल संपत्ति) की प्रामाणिकता को ट्रैक करने के लिए एक स्थिर विशिष्ट पहचानकर्ता और एक लेनदेन तंत्र बनाने की आवश्यकता है। सिस्टम के उपयोगकर्ताओं को इस बात का सबूत होना चाहिए कि कस्टम सिक्का प्रकार जालसाजी नहीं हैं और डिजिटल संपत्ति का सही प्रतिनिधित्व करते हैं।

Diagram 4. कस्टम उपयोगकर्ता-परिभाषित सिक्का प्रकार एक विशेष टकसाल लेनदेन से परिभाषित होते हैं। सिक्के के प्रकार को वर्गीकृत करने के लिए एक विशिष्ट पहचानकर्ता का उपयोग किया जाता है.

4. विशिष्ट पहचानकर्ता

किसी सिक्के के प्रकार के लिए एक विशिष्ट पहचानकर्ता को लागू करने के लिए, हम एक विशेष मार्कर लेनदेन का उपयोग करते हैं, जिसे "एसेटबेस लेनदेन" कहा जाता है, जो डिजिटल हस्ताक्षरों की श्रृंखला की शुरुआत (मिंट) के रूप में कार्य करता है। अद्वितीय पहचानकर्ता के लिए एक नए डेटा-संरचना की आवश्यकता के बजाय, हम लेन-देन पहचानकर्ता और आउटपुट इंडेक्स का पुन: उपयोग करते हैं, जिसे "आउटपॉइंट" कहा जाता है, सिक्का प्रकार के लिए अद्वितीय पहचानकर्ता के रूप में। यह आश्वासन दिया जाता है कि आउटपॉइंट्स (36-बाइट्स) यादृच्छिक और विश्व स्तर पर अद्वितीय हैं।

एक प्रोग्रामिंग निर्देश, जिसे OP_PUSHINPUTREF कहा जाता है, का उपयोग आउटपुट के संदर्भ को जोड़ने के लिए किया जाता है। निर्देश ठीक एक 36-बाइट पैरामीटर को स्वीकार करता है जो या तो 1) खर्च किए जा रहे आउटपुट में से किसी एक के आउटपॉइंट से मेल खाना चाहिए, या 2) वही 36-बाइट मान पहले से निर्दिष्ट OP_PUSHINPUTREF में खर्च किए जा रहे आउटपुट में से एक में दिखाई देता है . लेन-देन आउटपुट में किसी दिए गए मूल्य के प्रकट होने का एकमात्र तरीका यह है कि, कुछ पूर्वजों के लेन-देन के माध्यम से, यह प्रारंभिक माइनिंग एसेटबेस लेनदेन से आउटपॉइंट से मेल खाता है। लेन-देन जो एक मान निर्दिष्ट करते हैं जो किसी भी शर्त को पूरा नहीं करते हैं, अमान्य हैं।

Diagram 5. अद्वितीय पहचानकर्ताओं को खर्च किए जा रहे आउटपुट में से किसी एक के आउटपॉइंट से मिलान करके प्रारंभ किया जाता है, और तब तक बनाए रखा जाता है जब तक कि खर्च किए जा रहे आउटपुट में से कम से कम एक में स्क्रिप्ट बॉडी में एक ही अद्वितीय पहचानकर्ता होता है.

यह सरल प्रोग्रामिंग निर्देश एक विशिष्ट पहचानकर्ता प्रदान करता है जिसे उन्नत नियम बनाने के लिए एक स्थिर संदर्भ के रूप में उपयोग किया जा सकता है। उदाहरण के लिए, विभिन्न प्रकार के सिक्के, डिजिटल संपत्ति, अब अन्य प्रकार के सिक्कों पर निर्भर हो सकते हैं। चूंकि सभी डेटा लेन-देन के लिए स्थानीय है, इसके तत्काल मूल इनपुट लेनदेन के माध्यम से, ग्राहकों और सेवाओं के लिए O(1) निरंतर समय और स्थान में डिजिटल संपत्ति की प्रामाणिकता को मान्य करना आसान है, एक विश्वसनीय सेवा की आवश्यकता से परहेज .

5. प्रेरण द्वारा सबूत

एक वैकल्पिक तरीके से विशिष्ट पहचानकर्ता बनाना संभव है और एक संशोधित लेनदेन हैश एल्गोरिथम का उपयोग करके गणितीय प्रेरण [5] प्रमाण के लिए एक तंत्र भी प्रदान करता है। इनपुट स्क्रिप्ट को खर्च किए जा रहे मूल लेन-देन को स्वीकार करने की अनुमति देकर, नियम यह सत्यापित कर सकते हैं कि माता-पिता और उसके ग्रैंड-पैरेंट आवश्यक नियमों के अनुरूप हैं। स्पष्ट समस्या यह है कि मूल लेन-देन की प्रत्येक पूर्ण प्रतिलिपि एम्बेडेड होने के कारण, एक घातीय आकार विस्फोट होता है और तकनीक के व्यावहारिक उपयोग को रोकता है। लेनदेन को संपीड़ित करने का एक तरीका आवश्यक है, ताकि पूर्ण लेनदेन सामग्री की आवश्यकता के बजाय लेनदेन हैश प्राप्त करने के बजाय एक निश्चित आकार के डेटा-संरचना का उपयोग किया जा सके।

Diagram 6. पूर्ण माता-पिता लेनदेन सत्यापन, इनपुट में पूर्ण मूल लेनदेन को एम्बेड करके गणितीय प्रेरण प्रमाण जिसके परिणामस्वरूप घातीय लेनदेन आकार में वृद्धि हुई है.

हम बिटकॉइन में उपयोग किए जाने वाले लेन-देन हैश एल्गोरिथ्म को संशोधित करके इसे पूरा कर सकते हैं, जिसमें एक डबल शा-256 डाइजेस्ट की गणना क्रमबद्ध लेनदेन से की जाती है, एक नए संस्करण में जो पहले हैश प्राप्त करने के लिए लेनदेन सामग्री को सारांशित करता है। हम बिटकॉइन में संस्करण 1 और संस्करण 2 के उपयोग से इसे अलग करने के लिए लेन-देन हैश एल्गोरिथम संस्करण 3 पेश करते हैं। प्रक्रिया एक मध्यवर्ती हैश के लिए प्रत्येक क्षेत्र, या एक लेनदेन के घटक को हैश करना है, जिसे एक निश्चित आकार के इनपुट के रूप में इस्तेमाल किया जा सकता है और इस तरह घातीय लेनदेन के आकार में वृद्धि से बचा जा सकता है।

हम निम्नलिखित 112-बाइट डेटा-संरचना का उपयोग करते हैं, पूर्ण क्रमबद्ध लेन-देन बाइट्स के बजाय, जो अंत में लेनदेन हैश प्राप्त करने के लिए डबल sha-256 हैशेड है।

लेन-देन संस्करण 3 हैश प्रीइमेज फ़ील्ड:

  1. लेन-देन का nVersion(=3) (4 बाइट थोड़ा एंडियन)

  2. nTotalInputs (4 बाइट थोड़ा एंडियन)

  3. हैशप्रेवाउटइनपुट्स (32 बाइट हैश)

  4. हैशसीक्वेंस (32 बाइट हैश)

  5. nTotalOutputs (4 बाइट थोड़ा एंडियन)

  6. हैशऑटपुटहैश (32 बाइट हैश)

  7. लेन-देन का लॉकटाइम (4 बाइट लिटिल एंडियन)

संस्करण 3 के लेन-देन के लिए लेन-देन हैश एल्गोरिथ्म का उपयोग करके, हम लेन-देन के आकार में वृद्धि को रोकने के लिए गणितीय प्रेरण प्रमाण के प्रत्येक चरण में माता-पिता और दादा-दादी के लेनदेन को एम्बेड करने में सक्षम हैं, और आवश्यक नियमों को लागू कर सकते हैं।

Diagram 7. कंप्रेस्ड पेरेंट ट्रांजेक्शन वैलिडेशन, मैथमैटिकल इंडक्शन प्रूफ एंबेडिंग द्वारा ट्रांजैक्शन हैश वर्जन 3 प्रीइमेज डेटा-स्ट्रक्चर ऑफ पेरेंट एंड ग्रैंड-पैरेंट ताकि मनमाने नियमों और बाधाओं को लागू किया जा सके.

6. नेटवर्क

नेटवर्क टोपोलॉजी एक लगभग पूरा ग्राफ है, जिसमें हर माइनिंग नोड हर दूसरे माइनिंग नोड से जुड़ा होता है। नेटवर्क चलाने के चरण बिटकॉइन के समान हैं, विभिन्न नोड प्रकारों के लिए कुछ अंतरों के साथ: माइनिंग नोड्स, एजेंट नोड्स, आर्काइव नोड्स। खनन नोड्स ब्लॉक के सक्रिय प्रकाशक हैं और अन्य सभी नोड्स के साथ आम सहमति बनाए रखते हैं, आर्काइव नोड्स ऐतिहासिक ब्लॉक डेटा की सेवा करते हैं, और एजेंट नोड्स ब्लॉक को फ़िल्टर करने और उनके द्वारा प्रदान किए जाने वाले अनुप्रयोगों के हित के लेन-देन को ट्रैक करने के लिए डिज़ाइन किए गए हैं। आर्काइव और एजेंट नोड एक ही पीयर-टू-पीयर नेटवर्क पर काम कर सकते हैं और फिर भी ब्लॉक नहीं बनाते हैं। गैर-खनन नोड्स जैसे आर्काइव और एजेंट नोड्स को कभी-कभी नेटवर्क में उनकी भूमिका को अलग करने के लिए "श्रोता नोड्स" के रूप में संदर्भित किया जाता है।

Diagram 8. खनन नोड अच्छी तरह से जुड़े हुए हैं और एक दूसरे के ब्लॉक के शीर्ष पर बने हैं। ऐतिहासिक विश्लेषण और बूटस्ट्रैपिंग उद्देश्यों के लिए आर्काइव नोड्स पूरे ब्लॉक को स्टोर करते हैं। एजेंट नोड श्रोता नोड होते हैं जो ग्राहकों की सेवा के लिए लेनदेन को फ़िल्टर और स्टोर करते हैं.

खनन नोड्स अन्य खनन नोड्स के बीच लगभग पूर्ण ग्राफ में अच्छी तरह से जुड़े हुए हैं। उनका काम एक दूसरे के ब्लॉक के शीर्ष पर निर्माण करना और हाल के कुछ सौ ब्लॉकों के लिए आम सहमति बनाए रखना है, और दोहरे खर्च की रोकथाम के लिए UTXO सेट को बनाए रखना है।

एजेंट नोड्स को केवल एक सबसेट लेनदेन को स्टोर करने की आवश्यकता होती है, उदाहरण के लिए, एक विशिष्ट सिक्का प्रकार या डिजिटल संपत्ति। बड़े ब्लॉक के साथ भी, एक एजेंट नोड संदर्भों या विशिष्ट बाइट-अनुक्रमों द्वारा लेन-देन को जल्दी से फ़िल्टर कर सकता है, फिर उन लेनदेन को एप्लिकेशन प्रोग्रामर इंटरफ़ेस के माध्यम से सेवा देने के लिए संग्रहीत करता है। सहयोगी एजेंटों के बीच, प्रत्येक ब्लॉक में लेन-देन के लिए एक मर्कल ट्री रूट हैश की सार्वजनिक रूप से घोषणा की जा सकती है जो अन्य एजेंटों और उपभोक्ताओं को संकेत देने के लिए पूर्व-निर्धारित पैटर्न से मेल खाता है कि एजेंट ने कौन से लेनदेन को संसाधित किया है।

डेटा वेयरहाउसिंग, एनालिटिक्स और मशीन लर्निंग सहित विभिन्न अनुप्रयोगों के लिए आर्काइव नोड्स का उपयोग पूरे ब्लॉक की बैकअप प्रतियां बनाने के लिए किया जाता है। चूंकि पुरालेख नोड सीधे खनन में शामिल नहीं होते हैं, इसलिए उनके पास खनन या एजेंट नोड्स के समान वास्तविक समय प्रदर्शन और बैंडविड्थ आवश्यकताएं नहीं होती हैं।

7. गणना

हम उस परिदृश्य पर विचार करते हैं जहां रेडियंट नेटवर्क बढ़ता रहता है और यह खनन, संग्रह और एजेंट नोड्स की प्रसंस्करण आवश्यकताओं के लिए क्या आवश्यक है। तुलना के लिए, लेखन के समय, बिटकॉइन ब्लॉकचेन के लिए लगभग 83 मिलियन अव्ययित लेनदेन आउटपुट हैं, दोहरे खर्च को रोकने के लिए कुल लगभग 6 जीबी आवश्यक डेटा के लिए। यह केवल आवश्यक है कि सबसे हाल के कुछ सौ ब्लॉकों को खनन नोड्स द्वारा रखा जाए, पुराने ब्लॉक आर्काइव नोड्स से उपलब्ध हैं। एजेंट नोड्स के लिए, अव्ययित लेन-देन आउटपुट के प्रासंगिक विभाजन को रखना आवश्यक है जो उनके द्वारा प्रदान किए जाने वाले अनुप्रयोगों से संबंधित है, स्केलिंग बैंडविड्थ का एक कार्य है न कि भंडारण आवश्यकताओं का।

हमारे उद्देश्यों के लिए, हम मानते हैं कि हर 5 मिनट में 3 जीबी आकार के ब्लॉक होंगे जिन्हें टाइमस्टैम्प किया जाएगा और पूरे नेटवर्क में वितरित किया जाएगा, या 500 बाइट्स के औसत लेनदेन आकार के साथ प्रति सेकंड लगभग 20,000 लेनदेन, या प्रति ब्लॉक लगभग 6,000,000 लेनदेन होंगे। हम दिखाते हैं कि प्रत्येक प्रकार के नोड के लिए, वैश्विक मांग को पूरा करने के लिए नेटवर्क पर्याप्त रूप से स्केल करने में सक्षम है। यह ग्रह पर 8 अरब लोगों में से प्रत्येक के लिए प्रत्येक 5 दिनों में लगभग 1 लेन-देन के बराबर है।

माइनिंग नोड्स

माइनिंग नोड एकमात्र नोड प्रकार हैं जो एक दूसरे के ब्लॉक के ऊपर निर्मित होते हैं। आम सहमति बनाए रखने के लिए, खर्च न किए गए लेन-देन आउटपुट (यूटीएक्सओ) सेट को सिंक्रनाइज़ करना और केवल लगभग पिछले सौ ब्लॉकों को बनाए रखना पर्याप्त है। लेखन के समय, उच्च प्रदर्शन कमोडिटी सॉलिड-स्टेट ड्राइव 120,000 IOPS से ऊपर की ओर प्राप्त करने में सक्षम हैं, जिसकी लागत 280 GB के लिए लगभग $500 USD है, और इसलिए प्रति सेकंड लगभग 20,000 लेनदेन को संभाल सकती है (यह मानते हुए कि प्रत्येक लेनदेन में 2 इनपुट और 2 आउटपुट हैं) . इनपुट के लिए 2 रीड, इनपुट के लिए 2 अपडेट और नए आउटपुट के लिए 2 राइट हैं: 120,000/6 = 20,000 लेनदेन/सेकंड।

संग्रह नोड्स

आर्काइव नोड्स ऐतिहासिक ब्लॉक डेटा प्रदान करते हैं और मशीन लर्निंग, एनालिटिक्स और डेटा वेयरहाउसिंग एप्लिकेशन के लिए उपयुक्त हैं। आर्काइव नोड्स खनन नोड्स के बूटस्ट्रैपिंग को पूरक कर सकते हैं और यूटीएक्सओ सेट पर आवधिक स्थिरता जांच चला सकते हैं। लेखन के समय, 18 टीबी की कमोडिटी हार्ड डिस्क लगभग $350 USD में उपलब्ध हैं। मान लें कि हर 5 मिनट में 3 जीबी डेटा प्रतिदिन 732 जीबी डेटा स्टोरेज की आवश्यकता के बराबर है, या लगभग 22 टीबी प्रति माह। $5,000 USD की वार्षिक वृद्धिशील लागत के लिए, एक वर्ष के लिए हार्डवेयर लागत 15 हार्ड ड्राइव है, जिसमें 18 टीबी क्षमता है।

एजेंट नोड्स

एजेंट नोड्स नोड प्रकारों के बीच सबसे आसानी से स्केल करते हैं क्योंकि वे केवल उनके द्वारा प्रदान किए जाने वाले अनुप्रयोगों के लिए प्रासंगिक लेनदेन प्रकारों को संसाधित करते हैं। परिणामस्वरूप एजेंट नोड्स एक वेब सर्वर से लेकर सीमित प्रोसेसिंग और स्टोरेज क्षमता वाले हल्के IoT डिवाइस तक हो सकते हैं, और फिर भी 3 जीबी ब्लॉक, या प्रति सेकंड 20,000 लेनदेन के साथ शांति बनाए रख सकते हैं। उदाहरण के लिए, एक कंपनी डिजिटल संपत्ति के रूप में बनाए गए अपने लॉयल्टी पॉइंट्स के उपयोग को ट्रैक करना चाह सकती है, और इसलिए प्रत्येक ब्लॉक से लेन-देन अपडेट का एक छोटा सा उपसमुच्चय चुनने की आवश्यकता होती है जो उस सिक्के के प्रकार के लिए विशिष्ट पहचानकर्ता से मेल खाता हो।

लेखन के समय, एक वाणिज्यिक कंप्यूटिंग डिवाइस, रास्पबेरी पाई 4, लगभग $275 यूएसडी में बिकता है जिसमें क्वाडकोर 1.5 गीगाहर्ट्ज प्रोसेसर और 4 जीबी रैम है, जिसका उपयोग त्वरित रूप से फ़िल्टर करने और अप्रासंगिक लेनदेन को त्यागने के लिए किया जा सकता है। प्रति कोर 5,000 लेनदेन। बेशक, यह सिर्फ एक उदाहरण है कि बड़े ब्लॉकों को संसाधित करना कितना उचित है, एक विशिष्ट वेब एप्लिकेशन में कई और कोर उपलब्ध हो सकते हैं।

शीर्ष 25 देशों की औसत बैंडविड्थ गति 100 एमबीपीएस या लगभग 10 एमबी/सेकंड डाउनलोड से अधिक है, जिसमें कई इंटरनेट सेवा प्रदाता असीमित डाउनलोड की पेशकश करते हैं। प्रत्येक 5 मिनट में 3 जीबी ब्लॉक के लिए बैंडविड्थ की आवश्यकता कुल 22 टीबी प्रति माह के लिए लगभग 10 एमबी/सेकंड है। कम बैंडविड्थ क्षमता वाले एजेंट नोड्स के लिए कुल बैंडविड्थ आवश्यकताओं को फ़िल्टर करने के लिए एजेंट नोड्स के पदानुक्रम भी बनाए जा सकते हैं।

8. निष्कर्ष

हमने भरोसे पर भरोसा किए बिना डिजिटल संपत्ति प्रबंधन के लिए एक प्रणाली प्रस्तावित की है। हमने डिजिटल सिग्नेचर से बने सिक्कों के बुनियादी बिल्डिंग ब्लॉक्स के साथ शुरुआत की, जो स्वामित्व का मजबूत नियंत्रण प्रदान करता है। आवश्यक नियमों और प्रोत्साहनों से, हमने निरंतर O(1) समय और स्थान में डिजिटल संपत्ति को प्रमाणित करने और ट्रैक करने के लिए दो नए तरीके पेश किए। दोनों विधियाँ स्वतंत्र रूप से एक सामान्य गणितीय इंडक्शन प्रूफ सिस्टम प्रदान करती हैं जो किसी भी संभावित डिजिटल एसेट कॉन्फ़िगरेशन को एनकोड कर सकता है। सिस्टम द्वितीयक परतों की आवश्यकता के बिना लेनदेन सीमाओं के भीतर और उसके पार ट्यूरिंग पूर्ण है। दीप्तिमान एक सफल डिजाइन है जो खर्च न किए गए लेनदेन आउटपुट (यूटीएक्सओ) ब्लॉकचैन का प्रदर्शन और समानता लाभ प्रदान करता है, लेकिन एथेरियम वर्चुअल मशीन (ईवीएम) पर आधारित खाता-आधारित ब्लॉकचेन की अनुबंध क्षमता के साथ।

सन्दर्भ

[1] सतोशी नाकामोटो, "बिटकॉइन: ए पीयर-टू-पीयर इलेक्ट्रॉनिक कैश सिस्टम" URL https://bitcoin.org/bitcoin.pdf, 2009.

[2] विटालिक ब्यूटिरिन, "एथेरियम: ए नेक्स्ट-जेनरेशन स्मार्ट कॉन्ट्रैक्ट एंड डिसेंट्रलाइज्ड एप्लीकेशन प्लेटफॉर्म।" URL https://ethereum.org/en/whitepaper/, 2014.

[3] विकिपीडिया योगदानकर्ता। "ट्यूरिंग पूर्णता।" विकिपीडिया, मुक्त विश्वकोश। विकिपीडिया, मुक्त विश्वकोश, URL https://en.wikipedia.org/wiki/Turing_completeness, 21 Jul. 2022

[4] आर.सी. मेर्कले, "सार्वजनिक कुंजी क्रिप्टो सिस्टम के लिए प्रोटोकॉल," In Proc. 1980 Symposium on Security and Privacy, IEEE Computer Society, pages 122-133, April 1980.

[5] ब्रिटानिका, टी. एनसाइक्लोपीडिया के संपादक। "गणितीय प्रेरण।" एनसाइक्लोपीडिया ब्रिटानिका, URL https://www.britannica.com/science/mathematical-induction, 2018

Diagram 1. Radiant Transactions.
Diagram 2. Block Structure; transactions are organized into a Merkle Tree.
Diagram 3. Transactions representing user-defined coin types — or digital assets.
Diagram 4. Custom user-defined coin types are defined from a special mint transaction. A unique identifier is used to classify the coin type.
Diagram 5. Unique identifiers are initialized by matching an outpoint of one of the outputs being spent, and then maintained as long as at least one of the outputs being spent contains the same unique identifier in the script body.
Diagram 6. Full parent transaction validation, mathematical induction proof by embedding the full parent transactions into the inputs resulting in exponential transaction size increase.
Diagram 7. Compressed parent transaction validation, mathematical induction proof by embedding the transaction hash version 3 preimage data-structure of the parent and grand-parent to enforce arbitrary rules and constraints.
Diagram 8. Mining Nodes are well-connected and build on top of each other's blocks. Archive nodes store complete blocks for historical analysis and bootstrapping purposes. Agent nodes are listener nodes which filter and store transactions to serve clients.
Transferring control of a token contract
Positioning of Radiant relative to popular blockchains.
Diagram 1. Token replay attack
Diagram 2. Token man-in-the-middle forgery attack
Diagram 4. Transfer transaction OP_PUSHINPUTREF reference must match one of the previous outputs scripts being spent.
Diagram 5. Invalid TX with no matching previous output script or outpoint.
Diagram 6. OP_REQUIREINPUTREF functions the same as OP_PUSHINPUTREF but without pushing the reference to the current output.
Diagram 7. OP_DISALLOWPUSHINPUTREF disallows usage of a specific reference.
Diagram 8. OP_DISALLOWPUSHINPUTREFSIBLING: Disallow usage of a specific reference in all other outputs than the one in which this instruction appears.
Diagram 9. OP_REFHASHDATASUMMARY_UTXO: Output coloring diagram
Diagram 10. OP_REFHASHDATASUMMARY_UTXO: Push to stack the summary of an input being spent.
Diagram 11. OP_REFHASHVALUESUM_UTXOS: Push to stack the total sum of the inputs which match a specific reference hash
Diagram 12. Verify the parent and grand parent to identify forgery.
Diagram 13. Transaction Contents with Embedded Parents
Diagram 15. Transaction Identifier Version 3 Preimages with Embedded Parents
Diagram 17. Non-Fungible Token Design Overview.
Diagram 18. Non-Fungible Token Output Design

[1] Satoshi Nakamoto, "Bitcoin: A Peer-to-Peer Electronic Cash System" , 2009.

Peer-to-peer electronic cash was the first real use case of blockchain technology. But in recent years, smart contracts have grown in popularity. These smart contracts allow people to use the security that blockchains such as Bitcoin, Radiant and Ethereum offer and apply it to use cases other than cash. Especially Decentralised Finance (DeFi) applications such as , and have skyrocketed.

For support please join the Radiant community .

For more information including code for the mining smart contract see the .

Guide documentation sourced from

Diagrama 1. Transacciones en Radiant.
Diagrama 2. Estructura de Bloques; las transacciones se organizan en un árbol de Merkle.
Diagrama 3. Transacciones que representan tipos de moneda definidos por el usuario — o activos digitales.
Diagrama 4. Los tipos de moneda definidos por el usuario se definen a partir de una transacción especial de la Fábrica de Moneda. Se utiliza un identificador único para clasificar el tipo de moneda.
Diagrama 5. Los identificadores únicos se inician al coincidir con un punto de salida de una de las salidas que se gastan, y luego se mantienen mientras al menos una de las salidas que se gastan contenga el mismo identificador único en el cuerpo del script.
Diagrama 6. Validación de la transacción completa de los padres, prueba de inducción matemática mediante la incorporación de las transacciones completas de los padres en las entradas, lo que resulta en un aumento exponencial del tamaño de la transacción.
Diagrama 7. Validación comprimida de la transacción de los padres, prueba de inducción matemática mediante la incrustación de la estructura de datos de preimagen de la versión 3 del hash de la transacción de los padres y los abuelos para hacer cumplir reglas y restricciones arbitrarias.
Diagrama 8. Los nodos de minería están bien conectados y construyen sobre los bloques de los demás. Los nodos de archivo almacenan bloques completos para fines de análisis histórico y de arranque. Los nodos de agente son nodos de escucha que filtran y almacenan transacciones para servir a los clientes.

If you're interested to see what kind of things can be built with CashScript, you can look at the or . If you just want to dive into CashScript, refer to the and other pages in the documentation.

The cashc command line interface is used to compile CashScript .cash files into .json artifact files. These artifacts can be imported and used by the JavaScript SDK or other libraries / applications that use CashScript. For more information on this artifact format refer to .

There are some examples available on the , that can be used to take inspiration from. Further examples of the JavaScript integration can be found on . A simple example is included below.

Tip: Read more about the CashScript language syntax in the .

Tip: Read more about the JavaScript SDK in the .

For Visual Studio Code a dedicated was developed by community contributor . This plugin works with .cash files and supports syntax highlighting, autocompletion, snippets, linting and even integrated compilation.

The most popular Solidity plugin for Sublime Text 2/3 is . Install this plugin with , open a .cash file and set Solidity as the syntax language in the Sublime menu bar:

The most popular Solidity plugin for Atom is . Install this plugin and add the following snippet to your Config file:

Bitcoin covenants were first proposed in a paper titled , but several other proposals have been created over the years. In May of 2022 Bitcoin Cash implemented so-called Native Introspection which enables efficient and accessible covenants.

One interesting technique in Bitcoin Cash is called blind escrow, meaning that funds are placed in an escrow contract. This contract can only release the funds to one of the escrow participants, and has no other control over the funds. Non-custodial local exchange uses OP_CHECKDATASIG to do this, but we can also achieve something similar by restricting recipients with a covenant.

Besides these wasted days it can also be inconvenient to claim at set intervals, rather than the "streaming" model that the Ethereum project employs. Instead of set intervals, you should be able to claim funds at any time during the "money stream". Using simulated state, we can approach a similar system with BCH.

A drawback of using this "simulated state" method is that every new stream is a new contract with its own address. So additional abstractions are needed to provide a clear frontend layer for a system like this. Simulated state can be used to create much more sophisticated systems and is the main idea that powers complex solutions such as the offline payments card , and the Proof-of-Work SLP token .

A final way to restrict outputs is adding OP_RETURN outputs to the mix. This is necessary when you want to make any SLP-based covenants, such as or the . The integration of SLP and CashScript is still in its early stages though, so that is a topic for a more advanced guide.

Right now we'll use a simpler (but also less useful) example, where we restrict a smart contract to only being able to post on the on-chain social media platform . For this we use LockingBytecodeNullData, which works slightly different than the other LockingBytecode objects. While regular outputs have a locking script, OP_RETURN outputs can include different chunks of data. This is why LockingBytecode instead takes a list of bytes chunks.

We have discussed the main uses for covenants as they exist on Bitcoin Cash today. We've seen how we can achieve different use case by combining transaction output restrictions to P2SH and P2PKH outputs. We also touched on more advanced subjects such as simulated state and OP_RETURN outputs. Covenants are the main differentiating factor for BCH smart contracts when compared to BTC, while keeping the same efficient stateless verification. If you're interested in learning more about the differences in smart contracts among BCH, BTC and ETH, read the article .

https://bitcoin.org/bitcoin.pdf
Maker
Uniswap
Aave
Discord server
Glyph Miner GitHub
GitHub
Showcase
Examples
Getting Started page
About RXD
Command Line Interface
Getting Started
Guides
Language
SDK
Releases
npm install -g cashc
Usage: cashc [options] [source_file]

Options:
  -V, --version        Output the version number.
  -o, --output <path>  Specify a file to output the generated artifact.
  -h, --hex            Compile the contract to hex format rather than a full artifact.
  -A, --asm            Compile the contract to ASM format rather than a full artifact.
  -c, --opcount        Display the number of opcodes in the compiled bytecode.
  -s, --size           Display the size in bytes of the compiled bytecode.
  -?, --help           Display help
OP_PUSHINPUTREFSINGLETON <tokenRef> OP_DROP <p2pkh>
OP_REQUIREINPUTREF <contractRef>
<contractScriptSigHash> OP_2DROP // Hash256 of contract's script sig
OP_STATESEPARATOR
OP_PUSHINPUTREFSINGLETON <tokenRef> OP_DROP <p2pkh> // Standard token script
<hashIndex> // Position of ref + hash in token output
<refIndex> // Index of ref in token output data summary
<outputIndex> // Output index of token
// Contract ref
OP_PUSHINPUTREFSINGLETON <contractRef> OP_DROP
// Check token ref exists in the token output
OP_DUP OP_ROT OP_REFDATASUMMARY_OUTPUT OP_SWAP 36 OP_MUL OP_SPLIT OP_NIP 32 OP_SPLIT OP_DROP <tokenRef> OP_DUP OP_ROT OP_EQUALVERIFY
// Get ref + hash in token output script
OP_ROT OP_STATESCRIPTBYTECODE_OUTPUT OP_SWAP OP_SPLIT OP_NIP 69 OP_SPLIT OP_DROP
// Compare ref + hash in token output to ref + hash of this script's unlocking code
OP_SWAP 32 OP_INPUTINDEX OP_INPUTBYTECODE OP_HASH256 OP_CAT OP_CAT OP_EQUALVERIFY
// Propagate the contract
OP_INPUTINDEX OP_CODESCRIPTBYTECODE_UTXO OP_HASH256 OP_CODESCRIPTHASHOUTPUTCOUNT_OUTPUTS 1 OP_NUMEQUALVERIFY
//
// Contract code...
// 
npm install -g cashc
npm install cashscript
pragma cashscript ^0.7.0;

contract TransferWithTimeout(pubkey sender, pubkey recipient, int timeout) {
    // Allow the recipient to claim their received money
    function transfer(sig recipientSig) {
        require(checkSig(recipientSig, recipient));
    }

    // Allow the sender to reclaim their sent money after the timeout is reached
    function timeout(sig senderSig) {
        require(checkSig(senderSig, sender));
        require(tx.time >= timeout);
    }
}
cashc ./transfer_with_timeout.cash --output ./transfer_with_timeout.json
const { ElectrumNetworkProvider, Contract, SignatureTemplate } = require('cashscript');
const { alice, bob, alicePk, bobPk } = require('./keys');

async function run() {
  // Import the TransferWithTimeout JSON artifact
  const artifact = require('./transfer_with_timeout.json');

  // Initialise a network provider for network operations
  const provider = new ElectrumNetworkProvider('mainnet');

  // Instantiate a new TransferWithTimeout contract
  const contract = new Contract(artifact, [alicePk, bobPk, 600000], provider);

  // Call the transfer function with Bob's signature
  // i.e. Bob claims the money that Alice has sent him
  const transferDetails = await contract.functions
    .transfer(new SignatureTemplate(bob))
    .to('bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx', 10000)
    .send();
  console.log(transferDetails);

  // Call the timeout function with Alice's signature
  // i.e. Alice recovers the money that Bob has not claimed
  const timeoutDetails = await contract.functions
    .timeout(new SignatureTemplate(alice))
    .to('bitcoincash:qqeht8vnwag20yv8dvtcrd4ujx09fwxwsqqqw93w88', 10000)
    .send();
  console.log(timeoutDetails);
}

This associates `.cash` files with Solidity, and enables syntax highlighting for your CashScript files.

## Vim
The most popular Solidity plugin for Vim is [vim-solidity](https://github.com/TovarishFin/vim-solidity). Install this plugin and add the following snippet to your `.vimrc`:

```bash title=".vimrc"
au BufRead,BufNewFile *.cash setfiletype solidity
contract Escrow(bytes20 arbiter, bytes20 buyer, bytes20 seller) {
    function spend(pubkey pk, sig s) {
        require(hash160(pk) == arbiter);
        require(checkSig(s, pk));

        // Check that the correct amount is sent
        int minerFee = 1000; // hardcoded fee
        int amount = tx.inputs[this.activeInputIndex].value - minerFee;
        require(tx.outputs[0].value == amount);

        // Check that the transaction sends to either the buyer or the seller
        bytes25 buyerLock = new LockingBytecodeP2PKH(buyer);
        bytes25 sellerLock = new LockingBytecodeP2PKH(seller);
        bool sendsToBuyer = tx.outputs[0].lockingBytecode == buyerLock;
        bool sendsToSeller = tx.outputs[0].lockingBytecode == sellerLock;
        require(sendsToBuyer || sendsToSeller);
    }
}
contract LastWill(bytes20 inheritor, bytes20 cold, bytes20 hot) {
    function inherit(pubkey pk, sig s) {
        require(tx.age >= 180 days);
        require(hash160(pk) == inheritor);
        require(checkSig(s, pk));
    }

    function cold(pubkey pk, sig s) {
        require(hash160(pk) == cold);
        require(checkSig(s, pk));
    }

    function refresh(pubkey pk, sig s) {
        require(hash160(pk) == hot);
        require(checkSig(s, pk));

        // Check that the correct amount is sent
        int minerFee = 1000; // hardcoded fee
        int amount = tx.inputs[this.activeInputIndex].value - minerFee;
        require(tx.outputs[0].value == amount);

        // Check that the funds are sent back to the contract
        bytes23 contractLock = tx.inputs[this.activeInputIndex].lockingBytecode;
        require(tx.outputs[0].lockingBytecode == contractLock);
    }
}
contract Mecenas(bytes20 recipient, bytes20 funder, int pledge, int period) {
    function receive() {
        require(tx.age >= period);

        // Check that the first output sends to the recipient
        bytes25 recipientLockingBytecode = new LockingBytecodeP2PKH(recipient);
        require(tx.outputs[0].lockingBytecode == recipientLockingBytecode);

        // Calculate the value that's left
        int minerFee = 1000;
        int currentValue = tx.inputs[this.activeInputIndex].value;
        int changeValue = currentValue - pledge - minerFee;

        // If there is not enough left for *another* pledge after this one,
        // we send the remainder to the recipient. Otherwise we send the
        // pledge to the recipient and the change back to the contract
        if (changeValue <= pledge + minerFees) {
            require(tx.outputs[0].value == currentValue - minerFee);
        } else {
            require(tx.outputs[0].value == pledge);
            bytes changeBytecode = tx.inputs[this.activeInputIndex].lockingBytecode;
            require(tx.outputs[1].lockingBytecode == changeBytecode);
            require(tx.outputs[1].value == changeValue);
        }
    }

    function reclaim(pubkey pk, sig s) {
        require(hash160(pk) == funder);
        require(checkSig(s, pk));
    }
}
contract Mecenas(
    bytes20 recipient,
    bytes20 funder,
    int pledgePerBlock,
    bytes8 initialBlock,
) {
    function receive() {
        // Check that the first output sends to the recipient
        bytes25 recipientLockingBytecode = new LockingBytecodeP2PKH(recipient);
        require(tx.outputs[0].lockingBytecode == recipientLockingBytecode);

        // Check that time has passed and that time locks are enabled
        int initial = int(initialBlock);
        require(tx.time >= initial);

        // Calculate the amount that has accrued since last claim
        int passedBlocks = tx.locktime - initial;
        int pledge = passedBlocks * pledgePerBlock;

        // Calculate the leftover amount
        int minerFee = 1000;
        int currentValue = tx.inputs[this.activeInputIndex].value;
        int changeValue = currentValue - pledge - minerFee;

        // If there is not enough left for *another* pledge after this one,
        // we send the remainder to the recipient. Otherwise we send the
        // remainder to the recipient and the change back to the contract
        if (changeValue <= pledge + minerFee) {
            require(tx.outputs[0].value == currentValue - minerFee);
        } else {
            // Check that the outputs send the correct amounts
            require(tx.outputs[0].value == pledge);
            require(tx.outputs[1].value == changeValue);

            // Cut out old initialBlock (OP_PUSHBYTES_8 <initialBlock>)
            // Insert new initialBlock (OP_PUSHBYTES_8 <tx.locktime>)
            // Note that constructor parameters are added in reverse order,
            // so initialBlock is the first statement in the contract bytecode.
            bytes newContract = 0x08 + bytes8(tx.locktime) + this.activeBytecode.split(9)[1];

            // Create the locking bytecode for the new contract and check that
            // the change output sends to that contract
            bytes23 newContractLock = new LockingBytecodeP2SH(hash160(newContract));
            require(tx.outputs[1].lockingBytecode == newContractLock);
        }
    }

    function reclaim(pubkey pk, sig s) {
        require(hash160(pk) == funder);
        require(checkSig(s, pk));
    }
}
pragma cashscript ^0.7.0;

// This contract enforces making an announcement on Memo.cash and sending the
// remaining balance back to the contract.
contract Announcement() {
    function announce() {
        // Create the memo.cash announcement output
        bytes announcement = new LockingBytecodeNullData([
            0x6d02,
            bytes('A contract may not injure a human being or, '
             + 'through inaction, allow a human being to come to harm.')
        ]);

        // Check that the first tx output matches the announcement
        require(tx.outputs[0].value == 0);
        require(tx.outputs[0].lockingBytecode == announcement);

        // Calculate leftover money after fee (1000 sats)
        // Check that the second tx output sends the change back if there's
        // enough leftover for another announcement
        int minerFee = 1000;
        int changeAmount = tx.inputs[this.activeInputIndex].value - minerFee;
        if (changeAmount >= minerFee) {
            bytes changeLock = tx.inputs[this.activeInputIndex].lockingBytecode;
            require(tx.outputs[1].lockingBytecode == changeLock);
            require(tx.outputs[1].value == changeAmount);
        }
    }
}
OP_PUSHINPUTREFSINGLETON <ref> OP_DROP
OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG

Language Grammar

grammar CashScript;

sourceFile
    : pragmaDirective* contractDefinition EOF
    ;

pragmaDirective
    : 'pragma' pragmaName pragmaValue ';'
    ;

pragmaName
    : 'cashscript'
    ;

pragmaValue
    : versionConstraint versionConstraint?
    ;

versionConstraint
    : versionOperator? VersionLiteral
    ;

versionOperator
    : '^' | '~' | '>=' | '>' | '<' | '<=' | '='
    ;

contractDefinition
    : 'contract' Identifier parameterList '{' functionDefinition* '}'
    ;

functionDefinition
    : 'function' Identifier parameterList '{' statement* '}'
    ;

parameterList
    : '(' (parameter (',' parameter)* ','?)? ')'
    ;

parameter
    : typeName Identifier
    ;

block
    : '{' statement* '}'
    | statement
    ;

statement
    : variableDefinition
    | tupleAssignment
    | assignStatement
    | timeOpStatement
    | requireStatement
    | ifStatement
    ;

variableDefinition
    : typeName modifier? Identifier '=' expression ';'
    ;

tupleAssignment
    : typeName Identifier ',' typeName Identifier '=' expression ';'
    ;

assignStatement
    : Identifier '=' expression ';'
    ;

timeOpStatement
    : 'require' '(' TxVar '>=' expression ')' ';'
    ;

requireStatement
    : 'require' '(' expression ')' ';'
    ;

ifStatement
    : 'if' '(' expression ')' ifBlock=block ('else' elseBlock=block)?
    ;

functionCall
    : Identifier expressionList // Only built-in functions are accepted
    ;

expressionList
    : '(' (expression (',' expression)* ','?)? ')'
    ;

expression
    : '(' expression ')' # Parenthesised
    | typeName '(' castable=expression (',' size=expression)? ','? ')' # Cast
    | functionCall # FunctionCallExpression
    | 'new' Identifier expressionList #Instantiation
    | expression '[' index=NumberLiteral ']' # TupleIndexOp
    | scope='tx.outputs' '[' expression ']' op=('.value' | '.lockingBytecode') # UnaryIntrospectionOp
    | scope='tx.inputs' '[' expression ']' op=('.value' | '.lockingBytecode' | '.outpointTransactionHash' | '.outpointIndex' | '.unlockingBytecode' | '.sequenceNumber') # UnaryIntrospectionOp
    | expression op=('.reverse()' | '.length') # UnaryOp
    | left=expression op='.split' '(' right=expression ')' # BinaryOp
    | op=('!' | '-') expression # UnaryOp
    | left=expression op=('*' | '/' | '%') right=expression # BinaryOp
    | left=expression op=('+' | '-') right=expression # BinaryOp
    // | expression ('>>' | '<<') expression --- OP_LSHIFT & RSHIFT are disabled in BCH Script
    | left=expression op=('<' | '<=' | '>' | '>=') right=expression # BinaryOp
    | left=expression op=('==' | '!=') right=expression # BinaryOp
    | left=expression op='&' right=expression # BinaryOp
    | left=expression op='^' right=expression # BinaryOp
    | left=expression op='|' right=expression # BinaryOp
    | left=expression op='&&' right=expression # BinaryOp
    | left=expression op='||' right=expression # BinaryOp
    | '[' (expression (',' expression)* ','?)? ']' # Array
    | NullaryOp # NullaryOp
    | Identifier # Identifier
    | literal # LiteralExpression
    ;

modifier
    : 'constant'
    ;

literal
    : BooleanLiteral
    | numberLiteral
    | StringLiteral
    | DateLiteral
    | HexLiteral
    ;

numberLiteral
    : NumberLiteral NumberUnit?
    ;

typeName
    : 'int' | 'bool' | 'string' | 'pubkey' | 'sig' | 'datasig' | Bytes
    ;

VersionLiteral
    : [0-9]+ '.' [0-9]+ '.' [0-9]+
    ;

BooleanLiteral
    : 'true' | 'false'
    ;

NumberUnit
    : 'satoshis' | 'sats' | 'finney' | 'bits' | 'bitcoin'
    | 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks'
    ;

NumberLiteral
    : [-]?[0-9]+ ([eE] [0-9]+)?
    ;

Bytes
    : 'bytes' Bound? | 'byte'
    ;

Bound
    : [1-9] [0-9]*
    ;

StringLiteral
    : '"' ('\\"' | ~["\r\n])*? '"'
    | '\'' ('\\\'' | ~['\r\n])*? '\''
    ;

DateLiteral
    : 'date(' StringLiteral ')'
    ;

HexLiteral
    : '0' [xX] [0-9A-Fa-f]*
    ;

TxVar
    : 'tx.age'
    | 'tx.time'
    ;

NullaryOp
    : 'this.activeInputIndex'
    | 'this.activeBytecode'
    | 'tx.inputs.length'
    | 'tx.outputs.length'
    | 'tx.version'
    | 'tx.locktime'
    ;

Identifier
    : [a-zA-Z] [a-zA-Z0-9_]*
    ;

WHITESPACE
    : [ \t\r\n\u000C]+ -> skip
    ;

COMMENT
    : '/*' .*? '*/' -> channel(HIDDEN)
    ;

LINE_COMMENT
    : '//' ~[\r\n]* -> channel(HIDDEN)
    ;

Artifacts

Compiled contracts can be represented by so-called artifacts. These artifacts contain all information that is needed to interact with the smart contracts on-chain. Artifacts are stored in .json files so they can be shared and stored for later usage without having to recompile the contract.

Did you know? Artifacts allow any third-party SDKs to be developed, since these SDKs only need to import and use an artifact file, while the compilation of the contract is left to the official cashc compiler.

Artifact specification

interface Artifact {
  contractName: string // Contract name
  constructorInputs: AbiInput[] // Arguments required to instantiate a contract
  abi: AbiFunction[] // functions that can be called
  bytecode: string // Compiled Script without constructor parameters added (in ASM format)
  source: string // Source code of the CashScript contract
  compiler: {
    name: string // Compiler used to compile this contract
    version: string // Compiler version used to compile this contract
  }
  updatedAt: string // Last datetime this artifact was updated (in ISO format)
}

interface AbiInput {
  name: string // Input name
  type: string // Input type (see language documentation)
}

interface AbiFunction {
  name: string // Function name
  inputs: AbiInput[] // Function inputs / parameters
}

Radiant Opcodes

The Radiant blockchain introduced a set of opcodes that significantly improves on the capability of the Bitcoin technology it's based on. All these opcodes will be outlined in this article.

Terms

  • Ref: A 36 byte reference, either of normal or singleton type. A reference is created from an input's outpoint and can be propagated with every spend of a UTXO. For a ref to exist in an output there must be a matching input ref of the same type or an input with a matching outpoint. The ref type cannot be changed once it is created.

  • Normal ref: A type of reference that can be propagated to one or more outputs.

  • Singleton ref: A type of reference that can only ever exist in a single UTXO. Cannot be propagated to multiple outputs.

  • State script: The part of the script before and not including an OP_STATESEPARATOR. Useful for storing state data that can be changed by the contract.

  • Code script: The part of the script from and including an OP_STATESEPARATOR. If no state separator is present, the code script is the entire script.

  • codeScriptHash: The double SHA-256 of the code script.

Hash functions

SHA-512/256 is the hash function used by Radiant's proof of work algorithm. It is available in script as single and double hash functions. These can be used to validate Radiant block headers in script.

Word
Hex
Input
Output
Description

OP_SHA512_256

ce

bytes

hash

SHA-512/256

OP_HASH512_256

cf

bytes

hash

Double SHA-512/256

Induction opcodes

Word
Hex
Input
Output
Description

OP_PUSHINPUTREF

d0

ref

ref

Propagates a ref to an output. A ref of normal type must exist in an input or an input must have an outpoint matching the ref. Ref will be left on the stack.

OP_PUSHINPUTREFSINGLETON

d8

ref

ref

Propagates a singleton ref to an output. A ref of singleton type must exist in an input or an input must have an outpoint matching the ref. Ref will be left on the stack.

OP_REQUIREINPUTREF

d1

ref

ref

A ref of normal type must exist in an input. Ref is not propagated. Ref will be left on the stack.

OP_DISALLOWPUSHINPUTREF

d2

ref

ref

Requires a ref to not exist in any input. Leaves the ref on the stack.

OP_DISALLOWPUSHINPUTREFSIBLING

d3

ref

ref

Requires a ref to not exist in any input sibling. Leaves the ref on the stack.

OP_REFTYPE_UTXO

d9

ref

type

Get the type of a ref from transaction inputs. Returns 0 (not found), 1 (normal type) or 2 (singleton type).

OP_REFTYPE_OUTPUT

da

ref

type

Get the type of a ref from transaction outputs. Returns 0 (not found), 1 (normal type) or 2 (singleton type).

OP_REFHASHDATASUMMARY_UTXO

d4

inputIndex

summary

For the input of the given index returns a hash256 of <nValue><hash256(scriptPubKey)><numRefs><hash(sortedMap(pushRefs))>

OP_REFHASHDATASUMMARY_OUTPUT

d6

outputIndex

summary

For the output of the given index returns a hash256 of <nValue><hash256(scriptPubKey)><numRefs><hash(sortedMap(pushRefs))>

OP_REFDATASUMMARY_UTXO

e1

inputIndex

summary

Return refs used within an input. Pushes a vector of the form <ref1><ref2><ref3>.... If an input UTXO does not contain at least one reference, then the 36 byte zeroes are pushed.

OP_REFDATASUMMARY_OUTPUT

e2

outputIndex

summary

Return refs used within an output. Pushes a vector of the form <ref1><ref2><ref3>.... If an input UTXO does not contain at least one reference, then the 36 byte zeroes are pushed.

State/code script opcodes

Word
Hex
Input
Output
Description

OP_STATESEPARATOR

bd

Separates the state script from the code script. Only one can exist in a script and it must not exist within an OP_IF. No OP_RETURN can exist in the script when used.

OP_STATESEPARATORINDEX_UTXO

be

inputIndex

separatorIndex

Returns the byte index of the state separator for an input. Returns zero if no separator exists.

OP_STATESEPARATORINDEX_OUTPUT

bf

outputIndex

separatorIndex

Returns the byte index of the state separator for an output. Returns zero if no separator exists.

OP_CODESCRIPTBYTECODE_UTXO

e9

inputIndex

bytecode

Returns the code script bytecode for an input.

OP_CODESCRIPTBYTECODE_OUTPUT

ea

outputIndex

bytecode

Returns the code script bytecode for an output.

OP_STATESCRIPTBYTECODE_UTXO

eb

inputIndex

bytecode

Returns the state script bytecode for an output. Does not include the OP_STATESEPARATOR.

OP_STATESCRIPTBYTECODE_OUTPUT

ec

outputIndex

bytecode

Returns the state script bytecode for an output. Does not include the OP_STATESEPARATOR.

Count and sum opcodes

These opcodes provide a set queries on inputs and outputs by ref, ref hash and code script hash. Counts and sums can be performed over many outputs with a single opcode, meaning an unrolled loop in the script is not required.

Word
Hex
Input
Output
Description

OP_REFHASHVALUESUM_UTXOS

d5

refHash

photons

Returns the total sum of photons for inputs that match a 32 byte hash of the sorted set of ref asset ids.

OP_REFHASHVALUESUM_OUTPUTS

d7

refHash

photons

Returns the total sum of photons for outputs that match a 32 byte hash of the sorted set of ref asset ids.

OP_REFVALUESUM_UTXOS

db

ref

photons

Returns the total sum of photons for inputs that contain a ref.

OP_REFVALUESUM_OUTPUTS

dc

ref

photons

Returns the total sum of photons for outputs that contain a ref.

OP_REFOUTPUTCOUNT_UTXOS

dd

ref

count

Returns the count of inputs that contain a ref.

OP_REFOUTPUTCOUNT_OUTPUTS

de

ref

count

Returns the count of outputs that contain a ref.

OP_REFOUTPUTCOUNTZEROVALUED_UTXOS

df

ref

count

Returns the count of inputs that contain a ref and contain zero photons.

OP_REFOUTPUTCOUNTZEROVALUED_OUTPUTS

e0

ref

count

Returns the count of inputs that contain a ref and contain zero photons.

OP_CODESCRIPTHASHVALUESUM_UTXOS

e3

codeScriptHash

photons

Returns the total sum of photons for inputs that have a codeScriptHash.

OP_CODESCRIPTHASHVALUESUM_OUTPUTS

e4

codeScriptHash

photons

Returns the total sum of photons for outputs that have a codeScriptHash.

OP_CODESCRIPTHASHOUTPUTCOUNT_UTXOS

e5

codeScriptHash

count

Returns the count of inputs that have a codeScriptHash.

OP_CODESCRIPTHASHOUTPUTCOUNT_OUTPUTS

e6

codeScriptHash

count

Returns the count of outputs that have a codeScriptHash.

OP_CODESCRIPTHASHZEROVALUEDOUTPUTCOUNT_UTXOS

e7

codeScriptHash

count

Returns the count of inputs that have a codeScriptHash and contain zero photons.

OP_CODESCRIPTHASHZEROVALUEDOUTPUTCOUNT_OUTPUTS

e8

codeScriptHash

count

Returns the count of outputs that have a codeScriptHash and contain zero photons.

Artifacts
Writing Covenants & Introspection
Syntax Highlighting
Examples page
GitHub
Language Description
SDK documentation
Artifacts
Contract Structure
Examples
Global Functions & Operators
Global Variables
Types
CashScript extension
Nathaniel Cherian
Ethereum
Package Control
language-solidity
Bitcoin Covenants
LocalCryptos
Sablier
Be.cash
MistCoin
MistCoin
SLP Mint Contracts
Memo.cash
Smart contracts on Ethereum, Bitcoin and Bitcoin Cash

Global Variables

Globally available units

An integer literal can take a suffix of either monetary or temporary units to add semantic value to these integers and to simplify arithmetic. When these units are used, the underlying integer is automatically multiplied by the value of the unit. The units sats, finney, bits and bitcoin are used to denote monetary value, while the units seconds, minutes, hours, days and weeks are used to denote time.

Example

require(1 sats == 1);
require(1 finney == 10);
require(1 bit == 100);
require(1 bitcoin == 1e8);

require(1 seconds == 1);
require(1 minutes == 60 seconds);
require(1 hours == 60 minutes);
require(1 days == 24 hours);
require(1 weeks == 7 days);

Global time lock variables

Bitcoin Cash has support for different kinds of time locks, which can be used to specify time-based conditions inside Bitcoin Cash contracts. These time locks can be separated by three main attributes: location, targeting and metric. The location can be the transaction level or the contract level, but contract level locks also require you to use a corresponding transaction level lock. The targeting can be relative (e.g. at least 4 hours have passed) or absolute (e.g. the block number is at least 600,000). The metric can be blocks or seconds.

tx.time

tx.time is used to create absolute time locks. The value of tx.time can either represent the block number of the spending transaction or its timestamp. When comparing it with values below 500,000,000, it is treated as a blocknumber, while higher values are treated as a timestamp.

Due to limitations in the underlying Bitcoin Script, tx.time can only be used in the following way:

require(tx.time >= <expression>);

Note: tx.time corresponds to the nLocktime field of the current transaction and the OP_CHECKLOCKTIMEVERIFY opcode.

tx.age

tx.age is used to create relative time locks. The value of tx.age can either represent a number of blocks, or a number of chunks, which are 512 seconds. The corresponding transaction level time lock determines which of the two options is used.

Due to limitations in the underlying Bitcoin Script, tx.age can only be used in the following way:

require(tx.age >= <expression>);

Note: tx.age corresponds to the nSequence field of the current UTXO and the OP_CHECKSEQUENCEVERIFY opcode.

Introspection variables

this.activeInputIndex

int this.activeInputIndex

During the validation of a BCH transaction, every transaction input is evaluated in order, and the contract's code is evaluated in the context of the different inputs. this.activeInputIndex represents the index of the input that is currently being evaluated. This can be used in conjunction with the properties under tx.inputs.

this.activeBytecode

bytes this.activeBytecode

During the validation of a BCH transaction, every transaction input is evaluated in order, and the contract's code is evaluated in the context of the different inputs. this.activeBytecode represents the contract bytecode of the input that is currently being evaluated.

tx.version

int tx.version

tx.locktime

int tx.locktime

Represents the nLocktime field of the transaction.

tx.inputs

Represents the list of inputs of the evaluated transaction. This is an array, and cannot be used on itself. You need to access an input with a specific index and specify the properties you want to access.

tx.inputs.length

int tx.inputs.length

Represents the number of inputs in the transaction.

tx.inputs[i].value

int tx.inputs[i].value

Represents the value of a specific input (in satoshis).

tx.inputs[i].lockingBytecode

bytes tx.inputs[i].lockingBytecode

Represents the locking bytecode (scriptPubKey) of a specific input.

tx.inputs[i].unlockingBytecode

bytes tx.inputs[i].unlockingBytecode

Represents the unlocking bytecode (scriptSig) of a specific input.

tx.inputs[i].outpointTransactionHash

bytes32 tx.inputs[i].outpointTransactionHash

Represents the outpoint transaction hash where a specific input was initially locked.

tx.inputs[i].outpointIndex

int tx.inputs[i].outpointIndex

Represents the outpoint index where a specific input was initially locked.

tx.inputs[i].sequenceNumber

int tx.inputs[i].sequenceNumber

Represents the nSequence number of a specific input.

tx.outputs

Represents the list of outputs of the evaluated transaction. This is an array, and cannot be used on itself. You need to access an output with a specific index and specify the properties you want to access.

tx.outputs.length

int tx.outputs.length

Represents the number of outputs in the transaction.

tx.outputs[i].value

int tx.outputs[i].value

Represents the value of a specific output (in satoshis).

tx.outputs[i].lockingBytecode

bytes tx.outputs[i].lockingBytecode

Represents the locking bytecode (scriptPubKey) of a specific output.

Constructing locking bytecode

One of the main use cases of covenants is enforcing transaction outputs (where money is sent). To assist with enforcing these outputs, there is a number of LockingBytecode objects that can be instantiated. These locking bytecodes can then be compared to the locking bytecodes of transaction outputs.

Example

bytes25 lockingBytecode = new LockingBytecodeP2PKH(pkh);
int value = 10000;
require(tx.outputs[0].lockingBytecode == lockingBytecode);
require(tx.outputs[0].value == value);

LockingBytecodeP2PKH

new LockingBytecodeP2PKH(bytes20 pkh): bytes25

Creates new P2PKH locking bytecode for the public key hash pkh.

LockingBytecodeP2SH

new LockingBytecodeP2SH(bytes20 scriptHash): bytes23

Creates new P2SH locking bytecode for the script hash scriptHash.

LockingBytecodeNullData

new LockingBytecodeNullData(bytes[] chunks): bytes

Creates new OP_RETURN locking bytecode with chunks as its OP_RETURN data.

Contract Structure

Contracts in CashScript are somewhat similar to classes in object-oriented languages. A notable difference is that there is no mutable state. So once a contract is instantiated with certain parameters, these values cannot change. Instead, functions can be called on the contract that act on the contract's values to spend money from the contract. The extension of CashScript source code files is .cash, and the structure of these source files is explained below.

Pragma

A contract file may start with a pragma directive to indicate the CashScript version the contract was written for. This ensures that a contract is not compiled with an unsupported compiler version, which could cause unintended side effects.

Example

pragma cashscript ^0.7.0;
pragma cashscript >= 0.4.0 < 0.5.4;

Constructor

A CashScript constructor works slightly differently than what you might be used to in regular object-oriented languages. It is not possible to define any statements inside the constructor, as the constructor is only used to store values in the contract. Because of this limited nature, there is no separate constructor function, but instead the parameters are specified directly on the class definition.

Example

pragma cashscript ^0.7.0;

contract HTLC(pubkey sender, pubkey recipient, int expiration, bytes32 hash) {
    ...
}

Functions

Example

pragma cashscript ^0.7.0;

contract TransferWithTimeout(pubkey sender, pubkey recipient, int timeout) {
    function transfer(sig recipientSig) {
        ...
    }

    function timeout(sig senderSig) {
        ...
    }
}

Statements

CashScript functions are made up of a collection of statements that determine whether money may be spent from the contract.

require()

The most important statement of CashScript contracts is the require statement. This statement takes a boolean expression and checks that it evaluates to true. If it evaluates to false instead, the transaction fails. This statement is used to ensure that the requirements are met to spend money from the contract.

Example

pragma cashscript ^0.7.0;

contract P2PKH(bytes20 pkh) {
    function spend(pubkey pk, sig s) {
        require(hash160(pk) == pkh);
        require(checkSig(s, pk));
    }
}

Variable declaration

Variables can be declared by specifying their type and name. All variables need to be initialised at the time of their declaration, but can be reassigned later on - unless specifying the constant keyword. Since CashScript is strongly typed and has no type inference, it is not possible to use keywords such as var or let to declare variables.

Caution: CashScript disallows variable shadowing and unused variables.

Example

int myNumber = 3000;
string constant myString = 'Bitcoin Cash';

Variable assignment

After their initial declaration, any variable can be reassigned later on. However, CashScript lacks any compound assignment operators such as += or -=.

Example

i = i + 1;
hashedValue = sha256(hashedValue);
myString = 'Cash';

Control structures

The only control structures in CashScript are if and else statements. This is due to limitations in the underlying Bitcoin Script which prevent loops, recursion, and return statements. If-else statements follow usual semantics known from languages like C or JavaScript.

Note: There is no implicit type conversion from non-boolean to boolean types. So if (1) { ... } is not valid CashScript and should instead be written as if (bool(1)) { ... }

Example

pragma cashscript ^0.7.0;

contract OneOfTwo(bytes20 pkh1, bytes32 hash1, bytes20 pkh2, bytes32 hash2) {
    function spend(pubkey pk, sig s, bytes message) {
        require(checkSig(s, pk));
        bytes20 pkh = hash160(pk);

        if (pkh == pkh1) {
            require(sha256(message) == hash1);
        } else if (pkh == pkh2) {
            require(sha256(message) == hash2);
        } else {
            require(false); // fail
        }
    }
}

Comments

Comments can be added anywhere in the contract file. Comment semantics are similar to languages like JavaScript or C. This means that single-line comments can be added with // ..., while multiline comments can be added with /* ... */.

Example

// This is a single-line comment.

/*
This is a
multi-line comment.
*/

Global Functions & Operators

CashScript has several functions builtin for things like cryptographic and arithmetic applications. It also includes many common operators, although some important ones are notably missing due to the limitations of the underlying Bitcoin Script.

Arithmetic functions

abs()

int abs(int a)

Returns the absolute value of argument a.

min()

int min(int a, int b)

Returns the minimum value of arguments a and b.

max()

int max(int a, int b)

Returns the maximum value of arguments a and b.

within()

bool within(int x, int lower, int upper)

Returns true if and only if x >= lower && x < upper.

Hashing functions

ripemd160()

bytes20 ripemd160(any x)

Returns the RIPEMD-160 hash of argument x.

sha1()

bytes20 sha1(any x)

Returns the SHA-1 hash of argument x.

sha256()

bytes32 sha256(any x)

Returns the SHA-256 hash of argument x.

hash160()

bytes20 hash160(any x)

Returns the RIPEMD-160 hash of the SHA-256 hash of argument x.

hash256()

bytes32 hash256(any x)

Returns the double SHA-256 hash of argument x.

Signature checking functions

checkSig()

bool checksig(sig s, pubkey pk)

Checks that transaction signature s is valid for the current transaction and matches with public key pk.

checkMultiSig()

bool checkMultiSig(sig[] sigs, pubkey[] pks)

Performs a multi-signature check using a list of transaction signatures and public keys.

:::note While this function can be used inside your smart contracts, it is not supported by the JavaScript SDK, so it is recommended not to use it. Instead a checkMultiSig() call can be simulated using multiple checkSig() calls. :::

checkDataSig()

bool checkDataSig(datasig s, bytes msg, pubkey pk)

Checks that sig s is a valid signature for message msg and matches with public key pk.

Operators

An overview of all supported operators and their precedence is included below. Notable is a lack of exponentiation, since these operations are not supported by the underlying Bitcoin Script.

Precedence
Description
Operator

1

Parentheses

(<expression>)

2

Type cast

<type>(<expression>)

3

Object instantiation

new <class>(<args...>)

4

Function call

<function>(<args...>)

5

Tuple index

<tuple>[<index>]

6

Member access

<object>.<member>

7

Unary minus

-

7

Logical NOT

!

8

Multiplication, division and modulo

*, /, %

9

Addition and subtraction

+, -

9

String / bytes concatenation

+

10

Numeric comparison

<, >, <=, >=

11

Equality and inequality

==, !=

12

Bitwise AND

&

13

Bitwise XOR

^

14

Bitwise OR

|

15

Logical AND

&&

16

Logical OR

||

17

Assignment

=

Examples

Transfer With Timeout

One interesting use case of Bitcoin Cash is using it for paper tips. With paper tips, you send a small amount of money to an address, and print the corresponding private key on a piece of paper. Then you can hand out these pieces of paper as a tip or gift to people in person. In practice, however, people might not know what to do with these gifts or they might lose or forget about it.

As an alternative, a smart contract can be used for these kinds of gifts. This smart contract allows the recipient to claim their gift at any time, but if they don't claim it in time, the sender can reclaim it.

pragma cashscript ^0.7.0;

contract TransferWithTimeout(pubkey sender, pubkey recipient, int timeout) {
    // Require recipient's signature to match
    function transfer(sig recipientSig) {
        require(checkSig(recipientSig, recipient));
    }

    // Require timeout time to be reached and sender's signature to match
    function timeout(sig senderSig) {
        require(checkSig(senderSig, sender));
        require(tx.time >= timeout);
    }
}

HodlVault

For better or worse, HODLing and waiting for price increases is one of the main things people want to do with their cryptocurrency. But it can be difficult to hold on to your cryptocurrency when the price is going down. So to prevent weak hands from getting the best of you, it's better to store your stash in a smart contract that enforces HODLing for you.

This smart contract works by connecting with a price oracle. This price oracle is a trusted entity that publishes the BCH/USD price every block. This price is passed into the contract, and only if the price is higher than your target price you can spend the coins.

This involves some degree of trust in the price oracle, but since the oracle produces price data for everyone to use, their incentive to attack your smart contract is minimised. To improve this situation, you can also choose to connect with multiple oracle providers so you do not have to trust a single party.

pragma cashscript ^0.7.0;

// A minimum block is provided to ensure that oracle price entries from before
// this block are disregarded. i.e. when the BCH price was $1000 in the past,
// an oracle entry with the old block number and price can not be used.
contract HodlVault(pubkey ownerPk, pubkey oraclePk, int minBlock, int priceTarget) {
    function spend(sig ownerSig, datasig oracleSig, bytes oracleMessage) {
        // Decode the message { blockHeight, price }
        bytes4 blockHeightBin, bytes4 priceBin = oracleMessage.split(4);
        int blockHeight = int(blockHeightBin);
        int price = int(priceBin);


        // Check that blockHeight is after minBlock
        require(blockHeight >= minBlock);
        // Check that blockHeight is not in the future
        require(tx.time >= blockHeight);

        // Check that current price is at least priceTarget
        require(price >= priceTarget);

        // Check that the price message was signed by the oracle
        require(checkDataSig(oracleSig, oracleMessage, oraclePk));

        // Check that the transaction was signed by the contract owner
        require(checkSig(ownerSig, ownerPk));
    }
}

Licho's Mecenas

Donations are a great way to support the projects you love and periodic donations can incentivise continuous improvement to the product. But platforms like Patreon generally take fees of 10%+ and don't accept cryptocurrencies. Instead you can create a peer-to-peer smart contract that allows a recipient to withdraw a specific amount every month.

The contract works by checking that a UTXO is at least 30 days old, after which it uses a covenant to enforce that the pledge amount is sent to the recipient, while the remainder is sent back to the contract itself. By sending it back the tx.age counter is effectively reset, meaning this process can only be repeated when another 30 days have past.

Due to the nature of covenants we have to be very specific about the outputs (amounts and destinations) of the transaction. This also means that we have to account for the special case where the remaining contract balance is lower than the pledge amount, meaning no remainder should be sent back. Finally we have to account for a small fee that has to be taken from the contract's balance to pay the miners.

contract Mecenas(bytes20 recipient, bytes20 funder, int pledge, int period) {
    function receive() {
        require(tx.age >= period);

        // Check that the first output sends to the recipient
        bytes25 recipientLockingBytecode = new LockingBytecodeP2PKH(recipient);
        require(tx.outputs[0].lockingBytecode == recipientLockingBytecode);

        // Calculate the value that's left
        int minerFee = 1000;
        int currentValue = tx.inputs[this.activeInputIndex].value;
        int changeValue = currentValue - pledge - minerFee;

        // If there is not enough left for *another* pledge after this one,
        // we send the remainder to the recipient. Otherwise we send the
        // remainder to the recipient and the change back to the contract
        if (changeValue <= pledge + minerFee) {
            require(tx.outputs[0].value == currentValue - minerFee);
        } else {
            require(tx.outputs[0].value == pledge);
            bytes changeBytecode = tx.inputs[this.activeInputIndex].lockingBytecode;
            require(tx.outputs[1].lockingBytecode == changeBytecode);
            require(tx.outputs[1].value == changeValue);
        }
    }

    function reclaim(pubkey pk, sig s) {
        require(hash160(pk) == funder);
        require(checkSig(s, pk));
    }
}

Types

Boolean

bool: The possible values are constants true and false.

Operators:

  • ! (logical negation)

  • && (logical conjunction, “and”)

  • || (logical disjunction, “or”)

  • == (equality)

  • != (inequality)

Note: The operators || and && don't apply common short-circuiting rules. This means that in the expression f(x) || g(y), g(y) will still be executed even if f(x) evaluates to true.

Integer

int: Signed integer of 64 bit size.

Operators:

  • Comparisons: <=, <, ==, !=, >=, > (all evaluate to bool)

  • Arithmetic operators: +, -, unary -, *, /, % (modulo).

Note the clear lack of the ** (exponentiation) operator as well as any bitwise operators.

Caution: The script will fail when the right hand side of Division and modulo operations is zero.

Date Parsing

Dates and times are always represented as integers. To get the UTC timestamp of a date use the built-in parser to avoid any potential errors. This will take a date in the format date("YYYY-MM-DDThh:mm:ss") and convert it to an integer timestamp.

Example

String

string: UTF8-encoded byte sequence.

Operators:

  • + (concatenation)

  • == (equality)

  • != (inequality)

Members:

  • length: Number of characters in the string.

  • split(int): Splits the string at the specified index and returns a tuple with the two resulting strings.

  • reverse(): Reverses the string.

Caution: The script will fail if split() is called with an index that is out of bounds.

Bytes

bytes: Byte sequence. Can optionally be bound to a byte length by specifying e.g. bytes4, bytes32, bytes64. It is also possible to use byte as an alias for bytes1.

Operators:

  • + (concatenation)

  • == (equality)

  • != (inequality)

Members:

  • length: Number of bytes in the sequence.

  • split(int): Splits the byte sequence at the specified index and returns a tuple with the two resulting byte sequences.

  • reverse(): Reverses the byte sequence.

Caution: The script will fail if split() is called with an index that is out of bounds.

Bytes types with semantic meaning

Some byte sequences hold specific meanings inside Bitcoin Cash contracts. These have been granted their own types separate from the regular bytes type.

Public Key

pubkey: Byte sequence representing a public key. Generally 33 bytes long.

Operators:

  • == (equality)

  • != (inequality)

Transaction Signature

sig: Byte sequence representing a transaction signature. Generally 65 bytes long.

Operators:

  • == (equality)

  • != (inequality)

Data Signature

datasig: Byte sequence representing a data signature. Generally 64 bytes long.

Operators:

  • == (equality)

  • != (inequality)

Array

Arrays are not assignable and can only be used with the checkMultisig function using the following syntax:

Tuple

Tuples are the type that is returned when calling the split member function on a string or bytes type. Their first or second element can be accessed through an indexing syntax similar to other languages:

It is also possible to assign both sides of the tuple at once with a destructuring syntax:

Type Casting

Type casting can be done both explicitly and implicitly as illustrated below. pubkey, sig and datasig can be implicitly cast to bytes, meaning they can be used anywhere where you would normally use a bytes type. Explicit type casting can be done with a broader range of types, but is still limited. The syntax of this explicit type casting is illustrated below. Note that you can also cast to bounded bytes types.

Note: When casting integer types to bytes of a certain size, the integer value is padded with zeros. e.g. bytes4(0) == 0x00000000. It is also possible to pad with a variable number of zeros, by passing in a size parameter, which indicates the size of the output. e.g. bytes(0, 4 - 2) == 0x0000.

Caution: When casting bytes types to integer, you should be sure that the bytes value fits inside a 64-bit signed integer, or the script will fail.

See the following table for information on which types can be cast to other which other types.

Example

Sending Transactions

Transaction options

to()

The to() function allows you to add outputs to the transaction. Either a single pair to/amount pair can be provided, or a list of them. This function can be called any number of times, and the provided outputs will be added to the list of earlier added outputs.

Example

withOpReturn()

The withOpReturn() function allows you to add OP_RETURN outputs to the transaction. The chunks parameter can include regular UTF-8 encoded strings, or hex strings prefixed with 0x. This function can be called any number of times, and the provided outputs will be added to the list of earlier added outputs.

Example

from()

The from() function allows you to provide a hardcoded list of contract UTXOs to be used in the transaction. This overrides the regular UTXO selection performed by the CashScript SDK, so no further selection will be performed on the provided UTXOs. This function can be called any number of times, and the provided UTXOs will be added to the list of earlier added UTXOs.

Tip: The built-in UTXO selection is generally sufficient. But there are specific use cases for which it makes sense to use a custom selection algorithm.

Example

withFeePerByte()

The withFeePerByte() function allows you to specify the fee per per bytes for the transaction. By default the fee per bytes is set to 1.0 satoshis, which is nearly always enough to be included in the next block. So it's generally not necessary to change this.

Example

withHardcodedFee()

The withHardcodedFee() function allows you to specify a hardcoded fee to the transaction. By default the transaction fee is automatically calculated by the CashScript SDK, but there are certain use cases where the smart contract relies on a hardcoded fee.

Tip: If you're not building a covenant contract, you probably do not need a hardcoded transaction fee.

Example

withMinChange()

The withMinChange() function allows you to set a threshold for including a change output. Any remaining amount under this threshold will be added to the transaction fee instead.

Tip: This is generally only useful in specific covenant use cases.

Example

withoutChange()

The withoutChange() function allows you to disable the change output. The remaining amount will be added to the transaction fee instead. This is equivalent to withMinChange(Number.MAX_VALUE).

Caution: Be sure to check that the remaining amount (sum of inputs - sum of outputs) is not too high. The difference will be added to the transaction fee and cannot be reclaimed.

Example

withAge()

Example

withTime()

The withTime() function allows you to specify the minimum block number that the transaction can be included in. The time parameter will be the value of tx.time inside the smart contract.

Tip: By default, the transaction's time variable is set to the most recent block number, which is the most common use case. So you should only override this in specific use cases.

Example

Transaction building

send()

After completing a transaction, the send() function can be used to send the transaction to the BCH network. An incomplete transaction cannot be sent.

Example

build()

After completing a transaction, the build() function can be used to build the entire transaction and return the signed transaction hex string. This can then be imported into other libraries or applications as necessary.

Example

meep()

Example

Transaction errors

Transactions can fail for a number of reasons. Most of these are related to the execution of the smart contract (e.g. wrong parameters or a bug in the contract code). But errors can also occur because of other reasons (e.g. a fee that's too low or the same transaction already exists in the mempool). To facilitate error handling in your applications, the CashScript SDK provides an enum of different reasons for a failure.

This Reason enum only includes errors that are related to smart contract execution, so other reasons have to be caught separately. Besides the Reason enum, there are also several error classes that can be caught and acted on:

  • FailedRequireError, signifies a failed require statement. This includes the following reasons:

  • Reason.EVAL_FALSE

  • Reason.VERIFY

  • Reason.EQUALVERIFY

  • Reason.CHECKMULTISIGVERIFY

  • Reason.CHECKSIGVERIFY

  • Reason.CHECKDATASIGVERIFY

  • Reason.NUMEQUALVERIFY

  • FailedTimeCheckError, signifies a failed time check using tx.time or tx.age. This includes the following reasons:

  • Reason.NEGATIVE_LOCKTIME

  • Reason.UNSATISFIED_LOCKTIME

  • FailedSigCHeckError, signifies a failed signature check. This includes the following reasons:

  • Reason.SIG_COUNT

  • Reason.PUBKEY_COUNT

  • Reason.SIG_HASHTYPE

  • Reason.SIG_DER

  • Reason.SIG_HIGH_S

  • Reason.SIG_NULLFAIL

  • Reason.SIG_BADLENGTH

  • Reason.SIG_NONSCHNORR

  • FailedTransactionError, signifies a general fallback error. This includes all remaining reasons listed in the Reason enum as well as any other reasons unrelated to the smart contract execution.

Examples

Transfer With Timeout

```solidity title="TransferWithTimeout.cash" contract TransferWithTimeout(pubkey sender, pubkey recipient, int timeout) { function transfer(sig recipientSig) { require(checkSig(recipientSig, recipient)); }

}

Memo.cash Announcement

To ensure that this leftover money does not get lost in the contract, the contract performs an extra check, and adds the remainder to the transaction fee if it's too low.

```solidity title="Announcement.cash" pragma cashscript ^0.7.0;

// This contract enforces making an announcement on Memo.cash and sending the // remaining balance back to the contract. contract Announcement() { function announce() { // Create the memo.cash announcement output bytes announcement = new LockingBytecodeNullData([ 0x6d02, bytes('A contract may not injure a human being or, ' + 'through inaction, allow a human being to come to harm.') ]);

}

Mint and transfer of an NFT, followed by an invalid transaction
Diagram 1. Radiant Transactions.
Diagram 2. Block Structure; transactions are organized into a Merkle Tree.
Diagram 3. Transactions representing user-defined coin types — or digital assets.
Diagram 4. Custom user-defined coin types are defined from a special mint transaction. A unique identifier is used to classify the coin type.
Diagram 5. Unique identifiers are initialized by matching an outpoint of one of the outputs being spent, and then maintained as long as at least one of the outputs being spent contains the same unique identifier in the script body.
Diagram 6. Full parent transaction validation, mathematical induction proof by embedding the full parent transactions into the inputs resulting in exponential transaction size increase.
Diagram 7. Compressed parent transaction validation, mathematical induction proof by embedding the transaction hash version 3 preimage data-structure of the parent and grand-parent to enforce arbitrary rules and constraints.
Diagram 8. Mining Nodes are well-connected and build on top of each other's blocks. Archive nodes store complete blocks for historical analysis and bootstrapping purposes. Agent nodes are listener nodes which filter and store transactions to serve clients.
Diagram 1. Radiant Transactions.
Diagram 2. Block Structure; transactions are organized into a Merkle Tree.
Diagram 3. Transactions representing user-defined coin types — or digital assets.
Diagram 4. Custom user-defined coin types are defined from a special mint transaction. A unique identifier is used to classify the coin type.
Diagram 5. Unique identifiers are initialized by matching an outpoint of one of the outputs being spent, and then maintained as long as at least one of the outputs being spent contains the same unique identifier in the script body.
Diagram 6. Full parent transaction validation, mathematical induction proof by embedding the full parent transactions into the inputs resulting in exponential transaction size increase.
Diagram 7. Compressed parent transaction validation, mathematical induction proof by embedding the transaction hash version 3 preimage data-structure of the parent and grand-parent to enforce arbitrary rules and constraints.
Diagram 8. Mining Nodes are well-connected and build on top of each other's blocks. Archive nodes store complete blocks for historical analysis and bootstrapping purposes. Agent nodes are listener nodes which filter and store transactions to serve clients.
Diagram 1. Radiant Transactions.
Diagram 2. Block Structure; transactions are organized into a Merkle Tree.
Diagram 3. Transactions representing user-defined coin types — or digital assets.
Diagram 4. Custom user-defined coin types are defined from a special mint transaction. A unique identifier is used to classify the coin type.
Diagram 5. Unique identifiers are initialized by matching an outpoint of one of the outputs being spent, and then maintained as long as at least one of the outputs being spent contains the same unique identifier in the script body.
Diagram 6. Full parent transaction validation, mathematical induction proof by embedding the full parent transactions into the inputs resulting in exponential transaction size increase.
Diagram 7. Compressed parent transaction validation, mathematical induction proof by embedding the transaction hash version 3 preimage data-structure of the parent and grand-parent to enforce arbitrary rules and constraints.
Diagram 8. Mining Nodes are well-connected and build on top of each other's blocks. Archive nodes store complete blocks for historical analysis and bootstrapping purposes. Agent nodes are listener nodes which filter and store transactions to serve clients.
GitHub - RadiantBlockchain-Community/radiantscript: Fork of CashScript with support for Radiant opcodesGitHub

Caution: Be careful when using these units in precise calendar calculations though, because not every year equals 365 days and not even every minute has 60 seconds because of .

It can be difficult to fully grasp the intricacies of time locks, so if you're starting out it is recommended to start off with the simplest version: absolute block-based time locks. If you do want to dive into the more advanced uses of time locks, James Prestwich wrote that also fully applies to Bitcoin Cash.

Because of the way time locks work, a corresponding time lock needs to be added to the transaction. The CashScript SDK automatically sets this transaction level time lock to the most recent block number, as this is the most common use case. If you need to use a different block number or timestamp, this should be passed into the CashScript SDK using the function. If the default matches your use case, no additional actions are required.

Because of the way time locks work, a corresponding time lock needs to be added to the transaction. This can be done in the CashScript SDK using the function. However, the value passed into this function will always be treated as a number of blocks, so it is currently not supported to use tx.age as a number of second chunks.

Introspection functionality is used to create covenant contracts. Covenants are a technique used to put constraints on spending the money inside a smart contract. The main use case of this is limiting the addresses where money can be sent and the amount sent. To explore the possible uses of covenants inside smart contracts, read the .

Represents the version of the current transaction. Different transaction versions can have differences in functionality. Currently only version 1 and 2 exist, where only version 2 has support for .

Note: tx.locktime is similar to the global variable. It is recommended to only use tx.locktime for adding nLocktime to simulated state and in all other cases.

Note: The pragma directive follows regular .

The main construct in a CashScript contract is the function. A contract can contain one or multiple functions that can be executed to trigger transactions that spend money from the contract. In the basics the result of a function is just a yes or no answer to the question 'Can money be sent out of this contract?'. But by using a technique called covenants, it is possible to specify other conditions, like restricting where money can be sent. To read more about this technique, refer to the .

Caution: All signature checking functions must comply with the rule. This means that if you want to use the output of a signature check inside the condition of an if-statement, the input signature needs to either be correct, or an empty byte array. When you use an incorrect signature as an input, the script will fail.

An extensive collection of examples is available in the . Below we discuss a few of these examples in more details and go through the functionality.

CashScript is a statically typed language, which means that the type of each variable needs to be specified. Types can also be implicitly or explicitly cast to other types. For a quick reference of the various casting possibilities, see .

Type
Implicitly castable to
Explicitly castable to

When calling a contract function on a Contract object, an incomplete Transaction object is returned. This transaction can be completed by providing a number of outputs using the or functions. Other chained functions are included to set other transaction parameters.

Most of the available transaction options are only useful in very specific use cases, but the functions , and are commonly used. is also commonly used with covenant contracts.

The withAge() function allows you to specify the minimum age of the transaction inputs. This is necessary if you want to to use the tx.age CashScript functionality, and the age parameter passed into this function will be the value of tx.age inside the smart contract. For more information, refer to .

Tip: If the transaction fails, a meep command is automatically returned. This command can be used to debug the transaction using the

After completing a transaction, the meep() function can be used to return the required debugging command for the . This command string can then be used to debug the transaction.

An extensive collection of examples is available in the . Below we discuss a few of these examples in more details. These examples focus mainly on the use of the SDK, while the in the language section focuses more on the CashScript syntax.

The idea of this smart contract is explained on the . The gist is that it allows you to send an amount of BCH to someone, but if they don't claim the sent amount, it can be recovered by the sender.

is a Twitter-like social network based on Bitcoin Cash. It uses OP_RETURN outputs to post messages on-chain. By using a covenant we can create an example contract whose only job is to post Isaac Asimov's first law of smart contracts to Memo.cash. Just to remind its fellow smart contracts.

This contract expects a hardcoded transaction fee of 1000 satoshis. This is necessary due to the nature of covenants (See the for more information on this). The remaining balance after this transaction fee might end up being lower than 1000 satoshis, which means that the contract does not have enough leftover to make another announcement.

leap seconds
the best article explaining time locks in Bitcoin
CashScript Covenants Guide
BIP68
semantic versioning rules
CashScript Covenants Guide
NULLFAIL
GitHub repository
tx.time
tx.time
int timestamp = date("2021-02-17T01:30:00");
require(timestamp == 1613554200);
checkMultisig([sig1, sig2], [pk1, pk2, pk3]);
string question = "What is Bitcoin Cash?";
string answer = question.split(15)[0].split(8)[1];
string bitcoin, string cash = "BitcoinCash".split(7);
require(bitcoin == cash);

int

bytes, bool

bool

int

string

bytes

bytes

sig, pubkey, int

pubkey

bytes

bytes

sig

bytes

bytes

datasig

bytes

bytes

pubkey pk = pubkey(0x0000);
bytes editedPk = bytes(pk) + 0x1234;
bytes4 integer = bytes4(25);
transaction.to(to: string, amount: number): this
transaction.to(outputs: { to: string, amount: number }[]): this
.to('bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx', 500000)
transaction.withOpReturn(chunks: string[]): this
.withOpReturn(['0x6d02', 'Hello World!'])
transaction.from(inputs: Utxo[]): this
.from(await instance.getUtxos())
transaction.withFeePerByte(feePerByte: number): this
.withFeePerByte(2.3)
transaction.withHardcodedFee(hardcodedFee: number): this
.withHardcodedFee(1000)
transaction.withMinChange(minChange: number): this
.withMinChange(1000)
transaction.withoutChange(): this
.withoutChange()
transaction.withAge(age: number): this
.withAge(10)
transaction.withTime(time: number): this
.withTime(700000)
async transaction.send(): Promise<TransactionDetails>
interface TransactionDetails {
  inputs: Uint8Array[];
  locktime: number;
  outputs: Uint8Array[];
  version: number;
  txid: string;
  hex: string;
}
import { alice } from './somewhere';

const txDetails = await instance.functions
  .transfer(new SignatureTemplate(alice))
  .withOpReturn(['0x6d02', 'Hello World!'])
  .to('bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx', 200000)
  .to('bitcoincash:qqeht8vnwag20yv8dvtcrd4ujx09fwxwsqqqw93w88', 100000)
  .withHardcodedFee(1000)
  .send()
async transaction.build(): Promise<string>
const txHex = await instance.functions
  .transfer(new SignatureTemplate(alice))
  .to('bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx', 500000)
  .withAge(10).withFeePerByte(10).build()
async transaction.meep(): Promise<string>
const meepStr = await instance.functions
  .transfer(new SignatureTemplate(alice))
  .to('bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx', 500000)
  .withTime(700000)
  .meep()
enum Reason {
  EVAL_FALSE = 'Script evaluated without error but finished with a false/empty top stack element',
  VERIFY = 'Script failed an OP_VERIFY operation',
  EQUALVERIFY = 'Script failed an OP_EQUALVERIFY operation',
  CHECKMULTISIGVERIFY = 'Script failed an OP_CHECKMULTISIGVERIFY operation',
  CHECKSIGVERIFY = 'Script failed an OP_CHECKSIGVERIFY operation',
  CHECKDATASIGVERIFY = 'Script failed an OP_CHECKDATASIGVERIFY operation',
  NUMEQUALVERIFY = 'Script failed an OP_NUMEQUALVERIFY operation',
  SCRIPT_SIZE = 'Script is too big',
  PUSH_SIZE = 'Push value size limit exceeded',
  OP_COUNT = 'Operation limit exceeded',
  STACK_SIZE = 'Stack size limit exceeded',
  SIG_COUNT = 'Signature count negative or greater than pubkey count',
  PUBKEY_COUNT = 'Pubkey count negative or limit exceeded',
  INVALID_OPERAND_SIZE = 'Invalid operand size',
  INVALID_NUMBER_RANGE = 'Given operand is not a number within the valid range',
  IMPOSSIBLE_ENCODING = 'The requested encoding is impossible to satisfy',
  INVALID_SPLIT_RANGE = 'Invalid OP_SPLIT range',
  INVALID_BIT_COUNT = 'Invalid number of bit set in OP_CHECKMULTISIG',
  BAD_OPCODE = 'Opcode missing or not understood',
  DISABLED_OPCODE = 'Attempted to use a disabled opcode',
  INVALID_STACK_OPERATION = 'Operation not valid with the current stack size',
  INVALID_ALTSTACK_OPERATION = 'Operation not valid with the current altstack size',
  OP_RETURN = 'OP_RETURN was encountered',
  UNBALANCED_CONDITIONAL = 'Invalid OP_IF construction',
  DIV_BY_ZERO = 'Division by zero error',
  MOD_BY_ZERO = 'Modulo by zero error',
  INVALID_BITFIELD_SIZE = 'Bitfield of unexpected size error',
  INVALID_BIT_RANGE = 'Bitfield\'s bit out of the expected range',
  NEGATIVE_LOCKTIME = 'Negative locktime',
  UNSATISFIED_LOCKTIME = 'Locktime requirement not satisfied',
  SIG_HASHTYPE = 'Signature hash type missing or not understood',
  SIG_DER = 'Non-canonical DER signature',
  MINIMALDATA = 'Data push larger than necessary',
  SIG_PUSHONLY = 'Only push operators allowed in signature scripts',
  SIG_HIGH_S = 'Non-canonical signature: S value is unnecessarily high',
  MINIMALIF = 'OP_IF/NOTIF argument must be minimal',
  SIG_NULLFAIL = 'Signature must be zero for failed CHECK(MULTI)SIG operation',
  SIG_BADLENGTH = 'Signature cannot be 65 bytes in CHECKMULTISIG',
  SIG_NONSCHNORR = 'Only Schnorr signatures allowed in this operation',
  DISCOURAGE_UPGRADABLE_NOPS = 'NOPx reserved for soft-fork upgrades',
  PUBKEYTYPE = 'Public key is neither compressed or uncompressed',
  CLEANSTACK = 'Script did not clean its stack',
  NONCOMPRESSED_PUBKEY = 'Using non-compressed public key',
  ILLEGAL_FORKID = 'Illegal use of SIGHASH_FORKID',
  MUST_USE_FORKID = 'Signature must use SIGHASH_FORKID',
  UNKNOWN = 'unknown error',
}
withTime()
withAge()
function timeout(sig senderSig) {
    require(checkSig(senderSig, sender));
    require(tx.time >= timeout);
}

Now to put this smart contract in use in a JavaScript application we have to use the CashScript SDK in combination with a BCH library such as [BCHJS][bchjs], [bitcore-lib-cash][bitcore] or [Libauth][libauth]. These libraries are used to generate public/private keys for the contract participants. Then these keys can be used in the CashScript SDK. The key generation code is left out of this example, since this works differently for every library.

```ts title="TransferWithTimeout.js"
import { Contract, SignatureTemplate } from 'cashscript';
import { alicePriv, alicePub, bobPriv, bobPub } from './somewhere';

async function run() {
  // Import the compiled TransferWithTimeout JSON artifact
  const artifact = require('./transfer_with_timeout.json');

  // Instantiate a new contract using the artifact and constructor arguments:
  // { sender: alicePub, recipient: bobPub, timeout: 1000000 }
  // No network provider is provided, so the default ElectrumNetworkProvider is used
  const contract = new Contract(artifact, [alicePub, bobPub, 1000000]);

  // Display contract address and balance
  console.log('contract address:', contract.address);
  console.log('contract balance:', await contract.getBalance());

  // Call the transfer function with Bob's signature
  // i.e. Bob claims the money that Alice has sent him
  const txDetails = await contract.functions
    .transfer(new SignatureTemplate(bobPriv))
    .to('bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx', 10000)
    .send();
  console.log(txDetails);

  // Call the timeout function with Alice's signature
  // i.e. Alice recovers the money that Bob has not claimed
  // But because the timeout has not passed yet, the function fails and
  // we call the meep function so the transaction can be debugged instead
  const meepStr = await contract.functions
    .timeout(new SignatureTemplate(alicePriv))
    .to('bitcoincash:qqeht8vnwag20yv8dvtcrd4ujx09fwxwsqqqw93w88', 10000)
    .meep();
  console.log(meepStr);
}
    // Check that the first tx output matches the announcement
    require(tx.outputs[0].value == 0);
    require(tx.outputs[0].lockingBytecode == announcement);

    // Calculate leftover money after fee (1000 sats)
    // Check that the second tx output sends the change back if there's
    // enough leftover for another announcement
    int minerFee = 1000;
    int changeAmount = tx.inputs[this.activeInputIndex].value - minerFee;
    if (changeAmount >= minerFee) {
        bytes changeLock = tx.inputs[this.activeInputIndex].lockingBytecode;
        require(tx.outputs[1].lockingBytecode == changeLock);
        require(tx.outputs[1].value == changeAmount);
    }
}

The CashScript code above ensures that the smart contract **can only** be used in the way specified in the code. But the transaction needs to be created by the SDK, and to ensure that it complies with the rules of the smart contract, we need to use some of the more advanced options of the SDK.

```ts title="Announcement.js"
import { ElectrumNetworkProvider, Contract, SignatureTemplate } from 'cashscript';
import { alicePriv, alicePub } from './somewhere';

export async function run(){
  // Import the compiled announcement JSON artifact
  const artifact = require('./announcement.json');

  // Initialise a network provider for network operations on MAINNET
  const provider = new ElectrumNetworkProvider('mainnet');

  // Instantiate a new contract using the compiled artifact and network provider
  // AND providing the constructor parameters (none)
  const contract = new Contract(artifact, [], provider);

  // Display contract address, balance, opcount, and bytesize
  console.log('contract address:', contract.address);
  console.log('contract balance:', await contract.getBalance());
  console.log('contract opcount:', contract.opcount);
  console.log('contract bytesize:', contract.bytesize);

  // Create the announcement string. Any other announcement will fail because
  // it does not comply with the smart contract.
  const str = 'A contract may not injure a human being or, '
    + 'through inaction, allow a human being to come to harm.';

  // Send the announcement transaction
  const txDetails = await contract.functions
    .announce(alicePub, new SignatureTemplate(alicePriv))
    // Add the announcement string as an OP_RETURN output
    .withOpReturn(['0x6d02', str])
    // Hardcodes the transaction fee (like the contract expects)
    .withHardcodedFee(1000)
    // Only add a "change" output if the remainder is higher than 1000
    .withMinChange(1000)
    .send();

  console.log(txDetails);
}

HashedMap - Chinese

Map是一种非常常用的数据结构。sCrypt 的标准库提供了一个实现了 Map 数据结构的库合约 HashedMap。概念上与java中的map,python中的字典类型类似,但在使用上有一些区别。

HashedMap定义

在 HashedMap 中, key 和 value 的类型没有限制,可以是整型、字节、布尔等基本数据类型,也可以是数组、结构体等复杂数据结构。定义 HashedMap 的语法如下:

  1. 完整定义

HashedMap<int, int> map = new HashedMap<int, int>(b'')
  1. 右边简写定义

HashedMap<int, int> map = new HashedMap(b'')
  1. 使用 auto 关键字定义

auto map = new HashedMap<int, int>(b'')

序列化

data() 方法可以将 HashedMap 序列化成 bytes 字节。scryptlib 也提供了对应的 toData(map) 函数。用来在链外序列化 map。

下面是一个将 HashedMap 作为状态的有状态合约。

contract StateMapTest {

    @state
    bytes _mpData;  //Save the serialized data of the map

    // Add key-value pairs to the map
    public function insert(MapEntry entry, SigHashPreimage preimage) {
        require(Tx.checkPreimage(preimage));
        HashedMap<int, int> map = new HashedMap(this._mpData);
        require(map.set(entry.key, entry.val, entry.keyIndex));
        require(this.passMap(map.data(), preimage));
    }

    // update key-value pairs in the map
    public function update(MapEntry entry, SigHashPreimage preimage) {
        require(Tx.checkPreimage(preimage));
        HashedMap<int, int> map = new HashedMap(this._mpData);
        require(map.set(entry.key, entry.val, entry.keyIndex));
        require(this.passMap(map.data(), preimage));
    }

    // delete key-value pairs in the map
    public function delete(int key, int keyIndex, SigHashPreimage preimage) {
        require(Tx.checkPreimage(preimage));
        HashedMap<int, int> map = new HashedMap(this._mpData);
        require(map.delete(key, keyIndex));
        // Serialize map, update state
        require(this.passMap(map.data(), preimage));
    }

    // update state _mpData, and build a output contains new state
    function passMap(bytes newData, SigHashPreimage preimage) : bool {
        this._mpData = newData;
        bytes outputScript = this.getStateScript();
        bytes output = Util.buildOutput(outputScript, Util.value(preimage));
        return (hash256(output) == Util.hashOutputs(preimage));
    }
}

添加元素

HashedMap 合约提供了很多有用的方法,添加键值对(key-value)可以使用 HashedMap 合约的 set(K key, V val, int keyIndex) 方法。与其它语言的 map 不同的是添加键值需要传递 keyIndex 参数。该参数可以通过 scryptlib 提供的 findKeyIndex 函数在链下计算。首先我们需要在链外维护一个和链上保存同步的 map,并把要添加的元素先添加到该 map 中,然后使用 findKeyIndex 计算出 keyIndex。

scrypt:

public function insert(MapEntry entry, SigHashPreimage preimage) {
    require(Tx.checkPreimage(preimage));
    HashedMap<int, int> map = new HashedMap(this._mpData);
    require(map.set(entry.key, entry.val, entry.keyIndex));
    require(this.passMap(map.data(), preimage));
}

typescript:

let map = new Map<number, number>();
map.set(key, val);  //先把键值对添加到链外的 map

const tx = buildTx(map);
const preimage = getPreimage(tx, mapTest.lockingScript, inputSatoshis)
const result = mapTest.insert(new MapEntry({
    key: key,
    val: val,
    keyIndex: findKeyIndex(map, key) // 获取 `keyIndex` 参数
}), preimage).verify()
expect(result.success, result.error).to.be.true;

mapTest._mpData = toData(map)

如果把所有元素添加到添加到 map 之后,再开始计算 keyIndex,那需要先将 map 转换成有序的数组。 使用以下代码可以直接转换:

const mapEntrys = Array.from(sortmap(map), ([key, val]) => ({ key, val }))
    .map((entry, index) => {
        ...entry,
        keyIndex: index
    })

更新元素

更新元素和添加元素一样,都使用 HashedMap 合约的 set(K key, V val, int keyIndex) 方法。

删除元素

使用 HashedMap 合约的 delete(K key, int keyIndex) 方法来删除元素。如果删除的元素不存在会返回失败。同样需要在链外使用 findKeyIndex(map, key) 函数来计算 keyIndex。

scrypt:

public function delete(int key, int keyIndex, SigHashPreimage preimage) {
    require(Tx.checkPreimage(preimage));
    HashedMap<int, int> map = new HashedMap(this._mpData);
    require(map.delete(key, keyIndex));
    require(this.passMap(map.data(), preimage));
}

typescript:

const keyIndex = findKeyIndex(map, key);  //从链外map删除之前,先计算出keyIndex,并保存
map.delete(key);

const tx = buildTx(map);
const preimage = getPreimage(tx, mapTest.lockingScript, inputSatoshis)

const result = mapTest.delete(key, keyIndex, preimage).verify()  // 调用合约的删除方法需要提供key, keyIndex
expect(result.success, result.error).to.be.true;

mapTest._mpData = toData(map)

存储模型

HashedMap 只存储 key 和 value 的哈希。无法通过 HashedMap 的序列化 data 反序列出原始的键值对。这对于一些注重隐私性的应用场景很有帮助。

可以通过从外部传入原始键值对,并调用 canGet(key, val, keyIndex) 接口来检查是否包含某个键值对。

require(map.canGet(key, val, keyIndex));

或者通过 has(key, keyIndex) 接口检查是否包含某个键。

require(map.has(key, keyIndex));

HashedMap

Map is a very commonly used data structure. sCrypt's standard library provides a library contract HashedMap that implements the Map data structure. The concept is similar to the map in java and the dictionary in python, but there are some differences in usage.

Definition of HashedMap

In HashedMap, there are no restrictions on the types of key and value. They can be basic data types such as integers, bytes, and booleans, or they can be complex data structures such as arrays and structures. The syntax for defining HashedMap is as follows:

  1. Full definition

HashedMap<int, int> map = new HashedMap<int, int>(b'')
  1. Abbreviated definition on the right

HashedMap<int, int> map = new HashedMap(b'')
  1. Use the auto keyword definition

auto map = new HashedMap<int, int>(b'')

Serialization

The data() method can serialize HashedMap into bytes. scryptlib also provides the corresponding toData(map) function. Used to serialize the HashedMap off-chain.

Below is a stateful contract with HashedMap as the state.

contract StateMapTest {

    @state
    bytes _mpData;  //Save the serialized data of the map

    // Add key-value pairs to the map
    public function insert(MapEntry entry, SigHashPreimage preimage) {
        require(Tx.checkPreimage(preimage));
        HashedMap<int, int> map = new HashedMap(this._mpData);
        require(map.set(entry.key, entry.val, entry.keyIndex));
        require(this.passMap(map.data(), preimage));
    }

    // update key-value pairs in the map
    public function update(MapEntry entry, SigHashPreimage preimage) {
        require(Tx.checkPreimage(preimage));
        HashedMap<int, int> map = new HashedMap(this._mpData);
        require(map.set(entry.key, entry.val, entry.keyIndex));
        require(this.passMap(map.data(), preimage));
    }

    // delete key-value pairs in the map
    public function delete(int key, int keyIndex, SigHashPreimage preimage) {
        require(Tx.checkPreimage(preimage));
        HashedMap<int, int> map = new HashedMap(this._mpData);
        require(map.delete(key, keyIndex));
        // Serialize map, update state
        require(this.passMap(map.data(), preimage));
    }

    // update state _mpData, and build a output contains new state
    function passMap(bytes newData, SigHashPreimage preimage) : bool {
        this._mpData = newData;
        bytes outputScript = this.getStateScript();
        bytes output = Util.buildOutput(outputScript, Util.value(preimage));
        return (hash256(output) == Util.hashOutputs(preimage));
    }
}

Add Element

The HashedMap contract provides many useful methods. To add key-value pairs, you can use the set(K key, V val, int keyIndex) method of the HashedMap contract. Unlike map in other languages, adding key-value pairs requires passing the keyIndex parameter, which can be calculated off-chain by the findKeyIndex function provided by scryptlib. First, we need to maintain a map outside the chain that is synchronized with the chain, and add the elements to be added to the map first. Then use findKeyIndex to calculate keyIndex.

scrypt:

public function insert(MapEntry entry, SigHashPreimage preimage) {
    require(Tx.checkPreimage(preimage));
    HashedMap<int, int> map = new HashedMap(this._mpData);
    require(map.set(entry.key, entry.val, entry.keyIndex));
    require(this.passMap(map.data(), preimage));
}

typescript:

let map = new Map<number, number>();
map.set(key, val);  // First add the key-value pair to the map outside the chain

const tx = buildTx(map);
const preimage = getPreimage(tx, mapTest.lockingScript, inputSatoshis)
const result = mapTest.insert(new MapEntry({
    key: key,
    val: val,
    keyIndex: findKeyIndex(map, key) // Get the `keyIndex` parameter
}), preimage).verify()
expect(result.success, result.error).to.be.true;

mapTest._mpData = toData(map)

If you add all the elements to the map before starting to calculate the keyIndex, you need to convert the map into an ordered array first. Use the following code to convert directly:

const mapEntrys = Array.from(sortmap(map), ([key, val]) => ({ key, val }))
    .map((entry, index) => {
        ...entry,
        keyIndex: index
    })

Update Element

Updating elements is the same as adding elements, using the set(K key, V val, int keyIndex) method of the HashedMap contract.

Delete Element

Use the delete(K key, int keyIndex) method of the HashedMap contract to delete elements. If the deleted element does not exist, it will return failure. It is also necessary to use the findKeyIndex(map, key) function outside the chain to calculate the keyIndex.

scrypt:

public function delete(int key, int keyIndex, SigHashPreimage preimage) {
    require(Tx.checkPreimage(preimage));
    HashedMap<int, int> map = new HashedMap(this._mpData);
    require(map.delete(key, keyIndex));
    require(this.passMap(map.data(), preimage));
}

typescript:

const keyIndex = findKeyIndex(map, key);  // Before deleting from the off-chain map, first calculate the keyIndex and save it
map.delete(key);

const tx = buildTx(map);
const preimage = getPreimage(tx, mapTest.lockingScript, inputSatoshis)

const result = mapTest.delete(key, keyIndex, preimage).verify()  // To call the delete method of the contract, you need to provide the key and keyIndex
expect(result.success, result.error).to.be.true;

mapTest._mpData = toData(map)

Storage Model

HashedMap only stores the hash of key and value. It is not possible to deserialize the original key-value pairs through the serialization of data of HashedMap. This is very helpful for some privacy-focused application scenarios. You can pass in the original key-value pair from the outside and call the canGet(key, val, keyIndex) interface to check whether a key-value pair is included.

require(map.canGet(key, val, keyIndex));

Or check whether a key is included through the has(key, keyIndex) interface.

require(map.has(key, keyIndex));
BIP68
meep debugger
meep debugger
GitHub repository
Examples page
Language Examples page
Memo.cash
Licho's Mecenas example
Type Casting
to()
withOpReturn()
to()
withOpReturn()
send()
withHardcodedFee()

Contract Instantiation

Before interacting with smart contracts on the BCH network, the CashScript SDK needs to instantiate a Contract object. This is done by providing the contract's information and constructor arguments. After this instantiation, the CashScript SDK can interact with BCH contracts.

Contract class

The Contract class is used to represent a CashScript contract in a JavaScript object. These objects can be used to retrieve information such as the contract's address and balance. They can be used to interact with the contract by calling the contract's functions.

Constructor

new Contract(
  artifact: Artifact,
  constructorArgs: Argument[],
  provider?: NetworkProvider,
)

A CashScript contract can be instantiated by providing an Artifact object, a list of constructor arguments, and optionally a NetworkProvider.

Example

const { Contract, ElectrumNetworkProvider } = require('cashscript');
const { compileFile } = require('cashc');

// Import an artifact JSON file that was compiled earlier
const P2PKH = require('./p2pkh.json');

// Or compile a contract file
const P2PKH = compileFile(path.join(__dirname, 'p2pkh.cash'));

const provider = new ElectrumNetworkProvider('testnet');
const contract = new Contract(P2PKH, [alicePkh], provider);

address

contract.address: string

A contract's address can be retrieved through the address member field.

Example

console.log(contract.address)

opcount

contract.opcount: number

The number of opcodes in the contract's bytecode can be retrieved through the opcount member field. This is useful to ensure that the contract is not too big, since Bitcoin Cash smart contracts can contain a maximum of 201 opcodes.

Example

assert(contract.opcount <= 201)

bytesize

contract.bytesize: number

The size of the contract's bytecode in bytes can be retrieved through the bytesize member field. This is useful to ensure that the contract is not too big, since Bitcoin Cash smart contracts can be 520 bytes at most.

Example

console.log(contract.bytesize)

getRedeemScriptHex()

contract.getRedeemScriptHex: string

Returns the contract's redeem script encoded as a hex string.

Example

console.log(contract.getRedeemScriptHex())

getBalance()

async contract.getBalance(): Promise<number>

Returns the total balance of the contract in satoshis. Both confirmed and unconfirmed balance is included in this figure.

Example

const contractBalance = await contract.getBalance()

GetUtxos()

async contract.getUtxos(): Promise<Utxo[]>

Returns all UTXOs that can be spent by the contract. Both confirmed and unconfirmed UTXOs are included.

Example

const utxos = await contract.getUtxos()

Contract functions

contract.functions.<functionName>(...args: Argument[]): Transaction

The main way to use smart contracts once they have been instantiated is through the functions defined in the CashScript source code. These functions can be found by their name under functions member field of a contract object. To call these functions, the parameters need to match ones defined in the CashScript code.

Example

import { alice } from './somewhere';

const tx = await contract.functions
  .transfer(new SignatureTemplate(alice))
  .to('bitcoincash:qrhea03074073ff3zv9whh0nggxc7k03ssh8jv9mkx', 10000)
  .send()

SignatureTemplate

new SignatureTemplate(signer: Keypair | Uint8Array | string, hashtype?: HashType)

You may notice the SignatureTemplate object in the example above. When a contract function has a sig parameter, it requires a cryptographic signature over the spending transaction. But to generate this signature, the transaction needs to be built first, which is not yet the case when a contract function is first called.

Example

const wif = 'L4vmKsStbQaCvaKPnCzdRArZgdAxTqVx8vjMGLW5nHtWdRguiRi1';
const sig = new SignatureTemplate(wif, HashType.SIGHASH_ALL);

NetworkProvider

The CashScript SDK needs to connect to the BCH network to perform certain operations, like retrieving the contract's balance, or sending transactions. All network functionality that the CashScript SDK needs is encapsulated in a network provider. This allows different network providers to be used and makes it easy to swap out dependencies.

ElectrumNetworkProvider

new ElectrumNetworkProvider(network?: Network, electrum?: ElectrumCluster)

Example

const provider = new ElectrumProvider('testnet');

FullStackNetworkProvider

new FullStackNetworkProvider(network: Network, bchjs: BCHJS)

Example

const BCHJS = require('@psf/bch-js');

const restURL = 'https://api.fullstack.cash/v3/';
const apiToken = 'eyJhbGciO...'; // Your JWT token here.
const bchjs = new BCHJS({ restURL, apiToken });

const provider = new FullStackNetworkProvider('mainnet', bchjs);

BitboxNetworkProvider

new BitboxNetworkProvider(network: Network, bitbox: BITBOX)

Example

const BITBOX = require('bitbox-sdk');

const bitbox = new BITBOX({ restURL: 'https://rest.bitcoin.com/v2/' });
const provider = new FullStackNetworkProvider('mainnet', bitbox);

BitcoinRpcNetworkProvider

new BitcoinRpcNetworkProvider(network: Network, url: string, options?: any)

The BitcoinRpcNetworkProvider uses a direct connection to a BCH node. Note that a regular node does not have indexing, so any address of interest (e.g. the contract address) need to be registered by the node before sending any funds to those addresses. Because of this it is recommended to use a different network provider unless you have a specific reason to use the RPC provider.

Example

const provider = new BitcoinRpcNetworkProvider('mainnet', 'http://localhost:8332');

Custom NetworkProviders

A big strength of the NetworkProvider setup is that it allows you to implement custom providers. So if new BCH libraries are created in the future, it is simple to use them with CashScript. This also potentially enables the CashScript SDK to be used with other (partially) compatible networks, such as BTC or BSV.

NetworkProvider interface

interface NetworkProvider {
  /**
   * Variable indicating the network that this provider connects to.
   */
  network: Network;

  /**
   * Retrieve all UTXOs (confirmed and unconfirmed) for a given address.
   * @param address The CashAddress for which we wish to retrieve UTXOs.
   * @returns List of UTXOs spendable by the provided address.
   */
  getUtxos(address: string): Promise<Utxo[]>;

  /**
   * @returns The current block height.
   */
  getBlockHeight(): Promise<number>;

  /**
   * Retrieve the Hex transaction details for a given transaction ID.
   * @param txid Hex transaction ID.
   * @throws {Error} If the transaction does not exist
   * @returns The full hex transaction for the provided transaction ID.
   */
  getRawTransaction(txid: string): Promise<string>;

  /**
   * Broadcast a raw hex transaction to the network.
   * @param txHex The raw transaction hex to be broadcast.
   * @throws {Error} If the transaction was not accepted by the network.
   * @returns The transaction ID corresponding to the broadcast transaction.
   */
  sendRawTransaction(txHex: string): Promise<string>;
}

type Network = 'mainnet' | 'testnet3' | 'testnet4' | 'chipnet' | 'regtest';

interface Utxo {
  txid: string;
  vout: number;
  satoshis: number;
}

CashScript Compiler

Generally CashScript contracts are compiled to an Artifact JSON file using the CLI compiler. As an alternative to this, CashScript contracts can be compiled from within JavaScript apps using the cashc package. This package needs to be installed separately and exports two compilation functions.

npm install cashc

compileFile()

compileFile(sourceFile: string): Artifact

Compiles a CashScript contract from a source file. This is the recommended compile method if you're using Node.js and you have a source file available.

Example

const P2PKH = compileFile(path.join(__dirname, 'p2pkh.cash'));

compileString()

compileString(sourceCode: string): Artifact

Compiles a CashScript contract from a source code string. This is the recommended compile method if you're building a webapp, because compileFile() only works from a Node.js context. This is also the recommended method if no source file is locally available (e.g. the source code is retrieved with a REST API).

const baseUrl = 'https://raw.githubusercontent.com/Bitcoin-com/cashscript'
const result = await fetch(`${baseUrl}/master/examples/p2pkh.cash`);
const source = await result.text();

const P2PKH = compileString(source);

Migration Notes

v0.6 to v0.7

cashc compiler

The older preimage-based introspection/covenants have been replaced with the newly supported native introspection/covenants. This has significant consequences for any existing covenant contracts, but in general this native introspection makes covenants more accessible, flexible and efficient. See below for a list of changes. In some cases there is no one to one mapping between the old introspection and the new introspection methods, so the logic of the smart contracts will need to be refactored as well.

Covenant variables

  • tx.version and tx.locktime used to be bytes4, but are now int.

  • tx.hashtype has been removed and can no longer be accessed.

  • tx.hashPrevouts and tx.outpoint have been removed. Instead, the outpoints of individual inputs can be accessed with tx.inputs[i].outpointTransactionHash and tx.inputs[i].outpointIndex. The index of the active input can be accessed with this.activeInputIndex.

  • tx.hashSequence and tx.sequence have been removed. Instead, the sequence numbers of individual inputs can be accessed with tx.inputs[i].sequenceNumber. The index of the active input can be accessed with this.activeInputIndex.

  • tx.bytecode has been renamed to this.activeBytecode

  • tx.value has been removed. Instead, the value of individual inputs can be accessed with tx.inputs[i].value. The index of the active input can be accessed with this.activeInputIndex.

  • tx.hashOutputs has been removed. Instead, the value and locking bytecode of individual outputs can be accessed separately with tx.outputs[i].value and tx.outputs[i].lockingBytecode.

Additionally, it is now possible to access the number of inputs and outputs with tx.inputs.length and tx.outputs.length. It is also possible to access individual inputs' locking bytecode and unlocking bytecode with tx.inputs[i].lockingBytecode and tx.inputs[i].unlockingBytecode. It is also no longer a requirement to have a signature check somewhere in the contract in order to use this introspection/covenant functionality.

Utility classes

OutputP2PKH, OutputP2SH and OutputNullData have been replaced by LockingBytecodeP2PKH, LockingBytecodeP2SH and LockingBytecodeNullData respectively. These new classes only produce the locking bytecode, rather than the full output (including value). This means that the locking bytecode and value of outputs need to be checked separately.

Other changes

Casting from sig to datasig has been removed since that was only useful for old-style covenants. If, for any reason, you do want to cast a sig to a datasig you will need to manually cut the hashtype off the end and update datasig(s) to s.split(s.length - 1)[0].

Example

Since the new covenant functionality is very different from the existing, it may be useful to see a complex covenant contract refactored from the old way to the new way.

```solidity title="Mecenas.cash v0.6.0" pragma cashscript ^0.6.0;

contract Mecenas(bytes20 recipient, bytes20 funder, int pledge, int period) { function receive(pubkey pk, sig s) { require(checkSig(s, pk)); require(tx.age >= period);

    int minerFee = 1000;
    int intValue = int(bytes(tx.value));

    if (intValue <= pledge + minerFee) {
        // The contract has less value than the pledge, or equal.
        // The recipient must claim all of of it.

        bytes8 amount1 = bytes8(intValue - minerFee);
        bytes34 out1 = new OutputP2PKH(amount1, recipient);
        require(hash256(out1) == tx.hashOutputs);
    } else {
        // The contract has more value than the pledge. The recipient must
        // also add one change output sending the remaining coins back
        // to the contract.

        bytes8 amount1 = bytes8(pledge);
        bytes8 amount2 = bytes8(intValue - pledge - minerFee);
        bytes34 out1 = new OutputP2PKH(amount1, recipient);
        bytes32 out2 = new OutputP2SH(amount2, hash160(tx.bytecode));
        require(hash256(out1 + out2) == tx.hashOutputs);
    }
}

function reclaim(pubkey pk, sig s) {
    require(hash160(pk) == funder);
    require(checkSig(s, pk));
}

}


```solidity title="Mecenas.cash 0.7.0"
contract Mecenas(bytes20 recipient, bytes20 funder, int pledge, int period) {
    function receive() {
        require(tx.age >= period);

        // Check that the first output sends to the recipient
        bytes25 recipientLockingBytecode = new LockingBytecodeP2PKH(recipient);
        require(tx.outputs[0].lockingBytecode == recipientLockingBytecode);

        // Calculate the value that's left
        int minerFee = 1000;
        int currentValue = tx.inputs[this.activeInputIndex].value;
        int changeValue = currentValue - pledge - minerFee;

        // If there is not enough left for *another* pledge after this one,
        // we send the remainder to the recipient. Otherwise we send the
        // remainder to the recipient and the change back to the contract
        if (changeValue <= pledge + minerFee) {
            require(tx.outputs[0].value == currentValue - minerFee);
        } else {
            require(tx.outputs[0].value == pledge);
            bytes changeBytecode = tx.inputs[this.activeInputIndex].lockingBytecode;
            require(tx.outputs[1].lockingBytecode == changeBytecode);
            require(tx.outputs[1].value == changeValue);
        }
    }

    function reclaim(pubkey pk, sig s) {
        require(hash160(pk) == funder);
        require(checkSig(s, pk));
    }
}

v0.5 to v0.6

cashc compiler

The exports for library usage of cashc have been updated. All utility-type exports have been moved to the @cashscript/utils package, but they are still accessible from the utils export from cashc. Note that the recommended use of cashc is still the CLI, not the NPM package.

In v0.5 you could encode a string like this:

const { Data } = require('cashc');

const encodedString = Data.encodeString('Hello World');

While for v0.6 you'd need to use the utils export or @cashscript/utils:

const { utils } = require('cashc');
const { encodeString } = require('@cashscript/utils');

const encodedString = utils.encodeString('Hello World');
const encodedString = encodeString('Hello World');

Compilation functions used to be exported as part of the CashCompiler object, but are now exported as standalone functions.

In v0.5 compilation looked like this:

const { CashCompiler } = require('cashc');

const Mecenas = CashCompiler.compileFile(path.join(__dirname, 'mecenas.cash'));

In v0.6, this needs to be changed to this:

const { compileFile } = require('cashc');

const Mecenas = compileFile(path.join(__dirname, 'mecenas.cash'));

CashScript SDK

The CashScript SDK no longer depends on cashc and no longer exports the CashCompiler object. This reflects the recommended usage where the CLI is used for compilation and the artifact JSON is saved. Then this artifact JSON can be imported into the CashScript SDK. If you prefer to compile your contracts from code, you need to add cashc as a dependency and use its compilation functionality.

v0.4 to v0.5

CashScript SDK

The contract instantiation flow has been refactored to enable compatibility with more BCH libraries and simplify the different classes involved.


In v0.4 a contract could be compiled or imported using Contract.compile() or Contract.import(), which returned a Contract object. On that Contract object contract.new(...args) could be called, which returned an Instance object. In the v0.5 release, the Contract and Instance objects have been merged and simplified, while the compilation has been extracted into its own class.

In v0.4, contract instantiation looked like this:

const { Contract } = require('cashscript');

const Mecenas = Contract.compile(path.join(__dirname, 'mecenas.cash'), 'testnet');
const contract = Mecenas.new(alicePkh, bobPkh, 10000);

In v0.5, this needs to be changed to look like this:

const { CashCompiler, ElectrumNetworkProvider, Contract } = require('cashscript');

const Mecenas = CashCompiler.compileFile(path.join(__dirname, 'mecenas.cash'));
const provider = new ElectrumNetworkProvider('testnet');
const contract = new Contract(Mecenas, [alicePkh, bobPkh, 10000], provider);

  • Transaction object's .send() function now returns either a libauth Transaction or raw hex string rather than a BITBOX Transaction. If it is necessary, the raw hex string can be imported into libraries such as BITBOX to achieve similar functionality as before.

  • In v0.4.1, Sig was deprecated in favour of SignatureTemplate. In v0.5.0, the deprecated class has been removed. All occurrences of Sig should be replaced with SignatureTemplate.

v0.3 to v0.4

cashc compiler

In v0.3, casting an int type to a bytes would perform an NUM2BIN operation, padding the value to 8 bytes. This made bytes(10) equivalent to bytes8(10). From v0.4.0 onwards, casting to an unbounded bytes type is only a semantic cast, indicating that the int value should be treated as a bytes value.

  • If you need the old behaviour, you should change all occurrences of bytes(x) to bytes8(x).

CashScript SDK

The entire Transaction flow has been refactored to a more fluent chained TransactionBuilder API.

  • All occurrences of .send(to, amount) should be replaced with .to(to, amount).send().

  • All occurrences of .send(outputs) should be replaced with .to(outputs).send().

  • Alternatively, the list of outputs can be split up between several .to() calls.

  • If any of the outputs contain opReturn outputs, these should be added separately using .withOpReturn(chunks)

  • The same transformations are applicable to all .meep() calls.

  • The meep() function previously logged the meep command automatically, but now it returns the command as a string, so you should console.log() the command separately.

  • All transaction options previously included in the TxOptions object should now be provided using chained functions.

  • The time option should be provided using the .withTime(time) function.

  • The age option should be provided using the .withAge(age) function.

  • The fee option should be provided using the .withHardcodedFee(fee) function.

  • The minChange option should be provided using the .withMinChange(minChange) function.

In v0.2.2, Contract.fromCashFile() and Contract.fromArtifact() were deprecated in favour of Contract.compile() and Contract.import(). In v0.4.0, the deprecated functions have been removed.

  • All occurrences of Contract.fromCashFile() should be replaced with Contract.compile().

  • All occurrences of Contract.fromArtifact() should be replaced with Contract.import().

preimage

The preimage under the script opcode OP_CODESEPARATOR

The signature checked by OP_CHECKSIG is the signature of the hash of the preimage. The preimage contains outputs of the entire transaction, input and the locking script. Usually the locking script is complete. But if OP_CHECKSIG has executed OP_CODESEPARATOR before, then the preimage only contains the locking script from the position of the most recently executed OP_CODESEPARATOR until the end of the script, that is, only part of the locking script is included. Using this feature can be used to reduce the size of the preimage, thereby reducing the size of the entire transaction.

The sCrypt standard library Tx provides the following functions to check this preimage that only contains partial locking scripts:

Function
Description

Tx.checkPreimageOCS

The preimage to be checked only contains part of the locking script. The signature type checked by default is SigHash.ALL | SigHash.FORKID, and does not support custom signature type.

Tx.checkPreimageSigHashTypeOCS

Support custom signature type

Tx.checkPreimageAdvancedOCS

Support custom signature type and custom temporary key

Tx.checkPreimageOptOCS

Optimized version of Tx.checkPreimageOCS

Tx.checkPreimageOptOCS_

Optimized version of Tx.checkPreimageOCS, support custom signature type

The following is an example of using Tx.checkPreimageOCS() to check the preimage:

contract CheckLockTimeVerifyOCS {
    int time;

    public function unlock(SigHashPreimage preimage) {
        require(Tx.checkPreimageOCS(preimage));
        require(SigHash.nLocktime(preimage) > this.time);
    }
}

The following is an example of how to deploy and call the CheckLockTimeVerifyOCS contract:

const amount = 2000

// get locking script
const CLTVOCS = buildContractClass(loadDesc('cltvOCS_debug_desc.json'));
cltv = new CLTVOCS(1422674);

// lock fund to the script
const lockingTx = await deployContract(cltv, amount)
console.log('funding txid:      ', lockingTx.id);

// unlock
const unlockingTx = new bsv.Transaction();

unlockingTx.addInput(createInputFromPrevTx(lockingTx))
    .setLockTime(1422674 + 1)
    .change(privateKey.toAddress())
    .setInputScript(0, (tx, output) => {
        const preimage = getPreimage(tx, output.script.subScript(0), output.satoshis)
        return cltv.spend(new SigHashPreimage(toHex(preimage))).toScript()
    })
    .seal()


const unlockingTxid = await sendTx(unlockingTx)
console.log('unlocking txid:   ', unlockingTxid)

console.log('Succeeded on testnet')

When using getPreimage to calculate the preimage, the entire lock script cannot be passed. Need to use subScript(opsIndex: number) to tailor the locking script. The parameter index is the index of OP_CODESEPARATOR in this script, which is the number of OP_CODESEPARATOR. It should be noted that this function does not consider dynamic execution.

Release Notes

v0.7.5

CashScript SDK

  • :bug: Fix a bug with chipnet connection

v0.7.4

cashc compiler

  • :hammer_and_wrench: Internal refactoring

CashScript SDK

  • :bug: Fix a bug with ESM exports

v0.7.3

CashScript SDK

  • :sparkles: Add "chipnet" network option to ElectrumNetworkProvider, used to connect to the May 2023 testnet.

  • :hammer_and_wrench: Renamed network options "testnet" & "staging" to "testnet3" and "testnet4" respectively. Old options will be removed in a future release.

v0.7.2

cashc compiler

  • :bug: Fix bug where contracts using checkMultiSig() were unspendable.

CashScript SDK

  • :sparkles: Add signatureAlgorithm parameter to SignatureTemplate to allow ECDSA signatures.

v0.7.1

@cashscript/utils

  • :bug: Fix bug where 64bit integers could not be decoded.

v0.7.0

cashc compiler

  • :sparkles: Add destructuring assignments, e.g. bytes2 left, bytes1 right = 0x123456.split(2)

  • :sparkles: Add constant keyword, e.g. int constant x = 10;

  • :sparkles: Add multiplication, e.g. int x = 5 * 5

  • :sparkles: Add native introspection/covenants

  • :boom: BREAKING: Remove all old introspection/covenant functionality (tx.version, tx.hashPrevouts, tx.hashSequence, tx.outpoint, tx.bytecode, tx.value, tx.sequence, tx.hashOutputs, tx.locktime, tx.hashtype, OutputP2PKH, OutputP2SH, OutputNullData)

  • :boom: BREAKING: Remove sig to datasig casting since this was only useful for old covenants

  • :bug: Fix ESM build

CashScript SDK

  • :sparkles: Add "staging" network option to ElectrumNetworkProvider, used to connect to the May 2022 testnet

  • :hammer_and_wrench: Deprecate old introspection/covenant functionality. You can still use pre-0.7 contracts with the new SDK, but this support will be removed in a future release.

  • :boom: BREAKING: arguments of type datasig must be 64 bytes in length, effectively enforcing Schnorr

  • :bug: Fix ESM build

  • :bug: Small fixes


v0.6.5

cashc compiler

  • :bug: Fix cashc version

v0.6.4

cashc compiler

  • :sparkles: Add byte type alias for bytes1

v0.6.3

  • :hammer_and_wrench: Use ES2015 for the "module" output for better compatibility

v0.6.2

CashScript SDK

  • :bug: Fix typing issue with BitcoinRpcNetworkProvider

v0.6.1

CashScript SDK

  • :bug: Fix bug with incorrect fee calculation when providing custom fee per byte

v0.6.0

cashc compiler

  • :sparkles: Add date literal (gets converted to int timestamp)

  • :hammer_and_wrench: Update ParseError messages

  • :bug: The final statement in a contract now MUST be a require statement (in all branches)

  • :bug: Empty contracts and functions are now considered invalid

  • :bug: Fix bug where certain covenants could become unspendable due to incorrect bytesize calculation

  • :boom: BREAKING: Remove --args parameter from the CLI, since this is too error prone with the recent changes in mind

  • :boom: BREAKING: Restructure exports

CashScript SDK

  • :sparkles: Add BitcoinRpcNetworkProvider that connects to a BCH node RPC

  • :boom: BREAKING: Remove dependency on cashc and remove CashCompiler export


v0.5.7

cashc compiler

  • :bug: Better error reporting for parsing/lexing errors

v0.5.6

cashc compiler

  • :bug: Make compiler fail early when encountering lexing/parsing errors, rather than performing error recovery

  • :bug: Allow empty hex literals (i.e. 0x)

v0.5.5

CashScript SDK

  • :sparkles: Add 'regtest' as a possible network for NetworkProviders.

v0.5.4

  • :package: Add dual build system (CommonJS and ES Modules) to accommodate tree-shaking.

v0.5.3

CashScript SDK

  • :sparkles: Add getRedeemScriptHex() function to the Contract class.

  • :bug: Fix a bug where transaction locktime could not specifically be set to 0.

  • :bug: Fix a bug where signature buffers were not checked for size.

v0.5.2

cashc compiler

  • :bug: Fix a bug where an incorrect error message was displayed in Firefox when an incompatible pragma version was used.

v0.5.1

CashScript SDK

  • :sparkles: The .send() function now returns a TransactionDetails object. This extends the libauth Transaction with added txid and hex fields.

  • Because it extends the previous return type, this is backwards compatible.

  • Since this now returns the transaction hex as a field, using .send(true) to return the transaction hex is deprecated and will be removed in a future release.

  • :bug: Improve reliability of the ElectrumNetworkProvider when sending multiple concurrent requests.


v0.5.0

CashScript SDK

CashScript used to be very tightly coupled with BITBOX. This proved to be problematic after maintenance for BITBOX was stopped. The main objective of this update is to allow CashScript to be used with many different BCH libraries.

  • :sparkles: Add withoutChange() function to disable change outputs for a transaction.

  • :sparkles: SignatureTemplate can now be used with BITBOX keypairs, bitcore-lib-cash private keys, WIF strings, and raw private key buffers, rather than only BITBOX.

  • :boom: Remove Sig alias for SignatureTemplate that was deprecated in v0.4.1.

  • :boom: BREAKING: Refactor contract instantiation flow

  • A contract is now instantiated by providing a compiled artifact, constructor arguments and an optional network provider.

  • :boom: BREAKING: Remove the artifacts 'networks' field and .deployed() functionality, This proved to be confusing and is better suited to be handled outside of the CashScript SDK.

  • :boom: BREAKING: .send() now returns a libauth Transaction instead of a BITBOX Transaction object. Alternatively a raw flag can be passed into the function to return a raw hex string.

  • :hammer_and_wrench: Removed BITBOX as a dependency in favour of libauth for utility functions.


v0.4.4

cashc compiler

  • :bug: Fix a bug where covenants would not always get verified correctly when the first require(checkSig(...)) statement was inside a branch.

v0.4.3

cashc compiler

  • :racehorse: Add compiler optimisations.

v0.4.2

  • Re-add README files to NPM that were accidentally removed in the v0.4.0 release.

v0.4.1

cashc compiler

  • :racehorse: Add optimisations to bitwise operators.

  • :shell: New CLI arguments.

  • Add --opcount|-c flag that displays the number of opcodes in the compiled bytecode.

  • Add --size|-s flag that displays the size in bytes of the compiled bytecode.

  • :symbols: Add trailing comma support.

CashScript SDK

  • :name_badge: Rename Sig to SignatureTemplate to better convey its meaning.

  • Sig still exists for backward compatibility, but is deprecated and will be removed in a later release.


v0.4.0

cashc compiler

  • :sparkles: Add .reverse() member function to bytes and string types.

  • :sparkles: Add bitwise operators &, ^, |.

  • :sparkles: Allow casting int to variable size bytes based on size parameter.

  • :boom: BREAKING: Casting from int to unbounded bytes type now does not perform OP_NUM2BIN. Instead it is a purely semantic cast to signal that an integer value should be treated as a bytes value.

  • :horse_racing: Compiler optimisations.

  • Use NUMEQUALVERIFY for the final function in a contract.

  • Only drop the final VERIFY if the remaining stack size is less than 5.

  • Pre-calculate OutputNullData argument size.

  • :bug: Fix a bug where return type of sha1 was incorrectly marked as bytes32.

  • :bug: Data.decodeBool only treated numerical zero as false, now any zero-representation is considered false (e.g. 0x0000, -0, ...).

CashScript SDK

  • :sparkles: Add ability to provide hardcoded inputs to the transaction rather than use CashScript's coin selection.

  • :boom: BREAKING: Refactor the transaction flow to a fluent API

  • Remove the TxOptions argument and other arguments to the Transaction send() function.

  • Instead these parameters are passed in through fluent functions from(), to(), withOpReturn(), withAge(), withTime(), withHardcodedFee(), withFeePerByte() and withMinChange().

  • After specifying at least one output with either to() or withOpReturn()the transaction is ready. From here the transaction can be sent to the network with the send() function, the transaction hex can be returned with the build() function, or the meep debugging command can be returned with the meep() function.

  • :boom: Remove Contract.fromCashFile() and Contract.fromArtifact() which were deprecated in favour or Contract.compile() and Contract.import() in v0.2.2.

Migration


v0.3.3

cashc compiler

  • :bug: Fix bug where variables could not reliably be used inside OutputNullData instantiation.


v0.3.2

cashc compiler

  • :sparkles: Add OutputNullData(bytes[] chunks), an output type to enforce OP_RETURN outputs.

  • :shell: CLI improvements

  • The --output|-o flag is now optional, if it is omitted or manually set to -, the artifact will be written to stdout rather than a file.

  • Add --asm|-A flag that outputs only Script in ASM format instead of a full JSON artifact.

  • Add --hex|-h flag that outputs only Script in hex format instead of a full JSON artifact.

  • Add --args|-a flag that allows you to specify constructor arguments that are added to the generated bytecode.

    • :warning: The CLI does not perform type checking on these arguments, so it is recommended to use the CashScript SDK for type safety.

  • :bug: Fix a compilation bug that allowed compilation of "unverified covenants" (#56).

  • :bug: Fix a compilation bug that allowed compilation of OutputP2PKH(...) without new keyword (#57).

CashScript SDK

  • :globe_with_meridians: Browser support! You can now use CashScript inside web projects. Filesystem-based functionality such as compilation from file are not supported due to the nature of web, so CashScript files have to be read in a different way (e.g. Fetch API) and then passed into the CashScript SDK.

  • :purse: Add minChange to transaction options. If this minChange is not reached, the change will be added to the transaction fee instead.


v0.3.1

cashc compiler

  • :warning: Add warnings when a contract exceeds 201 opcodes or 520 bytes.

  • :bug: Fix a bug where an incorrect number of items were dropped from the stack after execution of a branch.

CashScript SDK

  • :sparkles: Improve error handling.

  • Further specified FailedTransactionError into FailedRequireError, FailedSigCheckError, FailedTimeCheckError and a general fallback FailedTransactionError.

  • Add Reason enum with all possible reasons for a Script failure - can be used to catch specific errors.

  • :mag: Add instance.opcount and instance.bytesize fields to all contract instances.

  • :bug: Fix a bug where the size of a preimage was not accounted for in fee calculation for covenants.


v0.3.0

cashc compiler

  • :sparkles: Covenants abstraction! All individual preimage fields can be accessed without manual decoding, passing, and verification.

  • Available fields: tx.version, tx.hashPrevouts, tx.hashSequence, tx.outpoint, tx.bytecode, tx.value, tx.sequence, tx.hashOutputs, tx.locktime, tx.hashtype.

  • When any of these fields is used inside a function, this function is marked covenant: true, and requires a preimage as parameter (automatically passed by CashScript SDK).

  • The correct fields are efficiently cut out of the preimage and made available.

  • The first occurrence of require(checkSig(sig, pubkey)); is identified, and preimage verification is inserted using the same sig/pubkey. Important: if you have multiple checkSig statements, keep in mind that the first will be used for verification.

  • Automatically cuts off VarInt from scriptCode, so tx.bytecode contains the actual contract bytecode.

  • :sparkles: Output instantiation! Automatically construct output formats for covenant transactions.

  • new OutputP2PKH(bytes8 amount, bytes20 pkh)

  • new OutputP2SH(bytes8 amount, bytes20 scriptHash)

  • :bug: Fix bug with invalid output when the final statement in a contract is an if-statement.

CashScript SDK

  • :sparkles: Add fee option to TransactionOptions. This allows you to specify a hardcoded fee for your transaction.

  • :sparkles: Automatically pass in sighash preimage into covenant functions. Important: uses the hashtype of the first signature in the parameters for generation of this preimage.

  • :dizzy: Better fee estimation for transactions with many inputs.


v0.2.3

cashc compiler

  • :bug: Fix a bug where unequal bytes types (e.g. bytes3 & bytes8) could not be concatenated together, as they were considered different types.


v0.2.2

CashScript SDK

  • :bug: Remove minimaldata encoding in OP_RETURN outputs that caused incompatibility with SLP.

  • :name_badge: Renamed Contract.fromCashFile to Contract.compile.

  • The new function allows to pass in a path to a .cash file, or a string of the contract source code.

  • Contract.fromCashFile still exists for backward compatibility, but is deprecated and will be removed in a later release.

  • :name_badge: Renamed Contract.fromArtifact to Contract.import.

  • The new function allows to pass in a path to a .json artifact file, or a JSON object of the artifact.

  • Contract.fromArtifact still exists for backward compatibility, but is deprecated and will be removed in a later release.

  • :hammer_and_wrench: instance.export's file argument is now optional.

  • If it is provided, the artifact is written to the file, if not, it is returned as an object.


v0.2.1

cashc compiler

  • :sparkles: Support bytes types with bounded size, e.g. bytes1, bytes13, bytes32.

  • :bug: Fix bug in bytecode optimisation

CashScript SDK

  • :sparkles: Support bytes types with bounded size, e.g. bytes1, bytes13, bytes32.

  • :bird: Automatically output meep command on failed transaction error.

  • :hammer: Make the hashtype parameter in signature placeholders optional.


v0.2.0

cashc compiler

  • :racehorse: Implement compiler optimisations

  • For the final use of a variable, it is retrieved with OP_ROLL rather than OP_PICK. This removes the need to clean the stack at the end of a contract.

  • Final OP_VERIFY OP_TRUE is removed as there is an implicit OP_VERIFY at the end of a Script.

  • OP_VERIFY is merged with preceding opcode where applicable.

  • Shallow OP_PICK and OP_ROLL are replaced by hardcoded opcodes (e.g. OP_SWAP, OP_DUP).

  • Several other bytecode optimisations.

  • :sparkles: Add pragma keyword to specify intended compiler version.

  • Example: pragma cashscript ^0.2.0;

  • Contract fails to compile when compiler version does not satisfy constraints.

  • :rotating_light: Add CashProof for all individual bytecode optimisations and for example contracts from 0.1.2 to 0.2.0.

  • :bug: Add "default case" for function selection that fixes a vulnerability where people could spend funds by not calling any function.

  • :arrow_up: Update dependencies.

CashScript SDK

  • :arrow_up: Update cashc and other dependencies.


v0.1.2

CashScript SDK

  • :sparkles: Add support for OP_RETURN outputs.

  • :bug: Improved error handling.

  • :bug: Poll for transaction details to make sure it's available.

  • :fire: Enable optional mainnet - NOT RECOMMENDED

  • :hammer: UTXO selection refactor

  • :rotating_light: Improve Transaction testing


v0.1.1

CashScript SDK

  • :bug: Bug fixes with incorrect parameter encoding for string/bool/int types.

v0.1.0

  • :tada: Initial release.

scryptlib

Javascript/TypeScript SDK for integration of Radiant (RAD RXD) Blockchain Smart Contracts written in the sCrypt language.

You can install scryptlib in your project as below:

$ npm install scryptlib

A smart contract is compiled to a locking script template. A contract function call is transformed to an unlocking script. Developers are responsible for setting the locking and unlocking scripts of a transaction properly before sending it to the Bitcoin network. This may include some actions described below:

  • Instantiate locking script: replace the constructor formal parameters, represented by placeholders in the locking script template, with actual parameters/arguments to form the complete locking script.

  • Assemble unlocking script: convert the arguments of a contract function call to script format and concatenate them to form the unlocking script.

By using scryptlib, both scripts can be obtained with ease.

Contract Description File

The compiler output results in a JSON file. It’s a representation used to build locking and unlocking scripts. We call this file a contract description file.

There are three ways to generate this file (named as xxx_desc.json):

  1. Use the function compile programmatically:

  import { compile } from 'scryptlib';

  ...

  compile(
    {
      path: contractFilePath  //  the file path of the contract
    },
    {
      desc: true  // set this flag to be `true` to get the description file output
      asm: true // set this flag to be `true` to get the asm file output
      optimize: false //set this flag to be `true` to get optimized asm opcode
      sourceMap: true //set this flag to be `true` to get source map
      hex: true //set this flag to be `true` to get hex format script
      stdout: false//set this flag to be `true` to make that the compiler will output the compilation result through stdout
    }
  );
  1. compileAsync is the asynchronous version of the function compile

  import { compileAsync } from 'scryptlib';

  ...

  compileAsync(
    {
      path: contractFilePath  //  the file path of the contract
    },
    settings
  ) : Promise<CompileResult>;
  1. Run npx command in CLI:

  # install compiler binary
  npx scryptlib download
  # compiling contract
  npx scryptlib your_directory/your_scrypt.scrypt

Types

1. Basic Types

All basic types of the sCrypt language have their corresponding javascript classes in scryptlib. In this way, the type of parameters could be checked and potential bugs can be detected before running.

Types (scrypt)
scryptlib (javascript/typescript)

int

new Int(1) or number or bigint

bool

new Bool(true) or boolean

bytes

new Bytes('0001') or new String("hello world 😊")

PubKey

new PubKey('0001')

PrivKey

new PrivKey(1)

Sig

new Sig('0001')

Ripemd160

new Ripemd160('0001')

Sha1

new Sha1('0001')

Sha256

new Sha256('0001')

SigHashType

new SigHashType('01')

SigHashPreimage

new SigHashPreimage('010001')

OpCodeType

new OpCodeType('76')

2. Array Types

scryptlib uses javascript array to represent the array types of the sCrypt language.


[[1, 3, 1]] // represent `int[1][3]` in **sCrypt** language

[new Bytes("00"), new Bytes("00"), new Bytes("00")] // represent `bytes[3]` in **sCrypt** language

3. Structure and Type Aliases

Composite types, including structs and type aliases, are dynamically generated by buildTypeClasses. When creating a structure, all members must specify values. Use dot to access structure members.

Structure and type aliases defined in sCrypt:

struct Person {
    bytes addr;
    bool isMale;
    int age;
}

struct Block {
    bytes hash;
    bytes header;
    int time;
}

type Male = Person;
type Female = Person;

contract Main {
    Person person;
    int x;

    ...

}

Access Structure and type aliases by SDK :


const PersonContract = buildContractClass(loadDescription('person_desc.json'));

/*Person is structure and Male, Female are type aliases */
const { Person, Male, Female } = buildTypeClasses(PersonContract);

let man = new Person({
    isMale: true,
    age: 14,
    addr: new Bytes("68656c6c6f20776f726c6421")
  });

man.age = 20;

let woman = new Female({
    isMale: false,
    age: 18,
    addr: new Bytes("68656c6c6f20776f726c6421")
  });

woman.addr = new Bytes("")

4. Library

Library is another composite types. When the constructor parameter of the contract contains library, we need to create library through sdk.

Library defined in sCrypt:

library L {
  private int x;

  constructor(int a, int b) {
    this.x = a + b;
  }
  function f() : int {
    return this.x;
  }
}

contract Test {
  public int x;
  L l;

  public function unlock(int x) {
    require(this.l.f() == x + this.x);
  }
}

Access Library by SDK :


const Test = buildContractClass(loadDescription('test_desc.json'));

const { L } = buildTypeClasses(Test);

let test = new Test(1, new L(1, 2));

As you can see, creating a library instance is similar to creating a contract instance. Sometimes the constructor parameters of the library may be generic types. At this time, the sdk will deduce the generic type based on the constructor arguments you pass.

Deploy a Contract and Call Its Function

Both deploying a contract and calling a contract function are achieved by sending a transaction. Generally speaking,

  • deploying a contract needs the locking script in the output of this transaction to be set properly;

  • calling a contract function needs the unlocking script in the input of this transaction to be set properly.

There are 2 steps.

1. Get Locking and Unlocking Script

You can use the description file to build a reflected contract class in Javascript/TypeScript like this:

const MyContract = buildContractClass(JSON.parse(descFileContent));

To create an instance of the contract class, for example:

const instance = new MyContract(1234, true, ...parameters);

To get the locking script, use:

const lockingScript = instance.lockingScript;
// To convert it to ASM/hex format
const lockingScriptASM = lockingScript.toASM();
const lockingScriptHex = lockingScript.toHex();

To get the unlocking script, just call the function and turn the result to bsv.Script object, for example:

const funcCall = instance.someFunc(new Sig('0123456'), new Bytes('aa11ff'), ...parameters);
const unlockingScript = funcCall.toScript();
// To convert it to ASM/hex format
const unlockingScriptASM = unlockingScript.toASM();
const unlockingScriptHex = unlockingScript.toHex();

2. Wrap Locking and Unlocking Script into a Transaction

Local Unit Tests

A useful method verify(txContext) is provided for each contract function call. It would execute the function call with the given context locally. The txContext argument provides some context information of the current transaction, needed only if signature is checked inside the contract.

{
  tx?: any;                 // current transaction represented in bsv.Transaction object
  inputIndex?: number;      // input index, default value: 0
  inputSatoshis?: number;   // input amount in satoshis
}

It returns an object:

{
  success: boolean;       // script evaluates to true or false
  error: string;          // error message, empty if success
}

It usually appears in unit tests, like:

const context = { tx, inputIndex, inputSatoshis };

// 1) set context per verify()
const funcCall = instance.someFunc(new Sig('0123456'), new Bytes('aa11ff'), ...parameters);
const result = funcCall.verify(context);
// 2) alternatively, context can be set at instance level and all following verify() will use it
instance.txContext = context;
const result = funcCall.verify();

expect(result.success, result.error).to.be.true;
assert.isFalse(result.success, result.error);

Contracts with State

contract Counter {
    @state
    int counter;

    constructor(int counter) {
        this.counter = counter;
    }
}

Use the initial state to instantiate the contract and read the state by accessing the properties of the contract instance.

const instance = new Counter(0);

let state = instance.counter;
// update state
instance.counter++;

Then use instance.getNewStateScript() to get a locking script that includes the new state. It accepts an object as a parameter. Each key of the object is the name of a state property, and each value is the value of the state property. You should provide all state properties in the object.

const tx = newTx(inputSatoshis);
let newLockingScript = instance.getNewStateScript({
    counter: 1
});

tx.addOutput(new bsv.Transaction.Output({
  script: newLockingScript,
  satoshis: outputAmount
}))

preimage = getPreimage(tx, instance.lockingScript, inputSatoshis)

You can also access the state of the contract by accessing the properties of the instance.


instance.counter++;
instance.person.name = new Bytes('0001');

Instantiate Inline Assembly Variables

Assembly variables can be replaced with literal Script in ASM format using replace(). Each variable is prefixed by its unique scope, namely, the contract and the function it is under.

const asmVars = {
  'contract1.function1.variable1': 'ff41',
  'contract2.function2.variable2': 'OP_4'
};
instance.replaceAsmVars(asmVars);

Construct contracts from raw transactions

In addition to using a constructor to create a contract, you can also use a raw transaction to construct it.

const axios = require('axios');

const Counter = buildContractClass(loadDesc("counter_debug_desc.json"));
let response = await axios.get("https://api.whatsonchain.com/v1/bsv/test/tx/7b9bc5c67c91a3caa4b3212d3a631a4b61e5c660f0369615e6e3a969f6bef4de/hex")
// constructor from raw Transaction.
let counter = Counter.fromTransaction(response.data, 0/** output index**/);

// constructor from Utxo lockingScript
let counterClone = Counter.fromHex(counter.lockingScript.toHex());

Support browsers that are not compatible with BigInt

Some contracts use Bigint to construct or unlock. but some browsers do not support Bigint, such as IE11. In this case, we use strings to build Bigint.


// polyfill

import 'react-app-polyfill/ie11';  
import 'core-js/features/number';
import 'core-js/features/string';
import 'core-js/features/array';



let demo = new Demo("11111111111111111111111111111111111", 1);

let result = demo.add(new Int("11111111111111111111111111111111112")).verify();

console.assert(result.success, result.error)

Scrypt Boilerplate

Note: Modified to use @radiantblockchian/radjs for testnet/superAssetR100.js

sCrypt Project Boilerplate

Prerequisites

Guide

  1. Contract Development and Test

  2. Contract Integration and Application Launch

Quickstart

Directory layout

For each contract x, a source file is at contracts/x.scrypt, a test file is at tests/js/x.scrypttest.js, and a deployment file is at testnet/x.js.

How to write test for an sCrypt contract

The major steps to write a sCrypt test are exemplified by tests/demo.scrypttest.js.

  1. Use the imported function buildContractClass to get a reflected contract, which has same properties and methods as defined in the specified sCrypt contract.

Note that demo_desc.json is the description file name of the compiled contract, which will be generated automatically if you run npm run watch and its name follows the rule $contractName_desc.json.

  1. Initialize the contract.

  1. Write tests for the instantiated contract as you would do in Javascript.

How to run tests locally

Run using sCrypt IDE

Run unit tests file within the editor/explorer context menu.

Note: The test files must be suffixed by .scrypttest.js or .scrypttest.ts, otherwise the "Run sCrypt Test" option would not appear in the menu.

Run from console

Tests could also be run from the console by executing npm test, just like regular Javascript/TypeScript tests.

How to deploy a contract and interacte with it

Deploy by writing javascript/typescript code

  1. Provide a private key with funds in privateKey.js

  1. Deploy a contract and call its function by issuing

Output like the following will appear in the console. And you have successfully deployed a contract and called its function on Bitcoin. Sweet!

It is strongly recommended to test your contract on testnet first, before deploying it on mainnet. By default, deployment is on testnet. To switch to mainnet, simply modify API_PREFIX in helper.js.

IDE provides a universal UI interface. You can deploy the contract with one click by simply filling in the relevant parameters. You can call the public function of the contract with the click of a button without writing a line of code.

Electron Wallet

FIRST USE - ELECTRON WALLET

The Electron wallet is recommended for general receiving and sending uses. It uses Electrumx as to connect to the Radiant node.

What you will see in this guide:

  • Downloading Wallet

  • Seed generation for the wallet.

  • Basics for sending and receiving.

Download the wallet

For this guide I recommend the portable version because it does not require installation. This allows you to move it to another pc or make a backup in a simple way.

  • MACOS: Electron-Radiant-v0.x.x-macosx.dmg

  • WIN PORTABLE: Electron-Radiant-v0.x.x-portable.exe

  • WIN INSTALL: Electron-Radiant-v0.x.x-setup.exe

  • LINUX PORTABLE: Electron-Radiant-v0.x.x-x86_64.AppImage

The first time it is run, it will be necessary to accept the Windows warning. It is also possible that the antivirus says that it may have a Trojan, but it is a false-positive.

And click in Run anyway

Two icons appear in the installation. The standard version, which is the Mainnet and the Testnet, which is the testnet network.

The next step is to indicate how to connect the wallet. By default it is correct

Now it is time to create a new wallet and it is necessary to give it a name. To load a backup, it would be necessary to load the file of the wallet

Radiant only works with standard wallets, which is the one to dial. To retrieve a specific private key, it would be option 3

Create new seed

In the next step we have two important options to choose from. Restore a wallet with the saved words or generate a new one.

In this case we select a new one:

  1. Create new seed

  2. Restore seed.

The words that appear will be the seed of the new portfolio. It is necessary and obligatory to write them down or copy them to a file. If they are lost, you will lose access to any radiant in the addresses of the portfolio.

IMPORTANT: MAKE SEVERAL BACKUP COPIES OF THESE WORDS

To ensure that the words are copied correctly, you are asked to enter them all in the following step

And the last step is to generate a password that encrypts the wallet.

TAB Options

History

  1. Status of TX.

  2. Transaction date.

  3. Address label.

  4. Amount of RXD of the transaction.

  5. Overall balance of the transaction.

Send

  1. Destination address

  2. Name of the address.

  3. Send button.

  4. Bar to establish network commission.

  5. Amount to send

If you have entered a password in the wallet, you need to enter it to make a transaction.

Once everything is filled in correctly, the transaction is displayed.

In history appears this transaction as shown in the image and the transaction will appear in the next block that is generated (about 5 minutes).

  1. Coin input

  2. Coin output

Receive

Address not used to receive new funds

  1. his address will always be one that has never received funds.

  2. Name to be added to the address

Addresses

The first 20 addresses of the wallet and the amount of coins in each one. In change

Coins

Here you can see all received and unspent transactions.

  1. Addresses to receive

  2. Number of coins per address

If the connectivity button is red, you can fix it by clicking on it and checking the servers to which you are connected.

In server all servers are loaded and can be added if necessary.

Here are the servers you can connect to

ACTUAL SERVERS

Docker

Run a Docker Radiant Node

Installing Docker:

Pulling Docker image:

If you want to run the electrum version there is no need to also run the pure node container.

Under Docker commands you can see a command to fetch the image so that you store it locally. If you know what image you want there is no need to do this manually. When we run the image and Docker can't find it locally it will search Docker Hub for it.

Running the container:

The command for starting the node container is:

You might not need to sudo if you have given docker sudo rights. The -itd tag is to run the container interactively, with tty (terminal) and detatched (background). The last bit is the reference to the image you want to run.

The name tag is optional, but makes it easier when we specify it when we want to inspect the container and when we stop it.

If it is the first time you run the command and dont have the image locally you will see the download progress. This is only for when you fetch new images. The second time you run it it will be much faster as you already have the image. When you run the command you should see a hash, and that means the container started.

The command for starting the node + electrum container is:

Inspecting the container:

You can run this to see what containers are currently running:

and you can run this to see all containers that has been ran:

Acces the container to inspect and troubleshoot: We can access the terminal of the running container to inspect and troubleshoot it using this command:

This will open up the terminal in the container and we can run commands to see that everything runs as expected. You will see a # symbol meaning we are in the terminal of the node. Now we can run:

To go back out of the container simply use runexit inside the container.

We now have a functional radiant node up and running, but why stop there? We can easilly run multiple nodes at once. Just start another node using the same image (but different name). This will spawn a new container with a new node running. For example:

We can now use

to see that we have two nodes running.

Stopping container:

When its time to stop the container we simply run:

"radiantnode" is the name we specified when we started the first container. To stop the second container we started:

To stop the node + electrum:

Chained APIs - Chinese

使用链式 APIs

部署

下面是一个实现部署合约功能的函数。任意类型的合约都可以使用该函数来部署。

调用合约

调用合约公共函数时分两种情况: 1. 该公共函数不包含 SigHashPreimage 类型的参数 2. 改公共函数包含 SigHashPreimage 类型的参数

不包含 SigHashPreimage

如果合约的公共函数不包含 SigHashPreimage 类型的参数,则构造调用合约的交易比较简单。一般可以按照以下步骤构建交易:

  1. 通过 createInputFromPrevTx(tx, outputIndex) 从合约所在的交易创建输入,并添加到交易中。

  2. 使用 change(address) 添加一个找零输出。

  3. 使用 setInputScript(inputIndex, (tx, output) => bsv.Script) 为步骤 1 添加的输入设置解锁脚本。

  4. 调用 seal() 封印交易,同时自动计算出正确的交易费用和找零余额。

包含 SigHashPreimage

如果合约的公共函数包含 SigHashPreimage 类型的参数,则构造调用合约的交易比较复杂。因为解锁脚本的大小影响到交易费用的计算,从而影响输出中的 satoshis 余额。输出中的 satoshis 余额又会影响 SigHashPreimage 的计算。使用 scryptlib 的链式 APIs 隐藏了处理这些繁琐计算的细节。 你只需按照以下方式来构造交易:

交易费用是由合约里的余额支付:

这种情况下不包含独立的找零输出。所有输出的余额在构造交易之前是未知的。交易费用会影响所有输出的余额的计算。

  1. 通过 createInputFromPrevTx(tx, outputIndex) 从合约所在的交易创建输入,并添加到交易中。

  2. 使用 setOutput(outputIndex, (tx) => bsv.Transaction.Output) 添加一个或多个输出。输出的余额通常需要减去交易费用;

    javascript const newAmount = amount - tx.getEstimateFee();

  3. 使用 setInputScript(inputIndex, (tx, output) => bsv.Script) 为步骤 1 添加的输入设置解锁脚本。解锁参数通常包含一个指定合约新余额的参数,其计算方式与上一步相同。

  4. 调用 seal() 封印交易,同时自动计算出正确的交易费用和找零余额。

交易费用是通过添加其它输入来支付:

这种情况下包含一个独立的找零输出。其它输出的余额一般是可以在构造交易之前计算出来的。交易费用只会影响找零输出的计算。

  1. 通过 createInputFromPrevTx(tx, outputIndex) 从合约所在的交易创建输入,并添加到交易中。

  2. 通过 from([utxo]) 来添加用于支付交易费用的输入。

  3. 使用 addOutput() 添加一个或多个的输出(根据合约业务逻辑添加)。

  4. 使用 change(address) 添加一个找零输出。

  5. 使用 setInputScript(inputIndex, (tx, output) => bsv.Script) 为步骤 1 添加的输入设置解锁脚本。解锁参数通常包含一个指定找零余额的参数,该参数可以通过 tx.getChangeAmount() 来获取。

  6. 使用 sign(privateKey) 来签名所有添加用于支付交易费用的输入。

  7. 调用 seal() 封印交易,同时自动计算出正确的交易费用和找零余额。

扩展的 APIs 列表

Restore

RESTORE WALLET - ELECTRON WALLET

The Electron wallet uses BIP39 to store the wallet keys. Samara and Chainbow also use BIP39 to host these keys, making all these wallets compatible with each other.

It is very important to have these words stored securely, as without them it is not possible to retrieve the stored funds. More information about BIP39

Create new wallet

Select a name for this restore wallet.

Select standard wallet

Specify that we already have a seed.

Write the saved seed in the box with the same saved word order.

Specify the derivation of the addresses. If it has not been changed, leave the default with: m/44'/0'/0'

Specifying a good password to the restored wallet.

Finally, the restored wallet data should be displayed on the screen.

Releases

SDK

rawstate

Manually Maintain Mutable State

One way to implement state in contract is dividing the contract in the locking script into two parts: data and code. Data part is the state. Code part contains the business logic of a contract that encodes rules for state transition.

OP_RETURN data of the contract locking script can be accessed by using an accessor named dataPart, for example:

After that, the counter.lockingScript would include the data part automatically. You can use it to calculate preimage.

If you want to access the code part of the contract's locking script including the trailing OP_RETURN, use:

Chained APIs

Using Chained APIs

Deploy Contract

The following is a function that deploys any type of contracts.

Call contract

There are two cases when calling a public function of a contract: 1. The function takes a parameter of type SigHashPreimage 2. No SigHashPreimage parameter.

No SigHashPreimage

If the public function of the contract being called does not contain parameter of type SigHashPreimage, it is relatively simple to build a transaction calling it. Generally, you can build a transaction according to the following steps:

  1. Create an input from a previous transaction where the contract is located by createInputFromPrevTx(tx, outputIndex) and add it to the transaction.

  2. Use change(address) to add a change output.

  3. Use setInputScript(inputIndex, (tx, output) => bsv.Script) to set the unlocking script for the input added in step 1.

  4. Call seal() to finalize the transaction, and automatically calculate the correct transaction fee and change balance.

SigHashPreimage

If the public function of the contract contains parameters of type SigHashPreimage, it is more complicated to build the transaction calling it. This is because unlocking script containing SigHashPreimage affects the calculation of transaction fee and thus the satoshis amount in the output. In turn, satoshis affects the calculation of SigHashPreimage. The chained APIs provided by scryptlib hides the details of processing these inter-dependent calculations. You only need to build the transaction in the following way:

1. Transaction fees paid by the bitcoins locked in the contract:

In this case, no separate change output is included. The balances of all outputs are unknown before the transaction is built. Transaction fees will affect the calculation of all output balances.

  1. Create an input from the transaction where the contract is located by createInputFromPrevTx(tx, outputIndex) and add it to the transaction.

  2. Use setOutput(outputIndex, (tx) => bsv.Transaction.Output) to add one or more outputs. The output balance usually needs to subtract transaction fee;

    javascript const newAmount = amount - tx.getEstimateFee();

  3. Use setInputScript(inputIndex, (tx, output) => bsv.Script) to set the unlocking script for the input added in step 1. The unlocking parameter usually contains a parameter that specifies the new balance of the contract and its calculation is the same as the previous step.

  4. Call seal() to finalize the transaction and automatically calculate the correct transaction fee and change amount.

2. Transaction fees paid by adding other inputs:

In this case, a separate change output is included. The balance of other outputs can generally be calculated before the transaction is built. Transaction fees only affect the calculation of change output.

  1. Create an input from the transaction where the contract is located by createInputFromPrevTx(tx, outputIndex) and add it to the transaction

  2. Use from([utxo]) to add inputs used to pay transaction fee.

  3. Use addOutput() to add one or more outputs (add according to the contract business logic).

  4. Use change(address) to add a change output.

  5. Use setInputScript(inputIndex, (tx, output) => bsv.Script) to set the unlocking script for the input added in step 1. The unlocking parameters usually contains a parameter that specifies the change amount, which can be obtained through tx.getChangeAmount().

  6. Use sign(privateKey) to sign all inputs added to pay transaction fee.

  7. Call seal() to finalize the transaction.

Extended APIs list

An Artifact object is the result of compiling a CashScript contract. See the for more information on Artifacts. Compilation can be done using the standalone or programmatically with the cashc NPM package (see ).

A NetworkProvider is used to manage network operations for the CashScript contract. By default, a mainnet ElectrumNetworkProvider is used, but alternative network providers can be used. See the section on below.

These contract functions return an incomplete Transaction object, which needs to be completed by providing outputs of the transaction. More information about sending transactions is found on the page.

So in the place of a signature, a SignatureTemplate can be passed, which will automatically generate the correct signature using the signer parameter. This signer can be any representation of a private key, including , , , or raw private key buffers. This ensures that any BCH library can be used.

The ElectrumNetworkProvider uses to connect to the BCH network. This is the recommended provider for most use cases and is used as the default when no other provider is provided. Both network and electrum parameters are optional, and they default to mainnet and a 2-of-3 ElectrumCluster with a number of reliable electrum servers.

The FullStackNetworkProvider uses ' infrastructure to connect to the BCH network. FullStack.cash' offers dedicated infrastructure and support plans for larger projects. Both network and bchjs parameters are mandatory, where bchjs is an instance of FullStack.cash' .

The BitboxNetworkProvider uses Bitcoin.com's to connect to the BCH network. Because BITBOX is no longer officially maintained it is not recommended to use this network provider, and it is only available for compatibility with older projects. Both network and bitbox parameters are mandatory, where bitbox is a BITBOX instance.

Most importantly, it is now possible to access specific data for all individual inputs and outputs, rather than e.g. working with hashes of the outputs (tx.hashOutputs). This offers more flexibility around the data you want to enforce. For more information about this new native introspection functionality, refer to the section of the documentation, the and the .

See the for an overview of other new changes.

See the for an overview of other new changes.

The size for executing this contract is 288 bytes, which contains a preimage with 201 size, and the size for executing the contract using the Tx.checkPreimage() version is 1005 bytes, which contains a preimage with 915 size. The functions of these two contracts are exactly the same, but the transaction size is optimized by 80%.

See the for details on migrating from the old introspection to the new native introspection methods.

:boom: BREAKING: Covenants using tx.bytecode now include a placeholder OP_NOP that gets replaced when constructor arguments are provided in the CashScript SDK. If you're not using the CashScript SDK, refer to the to see the steps required to do so manually.

Anyone can implement the NetworkProvider interface to create a custom provider. The CashScript SDK offers three providers out of the box: one based on electrum-cash (default), one based on FullStack.cash' infrastructure, and one based on BITBOX. See the for details.

See the for details on migrating from the old contract instantiation flow.

This update contains several breaking changes. See the for a full migration guide.

PLEASE NOTE: This is a fork of scryptlib as a convenience that contains the patches to the included bsv.js lib directly Alternatively, the regular scryptlib may be used along with radjs radiantjs https://github.com/RadiantBlockchain/radjs (latest) or and not using the bundled bsv in scryptlib.

Use to compile manually;

make building transactions super easy.

sCrypt offers . Declare any property that is part of the state with a decorator @state in a contract, for example:

You can also maintain state manually to, for example, optimize your contract or use customized state de/serialization .

You could find more examples using scryptlib in the repository.

Make sure you have the installed. sCrypt IDE is a tool for developers to write, test, deploy, call, and debug sCrypt smart contracts.

is a high-level programming language for writing smart contracts on Bitcoin SV. This project provides examples to help developers learn and integrate sCrypt smart contracts to their Javascript-based projects. Our recommended procedure of developing smart contract based applications is as follows:

After developing and unit testing the smart contracts, the next step is to integrate them into your application which is written in other languages such as Javascript or Python. Integration tests should be run on Bitcoin SV or before launching the application to the public on mainnet.

Install and import / require , which is a javascript SDK for integrating sCrypt smart contract.

Before deploying a contract, make sure the latest contract has been compiled to a , which is what will get deployed. This could be done automatically by running a daemon process with command npm run watch. It will monitor a contract file's change and recompile it when necessary. All generated description files are located at testnet/fixture/autoGen. Make sure it's up to date with the contract before deployment.

Deploy by using sCrypt IDE

URL:

TAB
Description

The process for installing docker engine depends on your operating system. Refer to and look up instructions for your OS.

You can browse for images that you might find use for. At the time of writing there are two options for running a Radiant node: just the node and the node with electrum.

The image for just the node: .

The image for the node + electrum: .

to see that our node is running. You can see other useful commands to run on the node in the .

在部署合约和调用合约的过程中,通常需要计算交易费用和找零输出。在不知道解锁脚本大小的情况,很难准确地计算交易费用以及找零输出。scryptlib 扩展了 库, 提供一套链式 APIs 来构造交易。使得即使在这种情况下,计算交易费用和找零都变得简单。

Api
描述
参数

BIP39:

sCrypt offers . Let us look at a simple example of a stateful contract: a counter contract tracking how many times its function increment() has been called. Its code is shown below with comments inline.

Chained APIs

When deploying and calling a contract, it is often necessary to calculate the transaction fee and the change output. Without knowing the size of the unlocking script, it is difficult to calculate both. scryptlib extends the library and provides a set of chained APIs to simplify building transactions in these cases.

Api
description
parameters
Sending Transactions
BITBOX/BCHJS' ECPair
bitcore-lib-cash' PrivateKey
WIF strings
electrum-cash
FullStack.cash
BCHJS
BITBOX
transaction
transaction
CheckLockTimeVerify
https://twitter.com/RoscoKalis/status/1529072055756414976
replaceBytecodeNop() function
https://twitter.com/RoscoKalis/status/1371896417443282956
https://twitter.com/RoscoKalis/status/1301521593399685121
migration notes
https://twitter.com/RoscoKalis/status/1298645699559596033
https://twitter.com/RoscoKalis/status/1267440143624884227
https://twitter.com/RoscoKalis/status/1264921879346917376
https://twitter.com/RoscoKalis/status/1224389493769342979
https://twitter.com/RoscoKalis/status/1223280232343515136
https://twitter.com/RoscoKalis/status/1217101473743544320
https://twitter.com/RoscoKalis/status/1204765863062188033
https://twitter.com/RoscoKalis/status/1202220857566908416
https://twitter.com/RoscoKalis/status/1192900277105389568
https://twitter.com/RoscoKalis/status/1186554051720167424
https://twitter.com/RoscoKalis/status/1178843657069154305
https://twitter.com/RoscoKalis/status/1174910060691984385
https://github.com/chainbow/radiantjs
https://github.com/RadiantBlockchain-Community/radiantjs
https://github.com/sCrypt-Inc/scryptlib
sCrypt VS Code extension
Chained APIs
stateful contracts
rawstate
boilerplate
Language Documentation
cashc CLI
CashScript Compiler
NetworkProvider
NetworkProvider docs
Covenants guide
Native Introspection CHIP
Global covenant variables
release notes
release notes
migration notes
migration notes
npm install
npm test
.
├── contracts                       # sCrypt contract files
│   ├── accumulatorMultiSig.scrypt      # 
│   ├── ackermann.scrypt                # 
│   ├── acs.scrypt                      # A contract which can be spent by anyone but only to a specific address
│   ├── advancedCounter.scrypt          # Use external UTXOs to pay  contract tx fees using sighash ANYONECANPAY
│   ├── advancedTokenSale.scrypt        # Sambe as above, but for token sale contract
│   ├── asm.scrypt                      # Embed Script directly into sCrypt 
│   ├── auction.scrypt                  # 
│   ├── binaryOption.scrypt             # 
│   ├── cltv.scrypt                     # 
│   ├── cointoss.scrypt                 # 
│   ├── cointossxor.scrypt              # 
│   ├── conwaygol.scrypt                # 
│   ├── counter.scrypt                  # Count the number of times a function has been called to showcase 
│   ├── ec.scrypt                       # 
│   ├── ecdsa.scrypt                    # 
│   ├── demo.scrypt                     # "hello world" contract
│   ├── faucet.scrypt                   # 
│   ├── forward.scrypt                  # 
│   ├── hashpuzzlep2pkh.scrypt          # combining  and p2pkh contracts
│   ├── kaggle.scrypt                   # 
│   ├── lottery.scrypt                  # 
│   ├── mast.scrypt                     # 
│   ├── merkleToken.scrypt              # Token based on Merkle Tree
│   ├── merkleTree.scrypt               #  validation and updating
│   ├── nonFungibleToken.scrypt         # 
│   ├── oracle.scrypt                    # 
│   ├── p2pkh.scrypt                    #  contract written in sCrypt
│   ├── p2sh.scrypt                     # 
│   ├── perceptron.scrypt               # 
│   ├── perceptron2.scrypt              # 
│   ├── rabin.scrypt                    #  to import off-chain data into a contract via oracle
│   ├── recurring.scrypt                # 
│   ├── rpuzzle.scrypt                  # 
│   ├── schnorr.scrypt                  # 
│   ├── rps.scrypt                      # Rock Paper Scissors
│   ├── simpleBVM.scrypt                # 
│   ├── spvToken.scrypt                 # 
│   ├── statecounter.scrypt             # 
│   ├── stateStruct.scrypt              # Recommended way to implement a stateful contract using struct
│   ├── sudoku.scrypt                   # 
│   ├── svd.scrypt                      # 
│   ├── tictactoe.scrypt                #  onchain p2p gaming
│   ├── timedcommit.scrypt              # : Trustless contracting by combining on-chain and off-chain transactions
│   ├── token.scrypt                    #  by storing token map as contract state in a single UTXO
│   ├── tokenSale.scrypt                # Selling tokens for bitcoins using 
│   ├── tokenSwap.scrypt                # Merkle tree-based token and bitcoin swap 
│   ├── tokenUtxo.scrypt                # 
│   ├── treeSig.scrypt                  # 
│   └── util.scrypt                     # utility functions and constants
├── testnet                         # examples to deploy contract and call its function on testnet
    └── fixture
        └── autoGen                     # contract description json files
└── tests                           # contract test files
    ├── js                              # Javascript unit tests
    └── ts                              # Typescript unit tests
npm install scryptlib
import { buildContractClass } from 'scryptlib';
// build a contract class
// either by compiling the contract from scratch
const Demo = buildContractClass(compileContract('demo.scrypt'))
// or from contract desc file if it's already generated from compilation
const Demo = buildContractClass(loadDesc('demo_desc.json'))
demo = new Demo(4, 7);
const result = demo.add(7 + 4).verify()
expect(result.success, result.error).to.be.true
const key = '$YOUR_PRIVATE_KEY_HERE'
node testnet/demo.js
locking txid:      8d58ff9067f5fa893b5c695179559e108ebf850d0ce4fd1e42bc872417ffd424
unlocking txid:    c60b57e93551a6c52282801130649c6a97edcca5d2b28b8b4ae2afe0ee59bf79
Succeeded on testnet
const API_PREFIX = 'https://api.whatsonchain.com/v1/bsv/main'
// const API_PREFIX = 'https://api.whatsonchain.com/v1/bsv/test' for Testnet
// const API_PREFIX = 'https://api.whatsonchain.com/v1/bsv/stn' for Scaling Test Net

1. History

All transactions from all addresses

2. Send

To send coins to an address

3. Receive

Address to receive coins and it will always be an unused one.

4. Addresses

The first 20 seed directions. All are valid for receiving and sending

5. Coins

The UTXO of each reception

6. Contacts

Addresses targeted for reuse

electrumx.radiant.ovh 50012
electrumx.radiantexplorer.com 50012
electrumx2.radiantexplorer.com 50012
electrumx.radiantblockchain.org 50012
electrumx.radiant4people.com 50012
sudo docker run --name radiantnode -itd radiantcommunity/radiant-node
sudo docker run --name radiantelectrum -itd radiantcommunity/electrumx_radiant_node
sudo docker ps
sudo docker ps -a
sudo docker exec -it radiantnode sh
radiant-cli -getinfo 
sudo docker run --name radiantnode2 -itd radiantcommunity/radiant-node
sudo docker ps
sudo docker stop radiantnode
sudo docker stop radiantnode2
sudo docker stop radiantelectrum

async function deployContract(contract, amount) {

  const address = privateKey.toAddress()
  const tx = new bsv.Transaction()
  tx.from(await fetchUtxos(address))  // 添加用来支付矿工费用和锁进合约的比特币的UTXO。钱包通常会在这里做 UTXO 的筛选,以防止添加过多 UTXO
  .addOutput(new bsv.Transaction.Output({  
    script: contract.lockingScript, // 将合约部署到第0个输出的锁定脚本
    satoshis: amount,
  }))
  .change(address)   // 添加找零输出
  .sign(privateKey) // 私钥签名, 只对P2PKH输入有效

  await sendTx(tx) // 广播交易
  return tx
}

const demo = new Demo(4, 7);

const deployTx = await deployContract(demo, 1000); // 部署合约

const unlockingTx = new bsv.Transaction();

unlockingTx.addInput(createInputFromPrevTx(deployTx))
.change(privateKey.toAddress())
.setInputScript(0, (_) => {  //设置第0个输入的解锁脚本
    return demo.add(11).toScript();
})
.feePerKb(250) // 设置交易费率,可选,默认是每KB 500 satoshis
.seal()  // 封印交易, 同时自动计算出正确的交易费用和找零余额

// 广播交易
await sendTx(unlockingTx)

const counter = new StateCounter(0)

let amount = 8000

const lockingTx =  await deployContract(counter, amount) // 部署合约

const unlockingTx = new bsv.Transaction();

unlockingTx.addInput(createInputFromPrevTx(prevTx))
.setOutput(0, (tx) => {
    const newLockingScript = counter.getNewStateScript({
        counter: i + 1
    })
    const newAmount = amount - tx.getEstimateFee(); // 计算合约的新余额,需要减去交易的费用
    return new bsv.Transaction.Output({
        script: newLockingScript,
        satoshis: newAmount,
        })
})
.setInputScript(0, (tx, output) => {  // output 包含输入/UTXO对应的锁定脚本和 satoshis 余额
    const preimage = getPreimage(tx, output.script, output.satoshis)
    const newAmount = amount - tx.getEstimateFee(); //计算合约的新余额,需要减去交易的费用
    return counter.unlock(new SigHashPreimage(toHex(preimage)), newAmount).toScript()
})
.seal() // 封印交易

// 广播交易
await sendTx(unlockingTx)

const unlockingTx = new bsv.Transaction();

const newAmount = amount + spendAmount; // 合约新余额与交易费用无关

unlockingTx.addInput(createInputFromPrevTx(prevTx))
.from(await fetchUtxos(privateKey.toAddress())) // 添加用于支付交易费用的输入
.addOutput(new bsv.Transaction.Output({ // 添加一个或多个的输出, newAmount 是合约新的余额
    script: newLockingScript,
    satoshis: newAmount,
}))
.change(privateKey.toAddress())  //添加找零输出
.setInputScript(0, (tx, output) => {
    let preimage = getPreimage(
    tx,
    output.script,
    output.satoshis,
    0,
    sighashType
    );

    return advTokenSale.buy(
    new SigHashPreimage(toHex(preimage)), 
    new Ripemd160(toHex(pkh)), // 找零地址
    tx.getChangeAmount(), // 计算找零余额
    new Bytes(toHex(publicKeys[i])), 
    numBought 
    ).toScript();

})
.sign(privateKey) // 签名所有添加用于支付交易费用的输入
.seal() //封印交易, 同时自动计算出正确的交易费用和找零余额

// 广播交易
lockingTxid = await sendTx(unlockingTx)

setInputScript

设置输入的解锁脚本

1. inputIndex 输入索引 2. (tx, output) => bsv.Script 返回解锁脚本的回调函数

setOutput

将输出添加到指定索引

1. outputIndex 输出索引 2. (tx) => bsv.Transaction.Output 返回一个输出的回调函数

setLockTime

设置交易的 nLockTime

1. nLockTime

setInputSequence

设置输入的 sequenceNumber

1. inputIndex 输入索引 2. sequenceNumber

seal

封印交易,封印后的交易不能再修改

-

getChangeAmount

获取找零输出的余额

-

getEstimateFee

根据交易大小和设置的费率评估出交易费用

-

checkFeeRate

检查交易的费用是否满足设置的费率

1. feePerKb 费率, satoshis 每 KB

prevouts

返回所有输入点的序列化串

-

contract Counter {
    public function increment(SigHashPreimage txPreimage, int amount) {
        require(Tx.checkPreimage(txPreimage));

        // deserialize state (i.e., counter value)
        bytes scriptCode = Util.scriptCode(txPreimage);
        int scriptLen = len(scriptCode);
        // counter is at the end
        int counter = unpack(scriptCode[scriptLen - Util.DataLen :]);

        // increment counter
        counter++;

        // serialize state
        bytes outputScript = scriptCode[: scriptLen - Util.DataLen] + num2bin(counter, Util.DataLen);

        bytes output = Util.buildOutput(outputScript, amount);
        // ensure output is expected: amount is same with specified
        // also output script is the same with scriptCode except counter incremented
        require(hash256(output) == Util.hashOutputs(txPreimage));
    }
}
const Counter = buildContractClass(loadDescription('counter_desc.json'));
counter = new Counter();
const dataPart = counter.dataPart;
const dataPartASM = counter.dataPart.toASM();
const dataPartHex = counter.dataPart.toHex();
// to set it using ASM
counter.setDataPart('01');
const preimage = getPreimage(tx, counter.lockingScript, inputSatoshis)
const codePart = instance.codePart;
const codePartASM = instance.codePart.toASM();
const codePartHex = instance.codePart.toHex();

async function deployContract(contract, amount) {

  const address = privateKey.toAddress()
  const tx = new bsv.Transaction()
  tx.from(await fetchUtxos(address))  // Add UTXOs/bitcoins that are locked into the contract and pay for miner fees. In practice, wallets only add enough UTXOs, not all UTXOs as done here for ease of exposition.
  .addOutput(new bsv.Transaction.Output({  
    script: contract.lockingScript, // Deploy the contract to the 0-th output
    satoshis: amount,
  }))
  .change(address)   // Add change output
  .sign(privateKey) // Sign inputs. Only apply to P2PKH inputs.

  await sendTx(tx) // Broadcast transaction
  return tx
}

const demo = new Demo(4, 7);

const deployTx = await deployContract(demo, 1000); // Deploy a contract

const unlockingTx = new bsv.Transaction();

unlockingTx.addInput(createInputFromPrevTx(deployTx))
.change(privateKey.toAddress())
.setInputScript(0, (_) => {  // Set the unlocking script for 0-th input
    return demo.add(11).toScript();
})
.feePerKb(250) // Set transaction fee rate. Optional, default is 500 satoshis per KB
.seal()  // Finalize the transaction and automatically calculate the correct transaction fee and change amount

// Broadcast transaction
await sendTx(unlockingTx)

const counter = new StateCounter(0)

let amount = 8000

const lockingTx =  await deployContract(counter, amount) // Deploy a contract

const unlockingTx = new bsv.Transaction();

unlockingTx.addInput(createInputFromPrevTx(prevTx))
.setOutput(0, (tx) => {
    const newLockingScript = counter.getNewStateScript({
        counter: i + 1
    })
    const newAmount = amount - tx.getEstimateFee(); // To calculate the new balance of the contract, you need to subtract the transaction fee
    return new bsv.Transaction.Output({
        script: newLockingScript,
        satoshis: newAmount,
        })
})
.setInputScript(0, (tx, output) => {  // output contains the locking script and satoshis balance of the UTXO
    const preimage = getPreimage(tx, output.script, output.satoshis)
    const newAmount = amount - tx.getEstimateFee(); // To calculate the new balance of the contract, you need to subtract the transaction fee
    return counter.unlock(new SigHashPreimage(toHex(preimage)), newAmount).toScript()
})
.seal() // Finalize the transaction

// Broadcast transaction
await sendTx(unlockingTx)

...

const unlockingTx = new bsv.Transaction();

const newAmount = amount + spendAmount; // The new balance of contract has nothing to do with transaction fee

unlockingTx.addInput(createInputFromPrevTx(lockingTx))
.from(await fetchUtxos(privateKey.toAddress())) // Add inputs to pay transaction fees
.addOutput(new bsv.Transaction.Output({ // Add one or more outputs, newAmount is the new balance of the contract
    script: newLockingScript,
    satoshis: newAmount,
}))
.change(privateKey.toAddress())  // Add change output
.setInputScript(0, (tx, output) => {
    let preimage = getPreimage(
    tx,
    output.script,
    output.satoshis,
    0,
    sighashType
    );

    return advTokenSale.buy(
    new SigHashPreimage(toHex(preimage)), 
    new Ripemd160(toHex(pkh)), // Change address
    tx.getChangeAmount(), // Calculate the change balance
    new Bytes(toHex(publicKeys[i])), 
    numBought 
    ).toScript();

})
.sign(privateKey) // Sign all inputs added to pay transaction fee
.seal() // Finalize the transaction

// Broadcast transaction
lockingTxid = await sendTx(unlockingTx)

setInputScript

Set input unlocking script

1. inputIndex Input index 2. (tx, output) => bsv.Script A callback function returns the unlocking script of the input

setOutput

Add output to the specified index

1. outputIndex Output index 2. (tx) => bsv.Transaction.Output A callback function returns the output

setLockTime

Set the transaction's nLockTime

1. nLockTime

setInputSequence

Set the sequenceNumber

1. inputIndex Input index 2. sequenceNumber

seal

Seal transactions. Transactions after sealing can no longer be modified

-

getChangeAmount

Get the balance of the change output

-

getEstimateFee

Estimate transaction fee based on transaction size and fee rate

-

checkFeeRate

Check whether the transaction fee meets the fee rate

1. feePerKb fee rate, satoshis per KB

prevouts

returns the serialization of all input outpoints

-

https://github.com/RadiantBlockchain-Community/scrypt-boilerplate
sCrypt IDE
sCrypt
Testnet
Scaling Test Network(STN)
scryptlib libary
description json file
deploy feature
https://github.com/RadiantBlockchain/electron-radiant/releases
https://docs.docker.com/engine/install/
https://hub.docker.com
https://hub.docker.com/r/radiantcommunity/radiant-node
https://hub.docker.com/r/radiantcommunity/electrumx_radiant_node
bsv
https://en.bitcoin.it/wiki/Seed_phrase
Migration Notes
Release Notes
Examples
Contract Instantiation
Sending Transactions
stateful contracts
简体中文版
bsv
Radiant Node guide

preimage - Chinese

脚本操作码 OP_CODESEPARATOR 下的 preimage 原象

OP_CHECKSIG 检查的签名是对 preimage 原象的哈希的签名。原象包含整个交易的输出、输入和锁定脚本。通常锁定脚本是完整的。但是如果 OP_CHECKSIG 之前执行了 OP_CODESEPARATOR,那么原象只包含锁定脚本从最近执行的 OP_CODESEPARATOR 的位置直到脚本结束,即只包含部分锁定脚本。利用这个特性,可以用来减少原象的大小,从而减少整个交易的大小。

sCrypt 标准库 Tx 提供了以下函数来检查这种只包含部分锁定脚本的原象:

函数名
描述

Tx.checkPreimageOCS

检查的原象只包含部分锁定脚本的,默认检查的签名类型是 SigHash.ALL | SigHash.FORKID ,不支持选择签名类型

Tx.checkPreimageSigHashTypeOCS

支持选择签名类型

Tx.checkPreimageAdvancedOCS

持选择签名类型以及自定义临时密钥

Tx.checkPreimageOptOCS

优化版的 Tx.checkPreimageOCS

Tx.checkPreimageOptOCS_

优化版的 Tx.checkPreimageOCS,支持选择签名类型

下面是一个使用 Tx.checkPreimageOCS() 来检查原象的例子:

contract CheckLockTimeVerifyOCS {
    int time;

    public function unlock(SigHashPreimage preimage) {
        require(Tx.checkPreimageOCS(preimage));
        require(SigHash.nLocktime(preimage) > this.time);
    }
}

下面是如何部署和调用使用 CheckLockTimeVerifyOCS 合约的例子:

const amount = 2000

// get locking script
const CLTVOCS = buildContractClass(loadDesc('cltvOCS_debug_desc.json'));
cltv = new CLTVOCS(1422674);

// lock fund to the script
const lockingTx = await deployContract(cltv, amount)
console.log('funding txid:      ', lockingTx.id);

// unlock
const unlockingTx = new bsv.Transaction();

unlockingTx.addInput(createInputFromPrevTx(lockingTx))
    .setLockTime(1422674 + 1)
    .change(privateKey.toAddress())
    .setInputScript(0, (tx, output) => {
        const preimage = getPreimage(tx, output.script.subScript(0), output.satoshis)
        return cltv.spend(new SigHashPreimage(toHex(preimage))).toScript()
    })
    .seal()


const unlockingTxid = await sendTx(unlockingTx)
console.log('unlocking txid:   ', unlockingTxid)

console.log('Succeeded on testnet')

在使用 getPreimage 计算原象时,不能传递整个锁定脚本了。需要使用 subScript(opsIndex: number) 来裁剪锁定脚本。其参数 index 是本脚本中 OP_CODESEPARATOR 的索引,即第几个 OP_CODESEPARATOR。需要注意的是,该函数没有考虑动态执行的情况。

Radiant Node

COMPILE RADIANT NODE WITH UBUNTU 22.04

Small guide to compile the Radiant node and use its commands

Github


Libraries to compile

Necessary libraries and applications to compile the node:

sudo apt-get install build-essential cmake git libboost-chrono-dev libboost-filesystem-dev libboost-test-dev libboost-thread-dev libevent-dev libminiupnpc-dev libssl-dev libzmq3-dev help2man ninja-build python3 libdb-dev libdb++-dev

Download node source

Download radiant node source code to compile:

git clone https://github.com/radiantblockchain/radiant-node.git

Once you have downloaded the source code, enter the generated folder, create the build folder and enter inside it

cd radiant-node/
mkdir build
cd build

Compile options

Two options to compile (QT does not work properly):

  • No QT (recommend)

cmake -GNinja .. -DBUILD_RADIANT_QT=OFF
  • No wallet, no QT

cmake -GNinja .. -DBUILD_RADIANT_WALLET=OFF -DBUILD_RADIANT_QT=OFF

Once everything is configured, proceed to compile with ninja and wait a few minutes.

ninja

The result will be generated in the src folder src/radiantd src/radiantd src/radiant-cli src/radiant-wallet

To run the node from any location, we can copy the file to a system directory

sudo cp src/radiant* /usr/local/bin/

Create config file

mkdir ~/.radiant
touch ~/.radiant/radiant.conf

Copy main data to the configuration file

cat > ~/.radiant/radiant.conf << EOL
rpcuser=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 12 | head -n 1)
rpcpassword=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)

listen=1
server=1
daemon=1

maxconnections=16

EOL

Useful commands

  • START NODE: radiantd

  • STOP NODE: radiant-cli stop

  • NODE INFO: radiant-cli -getinfo

  • NODE SYNCHRONISM: radiant-cli getblockchaininfo

  • CONNECTIVITY: radiant-cli getnetworkinfo

  • WALLET STATUS: radiant-cli getwalletinfo

  • CREATE NEW ADDRESS: radiant-cli getnewaddress "Principal"

  • GET PRIVATE ADDRESS KEY:radiant-cli dumpprivkey Direccion_Generada

  • IMPORT PRIVATE KEY: radiant-cli importprivkey L1NLnbPQWaE3sDMedHgHy9q3wMjzZ2HCRAtgfQqfwXppAvnPBeit Test

  • WALLET ADDRESSES: radiant-cli listreceivedbyaddress 1 true

Start node

Set up node for testnet

To start your node in testnet run the following command

radiantd -testnet -daemon

You probably need to establish a connection to a node already running on testnet. You can check if you have any connections and if the node is synching by checking

radiant-cli -testnet -getinfo

If you dont have any connections and the block count is 0 you will need to manually connect to a testnet node. To find nodes already running head on over to https://explorer-testnet.radiantblockchain.org/peers. There you will find a list over IPs that you can connect to. Choose one and replace the x'ed out IP in the command:

radiant-cli -testnet addnode xxx.xxx.xxx.xxx add

Now you should be able to see the connections and the node will sync.

执行这个合约的 大小是 288 字节,包含一个 288 字节的 preimage 原象。 而执行使用 Tx.checkPreimage() 版本的 合约的 大小是 1005 字节,包含一个 915 字节的 preimage 原象。 这两个合约的功能是完全一样的,但是交易的大小却优化了 80% 。

Radiant node source code:

Original guide to compile in Ubuntu:

交易
CheckLockTimeVerify
交易
https://github.com/RadiantBlockchain/radiant-node
https://github.com/RadiantBlockchain/radiant-node/blob/master/doc/build-unix-deb.md

Create multisign

Create multisign with 3 wallets and 2 need signers (3 of 2)

One of the new features of ZEUS and Electron Wallet 0.1.3 is the ability to use multi-signature wallets. Here is a basic process of its use.

What is a MultiSig wallet?

Starting

Required

  • Latest version of Electron Wallet. In this example I'm using version 0.1.3

  • SO compatible with wallet: Wndows, Linux and Mac.

  • Other wallets or persons to sign

Create wallets

The first step is to create a new wallet. In this guide, a test will be done with 3 wallets where it will only be necessary to sign with two of them.

Select Multi-signature wallet

In this section is where you select the type of multi-signature wallet. It is possible to make multiple possible variants, but here we will do the usual 2 out of 3.

  • Total cosigners: 3

  • Requiere signatures: 2

For the example I will create 3 wallets with 3 new seeds

Very important to make a secure copy of the words or the funds in the generated wallet could be lost.

Master public key is what you need to add to the other wallets to form the multisign wallet.

When adding cosigners it is necessary that the other two wallets tell you their master public key to continue creating the multisign wallet.

IMPORTANT: Never pass the seed to anyone, only the master public key.

Create a good password to encrypt your wallet

Example used in guide

These are the data for the wallet that I have created for the guide and can be used to review it

  • SEED 1: tumble cage van bind device party shoe rail valid canal witness dial

masterpub-key: xpub6CCA12nJkoBPVtZUswdEnVT9LR87zxutoKpSpA4crx9khyuupX4qsuWyswbZub63Dqhz7hRYfRZt4oPd6D9yherrjUYcmQmdguas99FcW5x

  • SEED 2: avocado tuition asthma wine solar garbage limb play example hybrid unaware loud

masterpub-key: xpub6D2CVbotwBsk7czNJQjKKWWa993PjTBJHG9fHV54yXoiPVPCZZwp6JNYUFD2PJQWfUAgjqueVwwx8nqfspGHYfmRj6FN1LCNz3wmK3hGiNR

  • SEED 3: drift kitten flash alter update program island slide leisure youth spider diet

masterpub-key: xpub6DJgWZhSCwU8VWQxnzpUky18gH9ZtvesYac2rVsbuyyeDyVsBxdgXF3GUDiic73eFsf5ioZTgwKRwyv4HLACjmnKQS3Pn4j9zhWFce3hmiG

The three wallets would look as shown in the image. The addresses of all the multi-signature wallets start with 3, this is correct.

Send RXD to test wallet

When receiving funds in the first direction, all wallets display the message to receive funds at the same time.

And here you would see it in the wallets

Test multisign wallet

Now it's time for the sending process with the signature of two wallets. A destination address is selected and the amount to be sent. This part does not change.

The change is made when you click on send and a window appears with the transaction data. You can see that there is a signature of two and that it has been transferred to the network.

For the signature in another of the two wallets, it is necessary to copy the transaction as shown in the picture below

And in the wallet of one of the other two wallets go to: TOOLS > LOAD TRANSACTION > FROM TEXT

Here is pasted the transaction copied from the wallet that generated the sending

Once accepted, a window with the transaction will pop up. Check that everything is OK and then click on Sign and Broadcast to send to the network.

If all goes well, the window with the generated TX will be displayed

And in all the wallets you will be able to see how the funds go to the selected address.

In wallet information appears data of the wallets involved in the multi-signature.

Fin

And this is the process for multisigning in Radiant from Electron-Wallet 0.1.3

Antares

I have sent 10.5 rxd for testing to the address: 3Be9yJ3qBuHVwEsCbptmxVykRhQcXwThC4

TX:

https://www.bitstamp.net/learn/security/what-is-a-multisig-wallet
https://www.coindesk.com/tech/2020/11/10/multisignature-wallets-can-keep-your-coins-safer-if-you-use-them-right
https://radiantexplorer.com/address/3Be9yJ3qBuHVwEsCbptmxVykRhQcXwThC4
https://radiantexplorer.com/tx/d960b5588af7302d689b00f226db4bf2e87f348b490764b7e2034479dd6d08bf
Logo
3KB
counter_debug_desc.json