Skip to main content
Change page

Απλή σειριοποίηση

Page last update: 9 Απριλίου 2025

Απλή σειριοποίηση (SSZ) είναι η μέθοδος σειριοποίησης που χρησιμοποιείται στο Beacon Chain. Αντικαθιστά τη σειριοποίηση RLP που χρησιμοποιείται στο επίπεδο εκτέλεσης παντού στο επίπεδο συναίνεσης εκτός από το πρωτόκολλο ανακάλυψης ομότιμου. Για να μάθετε περισσότερα σχετικά με τη σειριοποίηση RLP, ανατρέξτε στην ενότητα Πρόθεμα αναδρομικού μήκους (RLP). Το SSZ έχει σχεδιαστεί για να είναι ντετερμινιστικό και επίσης να είναι αποτελεσματικό στο merkleize. Το SSZ μπορεί να θεωρηθεί ότι έχει δύο στοιχεία: ένα σχήμα σειριοποίησης και ένα σχήμα Merkleization που έχει σχεδιαστεί για να λειτουργεί αποτελεσματικά με τη σειριακή δομή δεδομένων.

Πώς λειτουργεί το SSZ;

Σειριοποίηση

Το SSZ είναι ένα σχήμα σειριοποίησης που δεν αυτοπεριγράφεται - μάλλον βασίζεται σε ένα σχήμα που πρέπει να είναι γνωστό εκ των προτέρων. Ο στόχος της σειριοποίησης SSZ είναι να αναπαραστούν αντικείμενα αυθαίρετης πολυπλοκότητας ως συμβολοσειρές bytes. Αυτή είναι μια πολύ απλή διαδικασία για «βασικούς τύπους». Το στοιχείο απλώς μετατρέπεται σε δεκαεξαδικά bytes. Οι βασικοί τύποι περιλαμβάνουν:

  • unsigned integers
  • Booleans

Για τους σύνθετους τύπους, η σειριοποίηση είναι πιο περίπλοκη επειδή ο σύνθετος τύπος περιέχει πολλαπλά στοιχεία που μπορεί να έχουν διαφορετικούς τύπους ή διαφορετικά μεγέθη ή και τα δύο. Όπου όλα αυτά τα αντικείμενα έχουν σταθερά μήκη (δηλαδή το μέγεθος των στοιχείων θα είναι πάντα σταθερό ανεξάρτητα από τις πραγματικές τους τιμές) η σειριοποίηση είναι απλώς μια μετατροπή κάθε στοιχείου στον σύνθετο τύπο ταξινομημένο σε bytestrings little-endian. Αυτά τα bytestrings ενώνονται μεταξύ τους. Το σειριοποιημένο αντικείμενο έχει την αναπαράσταση bytelist των στοιχείων σταθερού μήκους στην ίδια σειρά με την οποία εμφανίζονται στο αποσυσκευασμένο αντικείμενο.

Για τύπους με μεταβλητά μήκη, τα πραγματικά δεδομένα αντικαθίστανται από μια τιμή "offset" στη θέση αυτού του στοιχείου στο σειριοποιημένο αντικείμενο. Τα πραγματικά δεδομένα προστίθενται σε έναν σωρό στο τέλος του σειριοποιημένου αντικειμένου. Η τιμή offset είναι ο δείκτης για την αρχή των πραγματικών δεδομένων στο σωρό, λειτουργώντας ως δείκτης στα σχετικά bytes.

Το παρακάτω παράδειγμα απεικονίζει πώς λειτουργεί η μετατόπιση για έναν περιέκτη με στοιχεία σταθερού και μεταβλητού μήκους:

1
2 struct Dummy {
3
4 number1: u64,
5 number2: u64,
6 vector: Vec<u8>,
7 number3: u64
8 }
9
10 dummy = Dummy{
11
12 number1: 37,
13 number2: 55,
14 vector: vec![1,2,3,4],
15 number3: 22,
16 }
17
18 serialized = ssz.serialize(dummy)
19
Εμφάνιση όλων

Το serialized θα είχε την ακόλουθη δομή (εδώ συμπληρώνεται μόνο σε 4 bit, στην πραγματικότητα συμπληρώνεται σε 32 bit και διατηρείται η αναπαράσταση int για λόγους σαφήνειας):

1[37, 0, 0, 0, 55, 0, 0, 0, 16, 0, 0, 0, 22, 0, 0, 0, 1, 2, 3, 4]
2------------ ----------- ----------- ----------- ----------
3 | | | | |
4 number1 number2 offset for number 3 value for
5 vector vector
6

Χωρισμένο σε γραμμές για λόγους σαφήνειας:

1[
2 37, 0, 0, 0, # little-endian encoding of `number1`.
3 55, 0, 0, 0, # little-endian encoding of `number2`.
4 16, 0, 0, 0, # The "offset" that indicates where the value of `vector` starts (little-endian 16).
5 22, 0, 0, 0, # little-endian encoding of `number3`.
6 1, 2, 3, 4, # The actual values in `vector`.
7]

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

1[
2 10100101000000000000000000000000 # little-endian encoding of `number1`
3 10110111000000000000000000000000 # little-endian encoding of `number2`.
4 10010000000000000000000000000000 # The "offset" that indicates where the value of `vector` starts (little-endian 16).
5 10010110000000000000000000000000 # little-endian encoding of `number3`.
6 10000001100000101000001110000100 # The actual value of the `bytes` field.
7]

Έτσι, οι πραγματικές τιμές για τους τύπους μεταβλητού μήκους αποθηκεύονται σε ένα σωρό στο τέλος του σειριοποιημένου αντικειμένου με τις μετατοπίσεις τους αποθηκευμένες στις σωστές θέσεις τους στην ταξινομημένη λίστα των πεδίων.

Υπάρχουν επίσης ορισμένες ειδικές περιπτώσεις που απαιτούν ειδική διαχείριση, όπως ο τύπος BitList που απαιτεί να προστεθεί ένα όριο μήκους κατά τη σειριοποίηση και να αφαιρεθεί κατά την αποσειριοποίηση. Πλήρεις λεπτομέρειες είναι διαθέσιμες στις προδιαγραφές SSZopens in a new tab.

Αποσειριοποίηση

Για να αποσειριοποιήσετε αυτό το αντικείμενο απαιτείται το σχήμα. Το σχήμα ορίζει την ακριβή διάταξη των σειριοποιημένων δεδομένων, έτσι ώστε κάθε συγκεκριμένο στοιχείο να μπορεί να αποσειριοποιηθεί από ένα blob byte σε κάποιο ουσιαστικό αντικείμενο με τα στοιχεία να έχουν τον σωστό τύπο, τιμή, μέγεθος και θέση. Είναι το σχήμα που λέει στον αποσειριοποιητή ποιες τιμές είναι πραγματικές τιμές και ποιες είναι μετατοπίσεις. Όλα τα ονόματα πεδίων εξαφανίζονται όταν ένα αντικείμενο σειριοποιείται, αλλά αποκαθίστανται κατά την αποσειριοποίηση σύμφωνα με το σχήμα.

Δείτε το ssz.devopens in a new tab για μια διαδραστική εξήγηση σχετικά με αυτό.

Merkleization

Αυτό το σειριοποιημένο αντικείμενο SSZ μπορεί στη συνέχεια να μετατραπεί σε Merkle, δηλαδή να μετατραπεί σε μια αναπαράσταση Merkle-δέντρου των ίδιων δεδομένων. Πρώτον, προσδιορίζεται ο αριθμός των chunk των 32 byte στο σειριοποιημένο αντικείμενο. Αυτά είναι τα «φύλλα» του δέντρου. Ο συνολικός αριθμός των φύλλων πρέπει να είναι δύναμη του 2, έτσι ώστε ο τελικός συνδυασμός των φύλλων να παράγει τελικά μια μόνο ρίζα δέντρου κατακερματισμού. Εάν αυτό δεν συμβαίνει φυσικά, προστίθενται επιπλέον φύλλα που περιέχουν 32 byte μηδενικών. Διαγραμματικά:

1 hash tree root
2 / \
3 / \
4 / \
5 / \
6 hash of leaves hash of leaves
7 1 and 2 3 and 4
8 / \ / \
9 / \ / \
10 / \ / \
11 leaf1 leaf2 leaf3 leaf4
Εμφάνιση όλων

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

Αντί να αναφερόμαστε σε αυτά τα στοιχεία δέντρου ως φύλλο X, κόμβος X κ.λπ., μπορούμε να τους δώσουμε γενικευμένους δείκτες, ξεκινώντας από τη ρίζα = 1 και μετρώντας από αριστερά προς τα δεξιά σε κάθε επίπεδο. Αυτός είναι ο γενικευμένος δείκτης που εξηγήθηκε παραπάνω. Κάθε στοιχείο στη σειριασμένη λίστα έχει έναν γενικευμένο δείκτη ίσο με 2 ** depth + idx όπου idx είναι η θέση του μηδενικού δείκτη στο σειριασμένο αντικείμενο και το βάθος είναι ο αριθμός των επιπέδων στο δέντρο Merkle, το οποίο μπορεί να προσδιοριστεί ως ο δυαδικός λογάριθμος του αριθμού των στοιχείων (φύλλα).

Γενικευμένοι δείκτες

Ένας γενικευμένος δείκτης είναι ένας ακέραιος που αντιπροσωπεύει έναν κόμβο σε ένα δυαδικό δέντρο Merkle όπου κάθε κόμβος έχει έναν γενικευμένο δείκτη 2 ** depth + δείκτης στη σειρά.

1 1 --depth = 0 2**0 + 0 = 1
2 2 3 --depth = 1 2**1 + 0 = 2, 2**1+1 = 3
3 4 5 6 7 --depth = 2 2**2 + 0 = 4, 2**2 + 1 = 5...
4

Αυτή η αναπαράσταση αποδίδει έναν δείκτη κόμβου για κάθε κομμάτι δεδομένων στο δέντρο Merkle.

Πολλαπλές πιστοποιήσεις

Η παροχή της λίστας των γενικευμένων δεικτών που αντιπροσωπεύουν ένα συγκεκριμένο στοιχείο μας επιτρέπει να το επαληθεύσουμε έναντι της ρίζας του δέντρου hash. Αυτή η ρίζα είναι η αποδεκτή έκδοση της πραγματικότητας από εμάς. Οποιαδήποτε δεδομένα μας παρέχονται μπορούν να επαληθευτούν έναντι αυτής της πραγματικότητας εισάγοντάς τα στη σωστή θέση στο δέντρο Merkle (καθορισμένη από τον γενικευμένο δείκτη του) και παρατηρώντας ότι η ρίζα παραμένει σταθερή. Υπάρχουν συναρτήσεις στις προδιαγραφές εδώopens in a new tab που δείχνουν πώς να υπολογίσουμε το ελάχιστο σύνολο κόμβων που απαιτούνται για την επαλήθευση του περιεχομένου ενός συγκεκριμένου συνόλου γενικευμένων δεικτών.

Για παράδειγμα, για να επαληθεύσουμε δεδομένα στον δείκτη 9 στο παρακάτω δέντρο, χρειαζόμαστε το hash των δεδομένων στους δείκτες 8, 9, 5, 3, 1. Το hash του (8,9) πρέπει να ισούται με το hash (4), το οποίο κάνει hash με το 5 για να παράγει το 2, το οποίο κάνει hash με το 3 για να παράγει τη ρίζα του δέντρου 1. Εάν παρέχθηκαν εσφαλμένα δεδομένα για το 9, η ρίζα θα άλλαζε - θα το ανιχνεύαμε αυτό και θα αποτύχαμε να επαληθεύσουμε το κλάδο.

1* = data required to generate proof
2
3 1*
4 2 3*
5 4 5* 6 7
68* 9* 10 11 12 13 14 15
7

Περισσότερες πληροφορίες

Ήταν χρήσιμο αυτό το άρθρο;