Δημόσια δοκιμή: Μια λύση για το απόρρητο και την επεκτασιμότητα στο Ethereum

Blockchain είναι μια καινοτόμος τεχνολογία που υπόσχεται να βελτιώσει πολλούς τομείς της ανθρώπινης ζωής. Μεταφέρει πραγματικές διαδικασίες και προϊόντα στον ψηφιακό χώρο, διασφαλίζει την ταχύτητα και την αξιοπιστία των χρηματοοικονομικών συναλλαγών, μειώνει το κόστος τους και σας επιτρέπει επίσης να δημιουργείτε σύγχρονες εφαρμογές DAPP χρησιμοποιώντας έξυπνα συμβόλαια σε αποκεντρωμένα δίκτυα.

Δεδομένων των πολλών πλεονεκτημάτων και των διαφορετικών εφαρμογών του blockchain, μπορεί να φαίνεται περίεργο το γεγονός ότι αυτή η πολλά υποσχόμενη τεχνολογία δεν έχει φτάσει ακόμη σε κάθε κλάδο. Το πρόβλημα είναι ότι οι σύγχρονες αποκεντρωμένες αλυσίδες μπλοκ στερούνται επεκτασιμότητας. Το Ethereum επεξεργάζεται περίπου 20 συναλλαγές ανά δευτερόλεπτο, κάτι που δεν αρκεί για να καλύψει τις ανάγκες των σημερινών δυναμικών επιχειρήσεων. Ταυτόχρονα, οι εταιρείες που χρησιμοποιούν τεχνολογία blockchain διστάζουν να εγκαταλείψουν το Ethereum λόγω του υψηλού βαθμού προστασίας του από hacking και αστοχίες δικτύου.

Για να διασφαλιστεί η αποκέντρωση, η ασφάλεια και η επεκτασιμότητα στο blockchain, λύνοντας έτσι το Scalability Trilemma, η ομάδα ανάπτυξης Ευκαιρία δημιούργησε το Plasma Cash, μια θυγατρική αλυσίδα που αποτελείται από ένα έξυπνο συμβόλαιο και ένα ιδιωτικό δίκτυο που βασίζεται στο Node.js, το οποίο μεταφέρει περιοδικά την κατάστασή του στη βασική αλυσίδα (Ethereum).

Δημόσια δοκιμή: Μια λύση για το απόρρητο και την επεκτασιμότητα στο Ethereum

Βασικές διεργασίες στο Plasma Cash

1. Ο χρήστης ονομάζει τη συνάρτηση έξυπνης σύμβασης «κατάθεση», μεταβιβάζοντας σε αυτήν το ποσό του ETH που θέλει να καταθέσει στο διακριτικό μετρητών Plasma. Η λειτουργία έξυπνου συμβολαίου δημιουργεί ένα διακριτικό και δημιουργεί ένα συμβάν σχετικά με αυτό.

2. Οι κόμβοι Plasma Cash που είναι εγγεγραμμένοι σε συμβάντα έξυπνων συμβολαίων λαμβάνουν ένα συμβάν σχετικά με τη δημιουργία κατάθεσης και προσθέτουν μια συναλλαγή σχετικά με τη δημιουργία ενός διακριτικού στο pool.

3. Περιοδικά, οι ειδικοί κόμβοι Plasma Cash λαμβάνουν όλες τις συναλλαγές από το pool (μέχρι 1 εκατομμύριο) και σχηματίζουν ένα μπλοκ από αυτές, υπολογίζουν το δέντρο Merkle και, κατά συνέπεια, τον κατακερματισμό. Αυτό το μπλοκ αποστέλλεται σε άλλους κόμβους για επαλήθευση. Οι κόμβοι ελέγχουν εάν ο κατακερματισμός Merkle είναι έγκυρος και εάν οι συναλλαγές είναι έγκυρες (για παράδειγμα, εάν ο αποστολέας του διακριτικού είναι ο κάτοχός του). Μετά την επαλήθευση του μπλοκ, ο κόμβος καλεί τη συνάρτηση «submitBlock» του έξυπνου συμβολαίου, η οποία αποθηκεύει τον αριθμό του μπλοκ και τον κατακερματισμό Merkle στην αλυσίδα άκρων. Το έξυπνο συμβόλαιο δημιουργεί ένα συμβάν που υποδεικνύει την επιτυχή προσθήκη ενός μπλοκ. Οι συναλλαγές αφαιρούνται από το pool.

4. Οι κόμβοι που λαμβάνουν το συμβάν υποβολής μπλοκ αρχίζουν να εφαρμόζουν τις συναλλαγές που προστέθηκαν στο μπλοκ.

5. Σε κάποιο σημείο, ο κάτοχος (ή μη κάτοχος) του διακριτικού θέλει να το αποσύρει από το Plasma Cash. Για να το κάνει αυτό, καλεί τη συνάρτηση «startExit», μεταβιβάζοντας σε αυτήν πληροφορίες για τις 2 τελευταίες συναλλαγές στο διακριτικό, οι οποίες επιβεβαιώνουν ότι είναι ο κάτοχος του διακριτικού. Το έξυπνο συμβόλαιο, χρησιμοποιώντας τον κατακερματισμό Merkle, ελέγχει την παρουσία συναλλαγών στα μπλοκ και στέλνει το διακριτικό για ανάληψη, η οποία θα πραγματοποιηθεί σε δύο εβδομάδες.

6. Εάν η λειτουργία ανάληψης διακριτικού συνέβη με παραβιάσεις (το κουπόνι ξοδεύτηκε μετά την έναρξη της διαδικασίας ανάληψης ή το διακριτικό ήταν ήδη κάποιου άλλου πριν από την ανάληψη), ο κάτοχος του διακριτικού μπορεί να αντικρούσει την ανάληψη εντός δύο εβδομάδων.

Δημόσια δοκιμή: Μια λύση για το απόρρητο και την επεκτασιμότητα στο Ethereum

Το απόρρητο επιτυγχάνεται με δύο τρόπους

1. Η ριζική αλυσίδα δεν γνωρίζει τίποτα για τις συναλλαγές που δημιουργούνται και προωθούνται εντός της θυγατρικής αλυσίδας. Οι πληροφορίες σχετικά με το ποιος κατέθεσε και απέσυρε ETH από το Plasma Cash παραμένουν δημόσιες.

2. Η θυγατρική αλυσίδα επιτρέπει ανώνυμες συναλλαγές χρησιμοποιώντας zk-SNARK.

Στοίβα τεχνολογίας

  • NodeJS
  • Ρέντη
  • Etherium
  • Soild

Δοκιμές

Κατά την ανάπτυξη του Plasma Cash, δοκιμάσαμε την ταχύτητα του συστήματος και λάβαμε τα ακόλουθα αποτελέσματα:

  • έως και 35 συναλλαγές ανά δευτερόλεπτο προστίθενται στο pool.
  • έως και 1 συναλλαγές μπορούν να αποθηκευτούν σε ένα μπλοκ.

Πραγματοποιήθηκαν δοκιμές στους ακόλουθους 3 διακομιστές:

1. Intel Core i7-6700 Quad-Core Skylake incl. NVMe SSD – 512 GB, 64 GB DDR4 RAM
Δημιουργήθηκαν 3 κόμβοι επικύρωσης Plasma Cash.

2. AMD Ryzen 7 1700X Octa-Core "Summit Ridge" (Zen), SATA SSD – 500 GB, 64 GB DDR4 RAM
Ο κόμβος ETH του δικτύου δοκιμής Ropsten αυξήθηκε.
Δημιουργήθηκαν 3 κόμβοι επικύρωσης Plasma Cash.

3. Intel Core i9-9900K Octa-Core incl. NVMe SSD – 1 TB, 64 GB DDR4 RAM
1 κόμβος υποβολής μετρητών Plasma αυξήθηκε.
Δημιουργήθηκαν 3 κόμβοι επικύρωσης Plasma Cash.
Ξεκίνησε μια δοκιμή για την προσθήκη συναλλαγών στο δίκτυο Plasma Cash.

Итого: 10 κόμβοι Plasma Cash σε ιδιωτικό δίκτυο.

Δοκιμή 1

Υπάρχει όριο 1 εκατομμυρίου συναλλαγών ανά μπλοκ. Επομένως, 1 εκατομμύριο συναλλαγές εμπίπτουν σε 2 μπλοκ (αφού το σύστημα καταφέρνει να λάβει μέρος των συναλλαγών και να υποβάλει ενώ αποστέλλονται).


Αρχική κατάσταση: τελευταίο μπλοκ #7; 1 εκατομμύριο συναλλαγές και μάρκες αποθηκεύονται στη βάση δεδομένων.

00:00 — έναρξη του σεναρίου δημιουργίας συναλλαγών
01:37 - Δημιουργήθηκαν 1 εκατομμύριο συναλλαγές και ξεκίνησε η αποστολή στον κόμβο
01:46 — Υποβολή κόμβου πήρε 240 συναλλαγές από το pool και σχηματίζει το μπλοκ #8. Βλέπουμε επίσης ότι 320 χιλιάδες συναλλαγές προστίθενται στο pool σε 10 δευτερόλεπτα
01:58 — το μπλοκ #8 υπογράφεται και αποστέλλεται για επικύρωση
02:03 — το μπλοκ #8 επικυρώνεται και η συνάρτηση «submitBlock» του έξυπνου συμβολαίου καλείται με τον κατακερματισμό και τον αριθμό μπλοκ Merkle
02:10 — το σενάριο επίδειξης ολοκληρώθηκε, το οποίο έστειλε 1 εκατομμύριο συναλλαγές σε 32 δευτερόλεπτα
02:33 - οι κόμβοι άρχισαν να λαμβάνουν πληροφορίες ότι το μπλοκ #8 προστέθηκε στη βασική αλυσίδα και άρχισαν να εκτελούν 240 χιλιάδες συναλλαγές
02:40 - 240 συναλλαγές αφαιρέθηκαν από το pool, οι οποίες βρίσκονται ήδη στο μπλοκ #8
02:56 — ο κόμβος υποβολής πήρε τις υπόλοιπες 760 συναλλαγές από το pool και άρχισε να υπολογίζει τον κατακερματισμό Merkle και να υπογράφει το μπλοκ #9
03:20 - όλοι οι κόμβοι περιέχουν 1 εκατομμύριο 240 συναλλαγές και μάρκες
03:35 — το μπλοκ #9 υπογράφεται και αποστέλλεται για επικύρωση σε άλλους κόμβους
03:41 - Παρουσιάστηκε σφάλμα δικτύου
04:40 — η αναμονή για την επικύρωση του μπλοκ #9 έχει λήξει
04:54 — ο κόμβος υποβολής πήρε τις υπόλοιπες 760 συναλλαγές από το pool και άρχισε να υπολογίζει τον κατακερματισμό Merkle και να υπογράφει το μπλοκ #9
05:32 — το μπλοκ #9 υπογράφεται και αποστέλλεται για επικύρωση σε άλλους κόμβους
05:53 — το μπλοκ #9 επικυρώνεται και αποστέλλεται στη βασική αλυσίδα
06:17 - οι κόμβοι άρχισαν να λαμβάνουν πληροφορίες ότι το μπλοκ #9 προστέθηκε στη βασική αλυσίδα και άρχισαν να εκτελούν 760 χιλιάδες συναλλαγές
06:47 — η ομάδα έχει εκκαθαριστεί από συναλλαγές που βρίσκονται στο μπλοκ #9
09:06 - όλοι οι κόμβοι περιέχουν 2 εκατομμύρια συναλλαγές και μάρκες

Δοκιμή 2

Υπάρχει όριο 350k ανά μπλοκ. Ως αποτέλεσμα, έχουμε 3 μπλοκ.


Αρχική κατάσταση: τελευταίο μπλοκ #9; 2 εκατομμύρια συναλλαγές και μάρκες αποθηκεύονται στη βάση δεδομένων

00:00 — το σενάριο δημιουργίας συναλλαγών έχει ήδη ξεκινήσει
00:44 - Δημιουργήθηκαν 1 εκατομμύριο συναλλαγές και ξεκίνησε η αποστολή στον κόμβο
00:56 — Υποβολή κόμβου πήρε 320 συναλλαγές από το pool και σχηματίζει το μπλοκ #10. Βλέπουμε επίσης ότι 320 χιλιάδες συναλλαγές προστίθενται στο pool σε 10 δευτερόλεπτα
01:12 — το μπλοκ #10 υπογράφεται και αποστέλλεται σε άλλους κόμβους για επικύρωση
01:18 — το σενάριο επίδειξης ολοκληρώθηκε, το οποίο έστειλε 1 εκατομμύριο συναλλαγές σε 34 δευτερόλεπτα
01:20 — το μπλοκ #10 επικυρώνεται και αποστέλλεται στη βασική αλυσίδα
01:51 - όλοι οι κόμβοι έλαβαν πληροφορίες από τη βασική αλυσίδα ότι το μπλοκ #10 προστέθηκε και αρχίζουν να εφαρμόζουν 320 χιλιάδες συναλλαγές
02:01 - η ομάδα έχει εκκαθαριστεί για 320 συναλλαγές που προστέθηκαν στο μπλοκ #10
02:15 — Υποβολή κόμβου πήρε 350 συναλλαγές από το pool και φόρμα μπλοκ #11
02:34 — το μπλοκ #11 υπογράφεται και αποστέλλεται σε άλλους κόμβους για επικύρωση
02:51 — το μπλοκ #11 επικυρώνεται και αποστέλλεται στη βασική αλυσίδα
02:55 — ο τελευταίος κόμβος ολοκλήρωσε τις συναλλαγές από το μπλοκ #10
10:59 — η συναλλαγή με την υποβολή του μπλοκ #9 πήρε πολύ χρόνο στη βασική αλυσίδα, αλλά ολοκληρώθηκε και όλοι οι κόμβοι έλαβαν πληροφορίες σχετικά με αυτό και άρχισαν να εκτελούν 350 χιλιάδες συναλλαγές
11:05 - η ομάδα έχει εκκαθαριστεί για 320 συναλλαγές που προστέθηκαν στο μπλοκ #11
12:10 - όλοι οι κόμβοι περιέχουν 1 εκατομμύριο 670 συναλλαγές και μάρκες
12:17 — Υποβολή κόμβου πήρε 330 συναλλαγές από το pool και σχηματίζει το μπλοκ #12
12:32 — το μπλοκ #12 υπογράφεται και αποστέλλεται σε άλλους κόμβους για επικύρωση
12:39 — το μπλοκ #12 επικυρώνεται και αποστέλλεται στη βασική αλυσίδα
13:44 - όλοι οι κόμβοι έλαβαν πληροφορίες από τη βασική αλυσίδα ότι το μπλοκ #12 προστέθηκε και αρχίζουν να εφαρμόζουν 330 χιλιάδες συναλλαγές
14:50 - όλοι οι κόμβοι περιέχουν 2 εκατομμύρια συναλλαγές και μάρκες

Δοκιμή 3

Στον πρώτο και δεύτερο διακομιστές, ένας κόμβος επικύρωσης αντικαταστάθηκε από έναν κόμβο υποβολής.


Αρχική κατάσταση: τελευταίο μπλοκ #84; 0 συναλλαγές και μάρκες που έχουν αποθηκευτεί στη βάση δεδομένων

00:00 — Έχουν κυκλοφορήσει 3 σενάρια που δημιουργούν και στέλνουν 1 εκατομμύριο συναλλαγές το καθένα
01:38 — Δημιουργήθηκαν 1 εκατομμύριο συναλλαγές και ξεκίνησε η αποστολή για υποβολή στον κόμβο #3
01:50 — υποβάλετε τον κόμβο #3 πήρε 330 συναλλαγές από το pool και σχηματίζει το μπλοκ #85 (f21). Βλέπουμε επίσης ότι 350 συναλλαγές προστίθενται στο pool σε 10 δευτερόλεπτα
01:53 — Δημιουργήθηκαν 1 εκατομμύριο συναλλαγές και ξεκίνησε η αποστολή για υποβολή στον κόμβο #1
01:50 — υποβάλετε τον κόμβο #3 πήρε 330 συναλλαγές από το pool και σχηματίζει το μπλοκ #85 (f21). Βλέπουμε επίσης ότι 350 συναλλαγές προστίθενται στο pool σε 10 δευτερόλεπτα
02:01 — υποβολή κόμβου #1 πήρε 250 συναλλαγές από το pool και σχηματίζει το μπλοκ #85 (65e)
02:06 — το μπλοκ #85 (f21) υπογράφεται και αποστέλλεται σε άλλους κόμβους για επικύρωση
02:08 — το σενάριο επίδειξης του διακομιστή #3, που έστειλε 1 εκατομμύριο συναλλαγές σε 30 δευτερόλεπτα, τελείωσε
02:14 — το μπλοκ #85 (f21) επικυρώνεται και αποστέλλεται στη βασική αλυσίδα
02:19 — το μπλοκ #85 (65e) υπογράφεται και αποστέλλεται σε άλλους κόμβους για επικύρωση
02:22 — Δημιουργήθηκαν 1 εκατομμύριο συναλλαγές και ξεκίνησε η αποστολή για υποβολή στον κόμβο #2
02:27 — το μπλοκ #85 (65e) επικυρώθηκε και εστάλη στη ριζική αλυσίδα
02:29 — υποβάλετε τον κόμβο #2 πήρε 111855 συναλλαγές από το pool και σχηματίζει το μπλοκ #85 (256).
02:36 — το μπλοκ #85 (256) υπογράφεται και αποστέλλεται σε άλλους κόμβους για επικύρωση
02:36 — το σενάριο επίδειξης του διακομιστή #1, που έστειλε 1 εκατομμύριο συναλλαγές σε 42.5 δευτερόλεπτα, τελείωσε
02:38 — το μπλοκ #85 (256) επικυρώνεται και αποστέλλεται στη βασική αλυσίδα
03:08 — το σενάριο του διακομιστή #2 ολοκληρώθηκε, το οποίο έστειλε 1 εκατομμύριο συναλλαγές σε 47 δευτερόλεπτα
03:38 - όλοι οι κόμβοι έλαβαν πληροφορίες από την ριζική αλυσίδα που προστέθηκαν τα μπλοκ #85 (f21), #86(65e), #87(256) και άρχισαν να εφαρμόζουν 330k, 250k, 111855 συναλλαγές
03:49 - το pool εκκαθαρίστηκε σε 330k, 250k, 111855 συναλλαγές που προστέθηκαν στα μπλοκ #85 (f21), #86(65e), #87(256)
03:59 — υποβάλετε τον κόμβο #1 πήρε 888145 συναλλαγές από το pool και το μπλοκ φόρμας #88 (214), υποβάλετε τον κόμβο #2 πήρε 750 συναλλαγές από το pool και σχηματίστε το μπλοκ #88 (50a), υποβάλετε τον κόμβο #3 πήρε 670 συναλλαγές από η πισίνα και το μπλοκ φόρμας #88 (d3b)
04:44 — το μπλοκ #88 (d3b) υπογράφεται και αποστέλλεται σε άλλους κόμβους για επικύρωση
04:58 — το μπλοκ #88 (214) υπογράφεται και αποστέλλεται σε άλλους κόμβους για επικύρωση
05:11 — το μπλοκ #88 (50a) υπογράφεται και αποστέλλεται σε άλλους κόμβους για επικύρωση
05:11 — το μπλοκ #85 (d3b) επικυρώνεται και αποστέλλεται στη βασική αλυσίδα
05:36 — το μπλοκ #85 (214) επικυρώνεται και αποστέλλεται στη βασική αλυσίδα
05:43 - όλοι οι κόμβοι έλαβαν πληροφορίες από τη βασική αλυσίδα που μπλοκ #88 (d3b), #89(214) έχουν προστεθεί και αρχίζουν να εφαρμόζουν 670k, 750k συναλλαγές
06:50 — λόγω αποτυχίας επικοινωνίας, το μπλοκ #85 (50a) δεν επικυρώθηκε
06:55 — υποβάλετε τον κόμβο #2 πήρε 888145 συναλλαγές από το pool και σχηματίζει το μπλοκ #90 (50a)
08:14 — το μπλοκ #90 (50a) υπογράφεται και αποστέλλεται σε άλλους κόμβους για επικύρωση
09:04 — το μπλοκ #90 (50a) επικυρώνεται και αποστέλλεται στη βασική αλυσίδα
11:23 - όλοι οι κόμβοι έλαβαν πληροφορίες από την ριζική αλυσίδα ότι το μπλοκ #90 (50a) προστέθηκε και αρχίζουν να εφαρμόζουν 888145 συναλλαγές. Ταυτόχρονα, ο διακομιστής #3 έχει ήδη εφαρμόσει συναλλαγές από τα μπλοκ #88 (d3b), #89(214)
12:11 - όλες οι πισίνες είναι άδειες
13:41 — όλοι οι κόμβοι του διακομιστή #3 περιέχουν 3 εκατομμύρια συναλλαγές και διακριτικά
14:35 — όλοι οι κόμβοι του διακομιστή #1 περιέχουν 3 εκατομμύρια συναλλαγές και διακριτικά
19:24 — όλοι οι κόμβοι του διακομιστή #2 περιέχουν 3 εκατομμύρια συναλλαγές και διακριτικά

Εμπόδια

Κατά την ανάπτυξη του Plasma Cash, αντιμετωπίσαμε τα ακόλουθα προβλήματα, τα οποία σταδιακά επιλύσαμε και επιλύουμε:

1. Σύγκρουση στην αλληλεπίδραση διαφόρων λειτουργιών του συστήματος. Για παράδειγμα, η λειτουργία της προσθήκης συναλλαγών στο pool απέκλεισε το έργο της υποβολής και επικύρωσης μπλοκ και αντίστροφα, γεγονός που οδήγησε σε πτώση της ταχύτητας.

2. Δεν ήταν αμέσως σαφές πώς να στείλετε έναν τεράστιο αριθμό συναλλαγών ελαχιστοποιώντας παράλληλα το κόστος μεταφοράς δεδομένων.

3. Δεν ήταν σαφές πώς και πού να αποθηκεύονται τα δεδομένα προκειμένου να επιτευχθούν υψηλά αποτελέσματα.

4. Δεν ήταν σαφές πώς να οργανωθεί ένα δίκτυο μεταξύ κόμβων, καθώς το μέγεθος ενός μπλοκ με 1 εκατομμύριο συναλλαγές καταλαμβάνει περίπου 100 MB.

5. Η εργασία σε λειτουργία μονού νήματος διακόπτει τη σύνδεση μεταξύ των κόμβων όταν πραγματοποιούνται μεγάλοι υπολογισμοί (για παράδειγμα, η κατασκευή ενός δέντρου Merkle και ο υπολογισμός του κατακερματισμού του).

Πώς το αντιμετωπίσαμε όλο αυτό;

Η πρώτη έκδοση του κόμβου Plasma Cash ήταν ένα είδος συνδυασμού που μπορούσε να κάνει τα πάντα ταυτόχρονα: να δέχεται συναλλαγές, να υποβάλλει και να επικυρώνει μπλοκ και να παρέχει ένα API για πρόσβαση σε δεδομένα. Δεδομένου ότι το NodeJS είναι εγγενώς μονονήμα, η βαριά συνάρτηση υπολογισμού δέντρου Merkle απέκλεισε τη συνάρτηση προσθήκης συναλλαγής. Είδαμε δύο επιλογές για την επίλυση αυτού του προβλήματος:

1. Εκκινήστε πολλές διαδικασίες NodeJS, καθεμία από τις οποίες εκτελεί συγκεκριμένες λειτουργίες.

2. Χρησιμοποιήστε worker_threads και μετακινήστε την εκτέλεση μέρους του κώδικα σε νήματα.

Ως αποτέλεσμα, χρησιμοποιήσαμε και τις δύο επιλογές ταυτόχρονα: χωρίσαμε λογικά έναν κόμβο σε 3 μέρη που μπορούν να λειτουργήσουν ξεχωριστά, αλλά ταυτόχρονα και συγχρονισμένα

1. Κόμβος υποβολής, ο οποίος δέχεται συναλλαγές στο pool και δημιουργεί μπλοκ.

2. Ένας κόμβος επικύρωσης που ελέγχει την εγκυρότητα των κόμβων.

3. Κόμβος API - παρέχει ένα API για πρόσβαση σε δεδομένα.

Σε αυτήν την περίπτωση, μπορείτε να συνδεθείτε σε κάθε κόμβο μέσω μιας υποδοχής unix χρησιμοποιώντας cli.

Μεταφέραμε βαριές λειτουργίες, όπως τον υπολογισμό του δέντρου Merkle, σε ξεχωριστό νήμα.

Έτσι, έχουμε επιτύχει κανονική λειτουργία όλων των λειτουργιών Plasma Cash ταυτόχρονα και χωρίς βλάβες.

Μόλις το σύστημα λειτούργησε, αρχίσαμε να δοκιμάζουμε την ταχύτητα και, δυστυχώς, λάβαμε μη ικανοποιητικά αποτελέσματα: 5 συναλλαγές ανά δευτερόλεπτο και έως 000 συναλλαγές ανά μπλοκ. Έπρεπε να καταλάβω τι εφαρμόστηκε λανθασμένα.

Αρχικά, αρχίσαμε να δοκιμάζουμε τον μηχανισμό επικοινωνίας με το Plasma Cash για να μάθουμε την κορυφαία ικανότητα του συστήματος. Γράψαμε νωρίτερα ότι ο κόμβος Plasma Cash παρέχει μια διεπαφή υποδοχής unix. Αρχικά βασιζόταν σε κείμενο. Τα αντικείμενα json στάλθηκαν χρησιμοποιώντας "JSON.parse()" και "JSON.stringify()".

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

Μετρήσαμε την ταχύτητα μεταφοράς τέτοιων αντικειμένων και βρήκαμε ~ 130k ανά δευτερόλεπτο. Προσπαθήσαμε να αντικαταστήσουμε τις τυπικές λειτουργίες για εργασία με json, αλλά η απόδοση δεν βελτιώθηκε. Ο κινητήρας V8 πρέπει να είναι καλά βελτιστοποιημένος για αυτές τις λειτουργίες.

Δουλέψαμε με συναλλαγές, διακριτικά και μπλοκ μέσω κλάσεων. Κατά τη δημιουργία τέτοιων κλάσεων, η απόδοση μειώθηκε κατά 2 φορές, πράγμα που δείχνει ότι το OOP δεν είναι κατάλληλο για εμάς. Έπρεπε να ξαναγράψω τα πάντα σε μια καθαρά λειτουργική προσέγγιση.

Καταγραφή στη βάση δεδομένων

Αρχικά, το Redis επιλέχθηκε για αποθήκευση δεδομένων ως μία από τις πιο παραγωγικές λύσεις που ικανοποιεί τις απαιτήσεις μας: αποθήκευση κλειδιού-τιμής, εργασία με πίνακες κατακερματισμού, σύνολα. Κυκλοφορήσαμε το redis-benchmark και λάβαμε ~80 λειτουργίες ανά δευτερόλεπτο σε 1 λειτουργία διοχέτευσης.

Για υψηλές επιδόσεις, ρυθμίσαμε το Redis πιο λεπτά:

  • Έχει δημιουργηθεί μια σύνδεση υποδοχής unix.
  • Απενεργοποιήσαμε την αποθήκευση της κατάστασης στο δίσκο (για αξιοπιστία, μπορείτε να ρυθμίσετε ένα αντίγραφο και να αποθηκεύσετε στο δίσκο σε ξεχωριστό Redis).

Στο Redis, ένα pool είναι ένας πίνακας κατακερματισμού, επειδή πρέπει να είμαστε σε θέση να ανακτήσουμε όλες τις συναλλαγές σε ένα ερώτημα και να διαγράψουμε τις συναλλαγές μία προς μία. Προσπαθήσαμε να χρησιμοποιήσουμε μια κανονική λίστα, αλλά είναι πιο αργή κατά την εκφόρτωση ολόκληρης της λίστας.

Κατά τη χρήση του τυπικού NodeJS, οι βιβλιοθήκες Redis πέτυχαν απόδοση 18 χιλιάδων συναλλαγών ανά δευτερόλεπτο. Η ταχύτητα έπεσε 9 φορές.

Δεδομένου ότι το σημείο αναφοράς μας έδειξε ότι οι δυνατότητες ήταν σαφώς 5 φορές μεγαλύτερες, αρχίσαμε να βελτιστοποιούμε. Αλλάξαμε τη βιβλιοθήκη σε ioredis και πήραμε απόδοση 25k ανά δευτερόλεπτο. Προσθέσαμε τις συναλλαγές μία προς μία χρησιμοποιώντας την εντολή «hset». Έτσι, δημιουργήσαμε πολλά ερωτήματα στο Redis. Προέκυψε η ιδέα να συνδυαστούν οι συναλλαγές σε παρτίδες και να αποσταλούν με μία εντολή «hmset». Το αποτέλεσμα είναι 32 χιλιάδες ανά δευτερόλεπτο.

Για διάφορους λόγους, τους οποίους θα περιγράψουμε παρακάτω, εργαζόμαστε με δεδομένα χρησιμοποιώντας "Buffer" και, όπως αποδεικνύεται, αν τα μετατρέψετε σε κείμενο ("buffer.toString('hex')") πριν γράψετε, μπορείτε να λάβετε επιπλέον εκτέλεση. Έτσι, η ταχύτητα αυξήθηκε στα 35k ανά δευτερόλεπτο. Προς το παρόν, αποφασίσαμε να αναστείλουμε την περαιτέρω βελτιστοποίηση.

Έπρεπε να μεταβούμε σε ένα δυαδικό πρωτόκολλο επειδή:

1. Το σύστημα συχνά υπολογίζει κατακερματισμούς, υπογραφές κ.λπ., και γι' αυτό χρειάζεται δεδομένα στο `Buffer.

2. Όταν αποστέλλονται μεταξύ υπηρεσιών, τα δυαδικά δεδομένα ζυγίζουν λιγότερο από το κείμενο. Για παράδειγμα, όταν στέλνετε ένα μπλοκ με 1 εκατομμύριο συναλλαγές, τα δεδομένα στο κείμενο μπορεί να καταλαμβάνουν περισσότερα από 300 megabyte.

3. Ο διαρκής μετασχηματισμός δεδομένων επηρεάζει την απόδοση.

Ως εκ τούτου, λάβαμε ως βάση το δικό μας δυαδικό πρωτόκολλο για την αποθήκευση και τη μετάδοση δεδομένων, που αναπτύχθηκε με βάση την υπέροχη βιβλιοθήκη «δυαδικών δεδομένων».

Ως αποτέλεσμα, έχουμε τις ακόλουθες δομές δεδομένων:

-Συναλλαγή

  ```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,
  }
  ```

— Σύμβολο

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

-ΟΙΚΟΔΟΜΙΚΟ ΤΕΤΡΑΓΩΝΟ

  ```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,
  }
  ```

Με τις συνήθεις εντολές «BD.encode(block, Protocol).slice();» και «BD.decode(buffer, Protocol)» μετατρέπουμε τα δεδομένα σε «Buffer» για αποθήκευση στο Redis ή προώθηση σε άλλο κόμβο και ανάκτηση του επιστροφή δεδομένων.

Έχουμε επίσης 2 δυαδικά πρωτόκολλα για τη μεταφορά δεδομένων μεταξύ υπηρεσιών:

— Πρωτόκολλο αλληλεπίδρασης με τον κόμβο πλάσματος μέσω υποδοχής 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)
  }
  ```

όπου:

  • `Τύπου` — η ενέργεια που πρέπει να εκτελεστεί, για παράδειγμα, 1 — sendTransaction, 2 — getTransaction.
  • «ωφέλιμο φορτίο». — δεδομένα που πρέπει να διαβιβαστούν στην κατάλληλη συνάρτηση·
  • `MessageId` — Αναγνωριστικό μηνύματος ώστε να είναι δυνατή η αναγνώριση της απάντησης.

— Πρωτόκολλο αλληλεπίδρασης μεταξύ κόμβων

  ```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)
  }
  ```

όπου:

  • «κωδικός». — κωδικός μηνύματος, για παράδειγμα 6 — PREPARE_NEW_BLOCK, 7 — BLOCK_VALID, 8 — BLOCK_COMMIT;
  • «Πρωτόκολλο έκδοσης». — Έκδοση πρωτοκόλλου, αφού οι κόμβοι με διαφορετικές εκδόσεις μπορούν να δημιουργηθούν στο δίκτυο και μπορούν να λειτουργήσουν διαφορετικά.
  • «ακολουθία». — αναγνωριστικό μηνύματος·
  • «countChunk». и «αριθμός κομματιού». απαραίτητο για τον διαχωρισμό μεγάλων μηνυμάτων.
  • «μήκος». и «ωφέλιμο φορτίο». μήκος και τα ίδια τα δεδομένα.

Δεδομένου ότι πληκτρολογήσαμε εκ των προτέρων τα δεδομένα, το τελικό σύστημα είναι πολύ πιο γρήγορο από τη βιβλιοθήκη `rlp` του Ethereum. Δυστυχώς, δεν καταφέραμε ακόμη να το αρνηθούμε, αφού είναι απαραίτητο να οριστικοποιηθεί το έξυπνο συμβόλαιο, το οποίο σκοπεύουμε να κάνουμε στο μέλλον.

Αν καταφέρναμε να φτάσουμε την ταχύτητα 35 000 συναλλαγές ανά δευτερόλεπτο, πρέπει επίσης να τις επεξεργαστούμε στον βέλτιστο χρόνο. Δεδομένου ότι ο κατά προσέγγιση χρόνος σχηματισμού μπλοκ διαρκεί 30 δευτερόλεπτα, πρέπει να συμπεριλάβουμε στο μπλοκ 1 000 000 συναλλαγές, που σημαίνει αποστολή περισσότερων 100 MB δεδομένων.

Αρχικά, χρησιμοποιήσαμε τη βιβλιοθήκη `ethereumjs-devp2p` για την επικοινωνία μεταξύ κόμβων, αλλά δεν μπορούσε να χειριστεί τόσα πολλά δεδομένα. Ως αποτέλεσμα, χρησιμοποιήσαμε τη βιβλιοθήκη «ws» και διαμορφώσαμε την αποστολή δυαδικών δεδομένων μέσω websocket. Φυσικά, αντιμετωπίσαμε προβλήματα και κατά την αποστολή μεγάλων πακέτων δεδομένων, αλλά τα χωρίσαμε σε κομμάτια και τώρα αυτά τα προβλήματα έχουν φύγει.

Επίσης σχηματίζοντας ένα δέντρο Merkle και υπολογισμό του κατακερματισμού 1 000 000 συναλλαγές απαιτεί περίπου 10 δευτερόλεπτα συνεχούς υπολογισμού. Σε αυτό το διάστημα, η σύνδεση με όλους τους κόμβους καταφέρνει να σπάσει. Αποφασίστηκε να μεταφερθεί αυτός ο υπολογισμός σε ξεχωριστό νήμα.

Συμπεράσματα:

Στην πραγματικότητα, τα ευρήματά μας δεν είναι νέα, αλλά για κάποιο λόγο πολλοί ειδικοί τα ξεχνούν όταν αναπτύσσονται.

  • Η χρήση του λειτουργικού προγραμματισμού αντί του αντικειμενοστρεφούς προγραμματισμού βελτιώνει την παραγωγικότητα.
  • Ο μονόλιθος είναι χειρότερος από μια αρχιτεκτονική υπηρεσίας για ένα παραγωγικό σύστημα NodeJS.
  • Η χρήση του «worker_threads» για μεγάλους υπολογισμούς βελτιώνει την ανταπόκριση του συστήματος, ειδικά όταν αντιμετωπίζουμε λειτουργίες i/o.
  • Η υποδοχή unix είναι πιο σταθερή και πιο γρήγορη από τα αιτήματα http.
  • Εάν χρειάζεται να μεταφέρετε γρήγορα μεγάλα δεδομένα μέσω του δικτύου, είναι καλύτερο να χρησιμοποιήσετε υποδοχές ιστού και να στείλετε δυαδικά δεδομένα, χωρισμένα σε κομμάτια, τα οποία μπορούν να προωθηθούν εάν δεν φτάσουν και στη συνέχεια να συνδυαστούν σε ένα μήνυμα.

Σας προσκαλούμε να επισκεφθείτε GitHub έργο: https://github.com/opporty-com/Plasma-Cash/tree/new-version

Το άρθρο συντάχθηκε από τον Αλεξάντερ Νασιβάν, ανώτερος προγραμματιστής Η Clever Solution Inc.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο