Cardano sidechains (Toolkit) | Partie 2

Introduction

Le Sidechain Toolkit(boîte à outils) est un ensemble de composants et de recommandations pour étendre Cardano. La boîte à outils permet aux développeurs de blockchain de déployer des solutions personnalisables qui répondent à leurs besoins techniques et sociaux. La boîte à outils est composée de trois composants principaux :

Scripts Plutus de la chaîne principale

Les scripts Plutus déployés sur la chaîne principale Cardano définissent le flux d’une chaîne latérale particulière. Ces scripts sont le début de l’initialisation d’une sidechain contenant des scripts Plutus pour les enregistrements de candidats producteurs de blocs, les politiques de frappe du jeton de la sidechain et les fonctions de déplacement des jetons.

Suiveur de chaîne

Le suiveur de chaîne est capable d’observer l’état des événements de la chaîne principale qui régissent la chaîne latérale et est capable de communiquer ces événements à la chaîne latérale. La version 1 de la boîte à outils utilise actuellement une instance Cardano DB Sync pour s’adapter à cela.

Module spécifique à la sidechain

Ce composant fait partie du validateur de nœud sidechain. Il a la responsabilité de filtrer et de transformer les données provenant du suiveur de chaîne et de les présenter aux composants qui les traiteront conformément aux règles régissant la spécification technique de la sidechain.

Spécifications techniques

La spécification du client sidechain Cardano est un document détaillé décrivant l’architecture, les cas d’utilisation et la mise en œuvre des composants de la sidechain. L’exemple d’implémentation EVM est le résultat de conception et d’ingénierie qui présente une implémentation possible de ladite spécification.

Scripts Plutus de la chaîne principale

La chaîne principale utilise les composants suivants pour gérer les interactions avec une chaîne latérale :

  • SidechainTokenMintingPolicy: une politique de frappe qui valide la frappe ou la gravure de jetons de sidechain (SC_Token) sur la chaîne principale.
  • MPTRootTokenMintingPolicy: une politique de frappe pour stocker les racines MPT des bundles de transactions inter-chaînes.
  • CommitteeCandidateValidator: une adresse de script pour les candidats du comité.
  • MPTRootTokenValidator: une adresse de script pour stocker MPTRootTokens.
  • CommitteeHashValidator: une adresse de script pour le hachage des membres du comité.

Toutes ces politiques et validateurs sont paramétrés par les paramètres de la chaîne latérale, ce qui permet d’obtenir des hachages uniques de politique de frappe et de script de validateur.

données SidechainParams = SidechainParams { chainId : : Entier , genesisHash :: GenesisHash _ – ^ ‘GenesisHash’ est un alias de type pour ByteString , genesisMint :: Peut- être TxOutRef – ^ ‘genesisMint’ est un ‘TxOutRef’ arbitraire utilisé dans la configuration Passive Bridge , où – La création de SC_Token ne peut se produire qu’une seule fois . Ce paramètre sera supprimé dans le produit final . , genesisUtxo :: TxOutRef _ – ^ ‘genesisUtxo’ est un ‘TxOutRef’ arbitraire utilisé pour identifier – ’ AssetClass’ s ( e . g . see [ 6. ] ( # 6 - update - commission - hash ) ) du – chaîne latérale }

1. Initialiser un contrat

Pour l’initialisation, utilisez un NFT (consommant des UTXO arbitraires) pour identifier de manière unique les membres actuels du comité en stockant le hachage des clés publiques concaténées sur la chaîne (voir 6.1). Ce comité est utilisé pour vérifier les signatures pour les transferts sidechain vers la chaîne principale (voir 3.1).

Flux de travail :

  1. Appelez le point de terminaison initialize sidechain pour générer le SidechainParams pour une nouvelle sidechain.
  2. Utilisez les paramètres de chaîne latérale donnés pour le reste des points de terminaison afin de travailler avec cette chaîne latérale particulière.

Paramètres de point de terminaison :

données InitSidechainParams = InitSidechainParams { initChainId : : Entier , initGenesisHash :: GenesisHash _ – ^ ‘GenesisHash’ est un alias de type pour ByteString , initUtxo :: TxOutRef _ – ^ ‘initUtxo’ est utilisé pour créer le comité NFT , initCommittee : : [ PubKey ] – ^ ‘initCommittee’ est le comité initial de la sidechain , initSidechainEpoch :: Entier _ – ^ ‘initSidechainEpoch’ est l’époque initiale de la sidechain de la sidechain , initMint :: Peut- être TxOutRef – ^ ‘initMint’ est utilisé uniquement dans le pont passif et sera supprimé dans le produit final }

2. Transférez les jetons SC_Token de la chaîne principale vers la chaîne latérale

Les jetons SC_Token sur le réseau Cardano représentent des actifs natifs verrouillés sur le réseau sidechain. Lorsque certains jetons sont verrouillés sur un certain contrat de sidechain, vous pouvez frapper le montant équivalent sur Cardano. A l’inverse graver ces tokens sur le réseau Cardano (voir 3) libérera ces tokens et les enverra au propriétaire.

Flux de travail :

  1. Appelez le point de terminaison de gravure du contrat avec BurnParams.
  2. Une transaction sera soumise à la chaîne principale en brûlant le nombre spécifié de jetons SC_Token et l’adresse de chaîne secondaire correspondante dans le rédempteur.
  3. Le composant Bridge (observant la chaîne principale où la politique de frappe donnée est gérée) vérifiera la transaction et créera une transaction sidechain appropriée.

Paramètres de point de terminaison :

données BurnParams = BurnParams { destinataire :: : SidechainAddress – ^ ’ SidechainAddress’ est un alias de type pour un ByteString ( par exemple 0x112233aabbcc ) , montant : : Entier }

Transaction entre la chaîne principale et la chaîne latérale (gravure des jetons SC_Token)

3. Transférez les jetons SC_Token de la sidechain vers la chaîne principale

Flux de travail :

  1. Sidechain collecte les transactions non gérées et les regroupe à la fin de chaque époque de sidechain.
  2. Les producteurs de blocs Sidechain calculent txs = outgoing_txs.map(tx => blake2b(cbor(MerkleTreeEntry(tx))) pour chaque transaction (voir MerkleTreeEntry) et créent un arbre Merkle à partir de celles-ci. La racine de cet arbre est signée par au moins t(seuil multisig) des membres du comité avec un schéma de signature en annexe.
  3. Bridge diffuse la racine de Merkle à la chaîne.
  4. Les transactions peuvent être réclamées individuellement (voir 3.2).

3.1. Insertion de la racine de Merkel

Paramètres de point de terminaison pour l’insertion de la racine Merkle :

données SaveRootParams = SaveRootParams { sidechainParams : : SidechainParams – ^ Paramètres identifiant la Sidechain , merkleRoot :: ByteString _ , précédentMerkleRoot :: Peut- être ByteString – ^ Chaîner les racines de Merkle pour assurer l’ ordre . La première racine aura Nothing ici . , comitéSignatures : : [ ( SidechainPubKey , Peut-être ByteString ) ] – ^ Clés publiques de tous les membres du comité avec leurs signatures correspondantes s’il y en a une }

Les racines Merkle sont stockées sur la chaîne, en utilisant MPTRootTokens, où tokenName est la racine Merkle. Ces jetons doivent se trouver à l’ MPTRootTokenValidator adresse du script.

Rédempteur:

données SignedMerkleRoot = SignedMerkleRoot { merkleRoot : : ByteString , previousMerkleRoot :: Maybe ByteString – Dernier hachage racine Merkle , signatures :: [ ByteString ] – Signatures de comité actuelles classées comme leurs clés correspondantes , bénéficiaire : : SidechainAddress , commissionPubKeys :: [ SidechainPubKey ] – Clés publiques triées lexicographiquement de tous les membres du comité }

Une politique de frappe vérifie que :

  • le hachage de commissionPublicKeys correspond au hachage enregistré sur la chaîne
  • toutes les signatures fournies sont valides
  • taille(signatures) > 2/3 * taille(committeePubKeys)
  • une liste de clés publiques ne contient pas de doublons
  • si previousMerkleRoot est spécifié, l’UTXO avec le roothash donné est référencé dans la transaction en tant qu’entrée de référence

Un script de validation vérifie les éléments suivants :

  • Les UTXO contenant un MPTRootToken ne peuvent pas être déverrouillés à partir de l’adresse du script

Frappe de jetons racine Merkle

L’arbre Merkle doit être construit exactement de la même manière que dans l’implémentation suivante de l’arbre Merkle. Les entrées dans l’arborescence doivent être calculées comme suit :

données MerkleTreeEntry = MerkleTreeEntry { index :: Integer – Entier non signé de 32 bits , utilisé pour assurer l’unicité des transactions au sein de l’ arborescence , montant :: Integer – Entier non signé de 256 bits qui représente le nombre de jetons envoyés depuis le pont , destinataire :: ByteString - chaîne d’octets de longueur arbitraire qui représente l’adresse bech32 cardano décodée , previousMerkleRoot :: Maybe ByteString – previousMerkleRoot est ajouté pour s’assurer que l’entrée hachée est unique }

entrée = blake2b ( cbor ( MerkleTreeEntry ) )

Les signatures pour l’arbre de Merkle doivent être construites comme suit :

données MerkleRootInsertionMessage = MerkleRootInsertionMessage { sidechainParams : : SidechainParams – ^ Paramètres identifiant la Sidechain , merkleRoot :: ByteString _ , précédentMerkleRoot :: Peut- être ByteString }

signature = ecdsa . signe ( données : blake2b ( cbor ( MerkleRootInsertionMessage ) ) , clé : commissionMemberPrvKey )

3.2. Réclamation individuelle

Paramètres de point de terminaison pour revendiquer :

données MintParams = MintParams { montant : : Entier , destinataire : : ByteString , merkleProof :: MerkleProof _ , indice : : Entier , précédentMerkleRoot :: Peut- être ByteString }

Une politique de frappe vérifie les éléments suivants :

  • MPTRootToken avec le nom de la racine Merkle de la transaction (calculée à partir de la preuve) se trouve dans l’ MPTRootTokenValidator adresse du script
  • destinataire, montant, index et précédentMerkleRoot combinés avec merkleProof correspondent à merkleRootHash
  • claimTransactionHash de la transaction n’est PAS inclus dans l’ensemble distribué
  • une nouvelle entrée avec le claimTransactionHash de la transaction est créée dans l’ensemble distribué
  • la transaction est signée par le destinataire
  • le montant correspond au contenu réel du corps tx.

claimTransactionHash est un blake2(cbor(MerkleTreeEntry)), identifiant de manière unique une transaction inter-chaîne en pointant vers un arbre Merkle et l’index de la transaction dans l’arbre.

Sidechain à la transaction de la chaîne principale (demande de jetons)

Rédempteur de politique de frappe :

données SidechainRedeemer = MainToSide ByteString ByteString – ^ Adresse du destinataire sur la sidechain et la signature de son propriétaire ( voir 2. ) | SideToMain MerkleTreeEntry MerkleProof

4. Inscrire un candidat au comité

Flux de travail :

  1. Un SPO s’inscrivant en tant que producteur de blocs (membre du comité) pour la sidechain envoie BlockProducerRegistration et sa signature (où le message signé contient les paramètres de la sidechain, la clé publique de la sidechain et l’UTXO d’entrée au format CBOR)
  2. Le pont surveillant l’adresse du script du candidat au comité valide les informations d’identification SPO, chainId et l’inputUtxo consommé

Données:

data BlockProducerRegistration = BlockProducerRegistration { bprSpoPubKey :: PubKey – propre clé publique _ , bprInputUtxo :: TxOutRef – un UTXO qui doit être dépensé avec la transaction , bprSidechainPubKey :: ByteString – clé publique dans le format souhaité de la sidechain , bprSpoSignature :: Signature – Signature de la clé privée SPO _ , bprSidechainSignature :: ByteString – Signature de la clé privée de la sidechain }

5. Désinscrire un membre du comité/candidat

Flux de travail :

  1. L’UTXO avec les informations d’enregistrement peut être échangé par l’expéditeur d’origine (il n’est pas nécessaire de vérifier l’entrée Utxo)
  2. Le pont surveillant l’adresse du script du candidat du comité interprète cela comme une action de désinscription

6. Passage de témoin au comité

Dans l’implémentation actuelle de la sidechain, une insertion de racine Merkle (voir 3.1) ne peut se produire qu’une seule fois par époque de la sidechain au moment du transfert du comité. La sidechain expose un point de terminaison qui peut gérer cette action, mais l’implémentation sous-jacente est détachée, donc en théorie, les actions d’insertion de la racine Merkle et de mise à jour du hachage du comité pourraient être effectuées indépendamment.

L’ordre de ces actions est important. Si la transaction insérant la racine Merkle pour l’époque 1 de la sidechain est soumise après le transfert du comité de committee of epoch 1 à committee of epoch 2 la transaction, la signature deviendra invalide, car elle est signée par le committee of epoch 1 . Pour atténuer ce problème, le Merkle root chain est introduit ; pour plus de détails, voir 6.2.

6.1 Mettre à jour un hachage de comité

  1. Le composant Bridge déclenche la transaction Cardano. Cette transaction effectue les opérations suivantes :

Paramètres du point de terminaison :

données UpdateCommitteeHashParams = UpdateCommitteeHashParams { newCommitteePubKeys : : [ SidechainPubKey ] – ^ Les clés publiques du nouveau comité . _ , comitéSignatures : : [ ( SidechainPubKey , Peut-être ByteString ) ] – ^ Clés publiques de tous les membres du comité avec leurs signatures correspondantes s’il y en a une , sidechainParams :: SidechainParams _ – ^ Paramètres identifiant la Sidechain , précédentMerkleRoot :: Peut- être ByteString – ^ dernière racine Merkle insérée sur la chaîne , à moins qu’il n’y ait pas encore de racine Merkle insérée , sidechainEpoque : : Entier – ^ époque sidechain du nouveau comité }

Le script de validation vérifie les éléments suivants :

  • le hachage de commissionPublicKeys correspond au hachage enregistré sur la chaîne
  • toutes les signatures fournies sont valides
  • taille(signatures) > 2/3 * taille(committeePubKeys)
  • le NFT de l’UTXO détenant l’ancienne clé de vérification à l’adresse du script
  • consomme l’UTXO mentionné ci-dessus
  • époque de la sidechain du nouveau hash de comité > époque de la sidechain du hash de comité consommé utxo
  • génère un nouvel UTXO avec le hachage de comité mis à jour contenant le NFT à la même adresse de script
  • la référence à la dernière racine Merkle est référencée dans la transaction

Données:

données UpdateCommitteeHash = UpdateCommitteeHash { comitéPubKeysHash : : ByteString – ^ Hachage de toutes les clés publiques triées lexicographiquement des membres actuels du comité , sidechainEpoque : : Entier – ^ époque sidechain du comité }

comitéPubKeys = sort ( [ clé1 , clé2 , … , cléN ] ) comitéPubKeysHash = blake2b ( concat ( comitéPubKeys ) ) keyN - Clé publique ecdsa compressée de 33 octets d’ un membre du comité

Transfert de comité (mise à jour du hachage du comité)

Rédempteur:

données UpdateCommitteeRedeemer = UpdateCommitteeRedeemer { signatures : : [ chaîne d’octets ] , newCommitteePubKeys :: [ SidechainPubKey ] _ , comitéPubKeys : : [ SidechainPubKey ] , précédentMerkleRoot :: Peut- être ByteString – ^ dernière racine Merkle insérée sur la chaîne , à moins qu’il n’y ait pas encore de racine Merkle insérée }

Les signatures sont construites comme suit :

SidechainPubKey - Clé publique ecdsa compressée de 33 octets données UpdateCommitteeMessage = UpdateCommitteeMessage { sidechainParams : : SidechainParams , newCommitteePubKeys :: [ SidechainPubKey ] – trié lexicographiquement , précédentMerkleRoot :: Peut- être ByteString – ^ dernière racine Merkle insérée sur la chaîne ( racine Merkle pour la dernière époque de la chaîne latérale ) , sidechainEpoque : : Entier – ^ époque sidechain du comité nouvellement enregistré } signature = ecdsa . signe ( données : blake2b ( cbor ( UpdateCommitteeMessage ) ) , clé : commissionMemberPrvKey )

6.2. Chaînage des racines de Merkle

Comme décrit dans la section 6 « Transfert de comité », l’ordre correct des insertions de racine Merkle et des mises à jour de hachage de comité doit être maintenu. Une nouvelle chaîne racine Merkle fait cela. Chaque racine Merkle a une référence à son prédécesseur (s’il en existe une). De plus, toutes les mises à jour de hachage de comité font référence à la dernière racine Merkle insérée (s’il en existe une).

Chaînage racine Merkle (SC ep = époque sidechain)

Comme on le voit dans le graphique ci-dessus, la première racine de Merkle n’a pas de référence, ce qui est tout à fait valide. L’existence de la dernière racine Merkle n’est pas imposée.

Dans le cas où une époque de sidechain est passée sans aucune transaction inter-chaînes, aucune racine Merkle n’est insérée, ce qui entraîne deux mises à jour de hachage de comité faisant référence à la même racine Merkle.

02

Chaînage racine Merkle - époque sans racine Merkle (SC ep = époque sidechain)

À l’avenir, il pourrait y avoir plusieurs racines Merkle par époque de sidechain, donc le résultat pourrait ressembler à ceci :

03

Chaînage racine Merkle - plusieurs racines Merkle par époque (SC ep = époque sidechain)

Suiveur de chaîne

Un suiveur de chaîne tel que DB Sync est un composant qui observe les transactions sur la chaîne principale telle que Cardano. Le suiveur de chaîne fournit les informations permettant à une chaîne latérale de rester cohérente avec la chaîne principale.

La page DB Sync donne des informations détaillées sur la fonctionnalité et comment la configurer.

Utilisation du suiveur de chaîne sur une sidechain

Le nœud de sidechain utilise DB Sync pour obtenir des informations concernant la chaîne principale.

Voici une liste non exhaustive des données extraites de la chaîne principale :

  • Le nonce pour une époque donnée
  • La répartition des enjeux pour une époque donnée
  • La liste des UTXO pour une adresse donnée, après un bloc donné
  • Le dernier bloc pour un slot
  • Le dernier bloc de la chaîne
  • Les informations de bloc pour un numéro de bloc donné
  • Le numéro d’emplacement correspondant à un UTXO
  • Les transactions inter-chaînes pour une politique donnée, un nom d’actif et une plage de blocs dans un emplacement
  • Les transactions inter-chaînes pour une politique, un nom d’actif et un ID UTXO donnés.

Le client sidechain utilise DB Sync pour écouter l’activité de la chaîne principale et effectuer diverses tâches :

  • Mettre à jour le comité du pool de la sidechain : une transaction spécifique est effectuée sur la chaîne principale, observée avec DB Sync, et prise en compte pour déterminer le comité du pool du slot suivant sur la sidechain
  • Transférez des fonds de la chaîne principale vers la chaîne latérale : lorsqu’un jeton est gravé sur la chaîne principale, la transaction correspondante est observée avec DB Sync et utilisée pour fournir les fonds correspondants sur la chaîne latérale.

Le transfert de fonds de la sidechain vers la chaîne principale n’utilise pas DB Sync, mais un mécanisme différent.

Configuration du nœud sidechain pour utiliser votre instance DB Sync

Après avoir installé et configuré une instance DB Sync, vous devez configurer un nœud sidechain pour qu’il pointe vers celle-ci. Dans le fichier de configuration du nœud, définissez le type de source de données sur db-syncet configurez les détails de connexion en fonction de votre configuration. La configuration terminée devrait ressembler à l’exemple ci-dessous :

{ … “sc-evm” : { “chaîne latérale” : { “source de données” : { “db-sync” : { “connect-thread-pool-taille” : 4 , “driver” : “org.postgresql.Driver” , “host” : “votre.hôte.dbsync” , “name” : “votre nom d’instance de synchronisation de base de données” , “port” : 5432 , “username” : “votre nom d’utilisateur db sync” “password” : “votre mot de passe de synchronisation de la base de données” , } , “type” : “db-sync” } } , … }

Rotation du comité

Une sidechain est initialisée par un SPO, ou « créateur de chaîne », qui publie les implémentations du contrat Sidechain Plutus, y compris les scripts d’inscription au comité. Ces scripts d’inscription permettent à d’autres OPP de s’inscrire ou de se désinscrire en tant que candidat au comité. Le SPO exécute également un nœud de validateur de sidechain qui démarre la sidechain lorsqu’un quorum de validateurs est atteint.

Script vidéo - Rotation des comités (1)

La sidechain est validée par un comité SPO où chaque SPO est un candidat exécutant un nœud de validation de sidechain. A chaque époque, un comité est tiré au sort et passé au suivant.

Script vidéo - Rotation des comités

Source : https://docs.cardano.org/cardano-sidechains/sidechain-toolkit/introduction/