Test public : une solution pour la confidentialité et l'évolutivité sur Ethereum

Blockchain est une technologie innovante qui promet d’améliorer de nombreux domaines de la vie humaine. Il transfère des processus et des produits réels dans l'espace numérique, garantit la rapidité et la fiabilité des transactions financières, réduit leur coût et vous permet également de créer des applications DAPP modernes à l'aide de contrats intelligents dans des réseaux décentralisés.

Compte tenu des nombreux avantages et des diverses applications de la blockchain, il peut paraître surprenant que cette technologie prometteuse n’ait pas encore fait son chemin dans tous les secteurs. Le problème est que les blockchains décentralisées modernes manquent d’évolutivité. Ethereum traite environ 20 transactions par seconde, ce qui n'est pas suffisant pour répondre aux besoins des entreprises dynamiques d'aujourd'hui. Dans le même temps, les entreprises utilisant la technologie blockchain hésitent à abandonner Ethereum en raison de son degré élevé de protection contre le piratage et les pannes de réseau.

Pour assurer la décentralisation, la sécurité et l'évolutivité de la blockchain, résolvant ainsi le trilemme de l'évolutivité, l'équipe de développement Opporty a créé Plasma Cash, une chaîne filiale composée d'un contrat intelligent et d'un réseau privé basé sur Node.js, qui transfère périodiquement son état à la chaîne racine (Ethereum).

Test public : une solution pour la confidentialité et l'évolutivité sur Ethereum

Processus clés dans Plasma Cash

1. L'utilisateur appelle la fonction de contrat intelligent « dépôt », en y transmettant le montant d'ETH qu'il souhaite déposer dans le jeton Plasma Cash. La fonction de contrat intelligent crée un jeton et génère un événement à ce sujet.

2. Les nœuds Plasma Cash abonnés aux événements de contrat intelligent reçoivent un événement sur la création d'un dépôt et ajoutent une transaction sur la création d'un jeton au pool.

3. Périodiquement, des nœuds Plasma Cash spéciaux prennent toutes les transactions du pool (jusqu'à 1 million) et forment un bloc à partir d'elles, calculent l'arbre Merkle et, par conséquent, le hachage. Ce bloc est envoyé à d'autres nœuds pour vérification. Les nœuds vérifient si le hachage Merkle est valide et si les transactions sont valides (par exemple, si l'expéditeur du jeton est son propriétaire). Après avoir vérifié le bloc, le nœud appelle la fonction «submitBlock» du contrat intelligent, qui enregistre le numéro de bloc et le hachage Merkle dans la chaîne Edge. Le contrat intelligent génère un événement indiquant l'ajout réussi d'un bloc. Les transactions sont supprimées du pool.

4. Les nœuds qui reçoivent l'événement de soumission de bloc commencent à appliquer les transactions qui ont été ajoutées au bloc.

5. À un moment donné, le propriétaire (ou non-propriétaire) du token souhaite le retirer de Plasma Cash. Pour ce faire, il appelle la fonction `startExit`, en lui transmettant des informations sur les 2 dernières transactions sur le token, qui confirment qu'il est le propriétaire du token. Le contrat intelligent, utilisant le hachage Merkle, vérifie la présence de transactions dans les blocs et envoie le jeton pour retrait, ce qui aura lieu dans deux semaines.

6. Si l'opération de retrait du token s'est produite avec des violations (le token a été dépensé après le début de la procédure de retrait ou le token appartenait déjà à quelqu'un d'autre avant le retrait), le propriétaire du token peut réfuter le retrait dans un délai de deux semaines.

Test public : une solution pour la confidentialité et l'évolutivité sur Ethereum

La confidentialité est obtenue de deux manières

1. La chaîne racine ne sait rien des transactions générées et transmises au sein de la chaîne enfant. Les informations sur les personnes qui ont déposé et retiré l'ETH de Plasma Cash restent publiques.

2. La chaîne enfant permet des transactions anonymes à l'aide de zk-SNARK.

Pile technologique

  • NodeJS
  • Redis
  • Etherium
  • Soild

Test

Lors du développement de Plasma Cash, nous avons testé la rapidité du système et obtenu les résultats suivants :

  • jusqu'à 35 000 transactions par seconde sont ajoutées au pool ;
  • jusqu'à 1 000 000 de transactions peuvent être stockées dans un bloc.

Les tests ont été effectués sur les 3 serveurs suivants :

1. Intel Core i7-6700 Quad-Core Skylake incl. SSD NVMe – 512 Go, 64 Go de RAM DDR4
3 nœuds de validation Plasma Cash ont été levés.

2. AMD Ryzen 7 1700X Octa-Core « Summit Ridge » (Zen), SSD SATA – 500 Go, 64 Go de RAM DDR4
Le nœud Ropsten testnet ETH a été activé.
3 nœuds de validation Plasma Cash ont été levés.

3. Intel Core i9-9900K Octa-Core avec. SSD NVMe – 1 To, 64 Go de RAM DDR4
1 nœud de soumission Plasma Cash a été généré.
3 nœuds de validation Plasma Cash ont été levés.
Un test a été lancé pour ajouter des transactions au réseau Plasma Cash.

Total: 10 nœuds Plasma Cash dans un réseau privé.

Essai 1

Il y a une limite de 1 million de transactions par bloc. Par conséquent, 1 million de transactions sont divisées en 2 blocs (puisque le système parvient à prendre une partie des transactions et à les soumettre pendant leur envoi).


Etat initial : dernier bloc #7 ; 1 million de transactions et de jetons sont stockés dans la base de données.

00h00 — début du script de génération de transaction
01:37 - 1 million de transactions ont été créées et l'envoi vers le nœud a commencé
01h46 — Le nœud de soumission a effectué 240 8 transactions du pool et forme le bloc n°320. Nous voyons également que 10 XNUMX transactions sont ajoutées au pool en XNUMX secondes
01h58 — le bloc n°8 est signé et envoyé pour validation
02h03 — le bloc n°8 est validé et la fonction `submitBlock` du contrat intelligent est appelée avec le hachage Merkle et le numéro de bloc
02h10 — Le script de démonstration a fini de fonctionner, qui a envoyé 1 million de transactions en 32 secondes
02:33 - les nœuds ont commencé à recevoir des informations indiquant que le bloc n°8 avait été ajouté à la chaîne racine et ont commencé à effectuer 240 XNUMX transactions.
02h40 - 240 8 transactions ont été supprimées du pool, qui se trouvent déjà dans le bloc n°XNUMX.
02:56 — Le nœud de soumission a pris les 760 9 transactions restantes du pool et a commencé à calculer le hachage Merkle et le bloc de signature n°XNUMX.
03h20 - tous les nœuds contiennent 1 million de 240 XNUMX transactions et jetons
03h35 — le bloc n°9 est signé et envoyé pour validation à d'autres nœuds
03:41 - une erreur réseau s'est produite
04h40 — l'attente de la validation du bloc n°9 a expiré
04:54 — Le nœud de soumission a pris les 760 9 transactions restantes du pool et a commencé à calculer le hachage Merkle et le bloc de signature n°XNUMX.
05h32 — le bloc n°9 est signé et envoyé pour validation à d'autres nœuds
05h53 — le bloc n°9 est validé et envoyé à la chaîne racine
06h17 - les nœuds ont commencé à recevoir des informations indiquant que le bloc n°9 avait été ajouté à la chaîne racine et ont commencé à effectuer 760 XNUMX transactions.
06h47 — le pool a effacé les transactions qui se trouvent dans le bloc n°9
09h06 - tous les nœuds contiennent 2 millions de transactions et de jetons

Essai 2

Il y a une limite de 350 3 par bloc. En conséquence, nous avons XNUMX blocs.


Etat initial : dernier bloc #9 ; 2 millions de transactions et de jetons sont stockés dans la base de données

00h00 — le script de génération de transaction a déjà été lancé
00:44 - 1 million de transactions ont été créées et l'envoi vers le nœud a commencé
00h56 — Le nœud de soumission a effectué 320 10 transactions du pool et forme le bloc n°320. Nous voyons également que 10 XNUMX transactions sont ajoutées au pool en XNUMX secondes
01h12 — le bloc n°10 est signé et envoyé à d'autres nœuds pour validation
01h18 — Le script de démonstration a fini de fonctionner, qui a envoyé 1 million de transactions en 34 secondes
01h20 — le bloc n°10 est validé et envoyé à la chaîne racine
01:51 - tous les nœuds ont reçu des informations de la chaîne racine indiquant que le bloc n°10 a été ajouté et commencent à appliquer 320 XNUMX transactions
02h01 - le pool a été autorisé pour 320 10 transactions qui ont été ajoutées au bloc n°XNUMX
02h15 — Le nœud de soumission a effectué 350 11 transactions du pool et forme le bloc n°XNUMX
02h34 — le bloc n°11 est signé et envoyé à d'autres nœuds pour validation
02h51 — le bloc n°11 est validé et envoyé à la chaîne racine
02h55 — le dernier nœud a terminé les transactions du bloc n°10
10h59 — la transaction avec la soumission du bloc n°9 a pris très longtemps dans la chaîne racine, mais elle a été terminée et tous les nœuds ont reçu des informations à ce sujet et ont commencé à effectuer 350 XNUMX transactions
11h05 - le pool a été autorisé pour 320 11 transactions qui ont été ajoutées au bloc n°XNUMX
12h10 - tous les nœuds contiennent 1 million de 670 XNUMX transactions et jetons
12h17 — Le nœud de soumission a effectué 330 12 transactions du pool et forme le bloc n°XNUMX
12h32 — le bloc n°12 est signé et envoyé à d'autres nœuds pour validation
12h39 — le bloc n°12 est validé et envoyé à la chaîne racine
13h44 - tous les nœuds ont reçu des informations de la chaîne racine indiquant que le bloc n°12 a été ajouté et commencent à appliquer 330 XNUMX transactions.
14h50 - tous les nœuds contiennent 2 millions de transactions et de jetons

Essai 3

Dans les premier et deuxième serveurs, un nœud de validation a été remplacé par un nœud de soumission.


Etat initial : dernier bloc #84 ; 0 transactions et jetons enregistrés dans la base de données

00h00 — 3 scripts ont été lancés qui génèrent et envoient chacun 1 million de transactions
01:38 — 1 million de transactions ont été créées et l'envoi vers le nœud de soumission n°3 a commencé
01h50 — Le nœud de soumission n°3 a pris 330 85 transactions du pool et forme le bloc n°21 (f350). Nous voyons également que 10 XNUMX transactions sont ajoutées au pool en XNUMX secondes
01:53 — 1 million de transactions ont été créées et l'envoi vers le nœud de soumission n°1 a commencé
01h50 — Le nœud de soumission n°3 a pris 330 85 transactions du pool et forme le bloc n°21 (f350). Nous voyons également que 10 XNUMX transactions sont ajoutées au pool en XNUMX secondes
02h01 — Le nœud de soumission n°1 a pris 250 85 transactions du pool et forme le bloc n°65 (XNUMXe)
02h06 — le bloc n°85 (f21) est signé et envoyé à d'autres nœuds pour validation
02h08 — le script de démonstration du serveur n°3, qui a envoyé 1 million de transactions en 30 secondes, a fini de fonctionner
02h14 — le bloc #85 (f21) est validé et envoyé à la chaîne racine
02h19 — le bloc n°85 (65e) est signé et envoyé à d'autres nœuds pour validation
02:22 — 1 million de transactions ont été créées et l'envoi vers le nœud de soumission n°2 a commencé
02:27 — bloc #85 (65e) validé et envoyé à la chaîne racine
02h29 — Le nœud de soumission n°2 a pris 111855 transactions du pool et forme le bloc n°85 (256).
02h36 — le bloc n°85 (256) est signé et envoyé à d'autres nœuds pour validation
02h36 — le script de démonstration du serveur n°1, qui a envoyé 1 million de transactions en 42.5 secondes, a fini de fonctionner
02h38 — le bloc #85 (256) est validé et envoyé à la chaîne racine
03h08 — Le script du serveur n°2 a fini de fonctionner, qui a envoyé 1 million de transactions en 47 secondes
03:38 - tous les nœuds ont reçu des informations de la chaîne racine qui bloquent les blocs #85 (f21), #86(65e), #87(256) et ont commencé à appliquer 330 250, 111855 XNUMX, XNUMX XNUMX transactions.
03h49 - le pool a été effacé à 330 250, 111855 85, 21 86 transactions qui ont été ajoutées aux blocs #65 (f87), #256(XNUMXe), #XNUMX(XNUMX)
03h59 — Le nœud de soumission n°1 a pris 888145 88 transactions du pool et le bloc de formulaires n°214 (2), le nœud de soumission n°750 a pris 88 50 transactions du pool et le bloc de formulaires n°3 (670a), le nœud de soumission n°88 a pris 3 XNUMX transactions de la piscine et les formulaires du bloc #XNUMX (dXNUMXb)
04h44 — le bloc n°88 (d3b) est signé et envoyé à d'autres nœuds pour validation
04h58 — le bloc n°88 (214) est signé et envoyé à d'autres nœuds pour validation
05h11 — le bloc n°88 (50a) est signé et envoyé à d'autres nœuds pour validation
05h11 — le bloc #85 (d3b) est validé et envoyé à la chaîne racine
05h36 — le bloc #85 (214) est validé et envoyé à la chaîne racine
05h43 - tous les nœuds ont reçu des informations de la chaîne racine qui bloquent #88 (d3b), #89(214) ont été ajoutés et commencent à appliquer 670 750, XNUMX XNUMX transactions
06h50 — en raison d'un échec de communication, le bloc n°85 (50a) n'a pas été validé
06h55 — Le nœud de soumission n°2 a pris 888145 90 transactions du pool et forme le bloc n°50 (XNUMXa)
08h14 — le bloc n°90 (50a) est signé et envoyé à d'autres nœuds pour validation
09h04 — le bloc n°90 (50a) est validé et envoyé à la chaîne racine
11h23 - tous les nœuds ont reçu des informations de la chaîne racine indiquant que le bloc n°90 (50a) a été ajouté et commencent à appliquer 888145 3 transactions. En même temps, le serveur n°88 a déjà appliqué les transactions des blocs n°3 (d89b), #214(XNUMX)
12h11 - toutes les piscines sont vides
13h41 — tous les nœuds du serveur n°3 contiennent 3 millions de transactions et de jetons
14h35 — tous les nœuds du serveur n°1 contiennent 3 millions de transactions et de jetons
19h24 — tous les nœuds du serveur n°2 contiennent 3 millions de transactions et de jetons

Les obstacles

Lors du développement de Plasma Cash, nous avons rencontré les problèmes suivants, que nous avons progressivement résolus et que nous résolvons actuellement :

1. Conflit dans l'interaction de diverses fonctions du système. Par exemple, la fonction d'ajout de transactions au pool bloquait le travail de soumission et de validation des blocs, et vice versa, ce qui entraînait une baisse de vitesse.

2. Il n'était pas immédiatement clair comment envoyer un grand nombre de transactions tout en minimisant les coûts de transfert de données.

3. Il n'était pas clair comment et où stocker les données afin d'obtenir des résultats élevés.

4. Il n'était pas clair comment organiser un réseau entre les nœuds, car la taille d'un bloc avec 1 million de transactions occupe environ 100 Mo.

5. Travailler en mode monothread rompt la connexion entre les nœuds lorsque de longs calculs se produisent (par exemple, construire un arbre Merkle et calculer son hachage).

Comment avons-nous géré tout cela ?

La première version du nœud Plasma Cash était une sorte de moissonneuse-batteuse qui pouvait tout faire en même temps : accepter des transactions, soumettre et valider des blocs et fournir une API pour accéder aux données. Étant donné que NodeJS est nativement monothread, la lourde fonction de calcul d'arbre Merkle a bloqué la fonction d'ajout de transaction. Nous avons vu deux options pour résoudre ce problème :

1. Lancez plusieurs processus NodeJS, chacun remplissant des fonctions spécifiques.

2. Utilisez work_threads et déplacez l'exécution d'une partie du code dans les threads.

En conséquence, nous avons utilisé les deux options en même temps : nous avons logiquement divisé un nœud en 3 parties qui peuvent fonctionner séparément, mais en même temps de manière synchrone

1. Nœud de soumission, qui accepte les transactions dans le pool et crée des blocs.

2. Un nœud de validation qui vérifie la validité des nœuds.

3. Nœud API - fournit une API pour accéder aux données.

Dans ce cas, vous pouvez vous connecter à chaque nœud via un socket Unix à l'aide de cli.

Nous avons déplacé les opérations lourdes, telles que le calcul de l'arbre Merkle, dans un thread séparé.

Ainsi, nous avons obtenu un fonctionnement normal de toutes les fonctions de Plasma Cash simultanément et sans panne.

Une fois le système fonctionnel, nous avons commencé à tester la vitesse et, malheureusement, nous avons obtenu des résultats insatisfaisants : 5 000 transactions par seconde et jusqu'à 50 000 transactions par bloc. J'ai dû comprendre ce qui n'avait pas été implémenté correctement.

Pour commencer, nous avons commencé à tester le mécanisme de communication avec Plasma Cash pour connaître la capacité maximale du système. Nous avons écrit plus tôt que le nœud Plasma Cash fournit une interface socket Unix. Au départ, c'était basé sur du texte. Les objets json ont été envoyés à l'aide de `JSON.parse()` et `JSON.stringify()`.

```json
{
  "action": "sendTransaction",
  "payload":{
    "prevHash": "0x8a88cc4217745fd0b4eb161f6923235da10593be66b841d47da86b9cd95d93e0",
    "prevBlock": 41,
    "tokenId": "57570139642005649136210751546585740989890521125187435281313126554130572876445",
    "newOwner": "0x200eabe5b26e547446ae5821622892291632d4f4",
    "type": "pay",
    "data": "",
    "signature": "0xd1107d0c6df15e01e168e631a386363c72206cb75b233f8f3cf883134854967e1cd9b3306cc5c0ce58f0a7397ae9b2487501b56695fe3a3c90ec0f61c7ea4a721c"
  }
}
```

Nous avons mesuré la vitesse de transfert de ces objets et avons trouvé environ 130 8 par seconde. Nous avons essayé de remplacer les fonctions standard pour travailler avec json, mais les performances ne se sont pas améliorées. Le moteur VXNUMX doit être bien optimisé pour ces opérations.

Nous avons travaillé avec des transactions, des jetons et des blocs à travers les classes. Lors de la création de telles classes, les performances ont chuté de 2 fois, ce qui indique que la POO ne nous convient pas. J'ai dû tout réécrire dans une approche purement fonctionnelle.

Enregistrement dans la base de données

Initialement, Redis a été choisi pour le stockage de données comme l'une des solutions les plus productives répondant à nos exigences : stockage clé-valeur, travail avec des tables de hachage, des ensembles. Nous avons lancé Redis-benchmark et obtenu environ 80 1 opérations par seconde en XNUMX mode pipeline.

Pour des performances élevées, nous avons réglé Redis plus finement :

  • Une connexion socket Unix a été établie.
  • Nous avons désactivé l'enregistrement de l'état sur le disque (pour plus de fiabilité, vous pouvez configurer une réplique et l'enregistrer sur le disque dans un Redis séparé).

Dans Redis, un pool est une table de hachage car nous devons pouvoir récupérer toutes les transactions en une seule requête et supprimer les transactions une par une. Nous avons essayé d'utiliser une liste normale, mais elle est plus lente lors du déchargement de la liste entière.

Lors de l'utilisation de NodeJS standard, les bibliothèques Redis ont atteint une performance de 18 9 transactions par seconde. La vitesse a chuté XNUMX fois.

Puisque le benchmark nous a montré que les possibilités étaient clairement 5 fois plus grandes, nous avons commencé à optimiser. Nous avons changé la bibliothèque en ioredis et obtenu des performances de 25 32 par seconde. Nous avons ajouté les transactions une par une à l'aide de la commande `hset`. Nous générions donc beaucoup de requêtes dans Redis. L'idée est née de combiner les transactions en lots et de les envoyer avec une seule commande « hmset ». Le résultat est de XNUMX XNUMX par seconde.

Pour plusieurs raisons, que nous décrirons ci-dessous, nous travaillons avec des données en utilisant `Buffer` et, il s'avère que si vous les convertissez en texte (`buffer.toString('hex')`) avant d'écrire, vous pouvez obtenir des performance. Ainsi, la vitesse a été augmentée à 35 XNUMX par seconde. Pour le moment, nous avons décidé de suspendre toute optimisation ultérieure.

Nous avons dû passer à un protocole binaire car :

1. Le système calcule souvent des hachages, des signatures, etc., et pour cela, il a besoin de données dans le « Buffer ».

2. Lorsqu'elles sont envoyées entre services, les données binaires pèsent moins que le texte. Par exemple, lors de l'envoi d'un bloc contenant 1 million de transactions, les données contenues dans le texte peuvent occuper plus de 300 mégaoctets.

3. La transformation constante des données affecte les performances.

Par conséquent, nous avons pris comme base notre propre protocole binaire de stockage et de transmission de données, développé sur la base de la merveilleuse bibliothèque « binaire-données ».

En conséquence, nous avons obtenu les structures de données suivantes :

-Transaction

  ```json
  {
    prevHash: BD.types.buffer(20),
    prevBlock: BD.types.uint24le,
    tokenId: BD.types.string(null),
    type: BD.types.uint8,
    newOwner: BD.types.buffer(20),
    dataLength: BD.types.uint24le,
    data: BD.types.buffer(({current}) => current.dataLength),
    signature: BD.types.buffer(65),
    hash: BD.types.buffer(32),
    blockNumber: BD.types.uint24le,
    timestamp: BD.types.uint48le,
  }
  ```

- Jeton

  ```json
  {
    id: BD.types.string(null),
    owner: BD.types.buffer(20),
    block: BD.types.uint24le,
    amount: BD.types.string(null),
  }
  ```

-Bloc

  ```json
  {
    number: BD.types.uint24le,
    merkleRootHash: BD.types.buffer(32),
    signature: BD.types.buffer(65),
    countTx: BD.types.uint24le,
    transactions: BD.types.array(Transaction.Protocol, ({current}) => current.countTx),
    timestamp: BD.types.uint48le,
  }
  ```

Avec les commandes habituelles `BD.encode(block, Protocol).slice();` et `BD.decode(buffer, Protocol)` nous convertissons les données en `Buffer` pour les enregistrer dans Redis ou les transférer vers un autre nœud et récupérer le données en arrière.

Nous disposons également de 2 protocoles binaires pour transférer des données entre services :

— Protocole d'interaction avec Plasma Node via socket Unix

  ```json
  {
    type: BD.types.uint8,
    messageId: BD.types.uint24le,
    error: BD.types.uint8,
    length: BD.types.uint24le,
    payload: BD.types.buffer(({node}) => node.length)
  }
  ```

où:

  • `type` — l'action à effectuer, par exemple 1 — sendTransaction, 2 — getTransaction ;
  • `charge utile` — les données qui doivent être transmises à la fonction appropriée ;
  • `messageId` — identifiant du message afin que la réponse puisse être identifiée.

— Protocole d'interaction entre nœuds

  ```json
  {
    code: BD.types.uint8,
    versionProtocol: BD.types.uint24le,
    seq: BD.types.uint8,
    countChunk: BD.types.uint24le,
    chunkNumber: BD.types.uint24le,
    length: BD.types.uint24le,
    payload: BD.types.buffer(({node}) => node.length)
  }
  ```

où:

  • "code" — code de message, par exemple 6 — PREPARE_NEW_BLOCK, 7 — BLOCK_VALID, 8 — BLOCK_COMMIT ;
  • `versionProtocole` — la version du protocole, puisque des nœuds avec des versions différentes peuvent être créés sur le réseau et fonctionner différemment ;
  • `séquence` — identifiant du message ;
  • `countChunk` и `numéromorceau` nécessaire pour diviser les messages volumineux ;
  • 'longueur' и `charge utile` la longueur et les données elles-mêmes.

Puisque nous avons pré-saisi les données, le système final est beaucoup plus rapide que la bibliothèque `rlp` d'Ethereum. Malheureusement, nous n'avons pas encore pu le refuser, car il est nécessaire de finaliser le contrat intelligent, ce que nous prévoyons de faire dans le futur.

Si nous parvenions à atteindre la vitesse +35 (000)XNUMX XNUMX transactions par seconde, nous devons également les traiter dans le délai optimal. Étant donné que le temps approximatif de formation d'un bloc prend 30 secondes, nous devons inclure dans le bloc +1 (000)000 transactions, ce qui signifie envoyer plus 100 Mo de données.

Initialement, nous utilisions la bibliothèque `ethereumjs-devp2p` pour communiquer entre les nœuds, mais elle ne pouvait pas gérer autant de données. En conséquence, nous avons utilisé la bibliothèque `ws` et configuré l'envoi de données binaires via websocket. Bien sûr, nous avons également rencontré des problèmes lors de l'envoi de paquets de données volumineux, mais nous les avons divisés en morceaux et ces problèmes ont désormais disparu.

Former également un arbre Merkle et calculer le hachage +1 (000)000 les transactions nécessitent environ 10 secondes de calcul continu. Pendant ce temps, la connexion avec tous les nœuds parvient à être interrompue. Il a été décidé de déplacer ce calcul vers un fil de discussion distinct.

Conclusions:

En fait, nos découvertes ne sont pas nouvelles, mais pour une raison quelconque, de nombreux experts les oublient lors de leur développement.

  • L'utilisation de la programmation fonctionnelle au lieu de la programmation orientée objet améliore la productivité.
  • Le monolithe est pire qu'une architecture de service pour un système NodeJS productif.
  • L'utilisation de `worker_threads` pour des calculs lourds améliore la réactivité du système, en particulier lorsqu'il s'agit d'opérations d'E/S.
  • Le socket Unix est plus stable et plus rapide que les requêtes http.
  • Si vous avez besoin de transférer rapidement des données volumineuses sur le réseau, il est préférable d'utiliser des websockets et d'envoyer des données binaires, divisées en morceaux, qui peuvent être transmis si elles n'arrivent pas, puis combinées en un seul message.

Nous vous invitons à visiter GitHub projet: https://github.com/opporty-com/Plasma-Cash/tree/new-version

L'article a été co-écrit par Alexandre Nachivan, DEVELOPPEUR sénior Solution Intelligente Inc.

Source: habr.com

Ajouter un commentaire