./research / verifiable-audit-layer
Ένα επαληθεύσιμο στρώμα ελέγχου για την ΤΝ.
Ένα στρώμα ελέγχου που αποκαλύπτει την παραποίηση για την εξαγωγή συμπερασμάτων μοντέλων και την εκτέλεση πρακτόρων: κανονική σειριοποίηση, κατακερματισμός με διαχωρισμό πεδίων, υπογραφές DSSE, ένα Merkle DAG ανά εκτέλεση και ένα δια-εκτελεσιακό Merkle Mountain Range που αποδεικνύει ότι καμία εκτέλεση δεν διαγράφηκε. Κάθε πρωτογενές στοιχείο επιλέχθηκε από μια απευθείας σύγκριση επιδόσεων έναντι των πραγματικών εναλλακτικών, με τα ακατέργαστα δεδομένα δεσμευμένα. Αυτή η σημείωση είναι ο σχεδιασμός και τα πειράματα πίσω του.
Μοντέλο απειλής
Υποθέτουμε έναν αντίπαλο που ελέγχει την αποθήκευση και μπορεί να ξαναγράψει εγγραφές εκ των υστέρων, συμπεριλαμβανομένου του χειριστή που τις παρήγαγε. Ο στόχος είναι κάθε επεξεργασία μιας σφραγισμένης εκτέλεσης να είναι ανιχνεύσιμη, η εγγραφή να παραμένει επαληθεύσιμη για μια περίοδο διατήρησης μετρημένη σε έτη, και η επαλήθευση να μην εκθέτει καμία ιδιωτική είσοδο. Δύο ιδιότητες κρατούνται διακριτές παντού: η ακεραιότητα μιας μόνο εκτέλεσης και η πληρότητα του συνόλου των εκτελέσεων. Μια απόδειξη ανά εκτέλεση δίνει την πρώτη και δεν λέει τίποτα για τη δεύτερη, οπότε και οι δύο σχεδιάζονται ξεχωριστά.
Κάθε επιλογή παρακάτω έγινε με τον ίδιο τρόπο. Δεν ισχυριζόμαστε ότι ένα πρωτογενές στοιχείο είναι το καλύτερο· το συγκρίνουμε με τις αξιόπιστες εναλλακτικές στην ίδια μηχανή και δεσμεύουμε τα ακατέργαστα δεδομένα, ώστε κάθε αριθμός εδώ να αναπαράγεται με μία εντολή. Οι χρόνοι είναι ο ταχύτερος από πολλές εκτελέσεις αφού απορριφθεί η προθέρμανση, με τον μέσο όρο και την τυπική απόκλιση να διατηρούνται ώστε μια ασταθής μέτρηση να επισημαίνεται αντί να κρύβεται. Οι αριθμοί προέρχονται από έναν μόνο κεντρικό υπολογιστή AMD64 σε CPython 3.14 και έχουν νόημα ως λόγοι, όχι ως απόλυτες τιμές.
Πειράματα και εκβάσεις
Κάθε ορόσημο ήταν μια απευθείας δοκιμή έναντι των πραγματικών εναλλακτικών, όχι μία μόνο μέτρηση. Η επισκόπηση είναι παρακάτω· τα αναλυτικά αποτελέσματα του καθενός βρίσκονται στις ενότητες που ακολουθούν.
| Ορόσημο | Δοκιμάστηκε έναντι | Έκβαση |
|---|---|---|
| Κανονική μορφή | αφελές JSON, CBOR | JCS, πανομοιότυπο ανά byte, ~3x κόστος |
| Κατακερματισμός | SHA-256, BLAKE3, keccak-256 | keccak εγγενώς 17x, ίδια byte |
| Εγγενής πυρήνας | backends pip vs Rust | σφράγιση 6x, ρίζα πανομοιότυπη ανά byte |
| Υπογραφή | ECDSA, BLS, ML-DSA | Ed25519, 64 B, ντετερμινιστική |
| Μετακβαντική | ML-DSA 44 / 65 / 87 | σταδιακά, επαλήθευση ισοδύναμη, 38x μέγεθος |
| Ίχνος ανά εκτέλεση | 8 επιθέσεις πλαστογράφησης, επιβάρυνση | όλες συνελήφθησαν, ~121 us / συμβάν |
| Δια-εκτελεσιακό αρχείο | απλή ανακατασκευή Merkle | MMR, επίπεδη προσάρτηση, 483x |
Κανονική σειριοποίηση
Μια κάψουλα πρέπει να κατακερματίζεται στα ίδια byte στον πυρήνα μας Python, στον πελάτη μας TypeScript και στο δικό του εργαλείο ενός ελεγκτή, αλλιώς μια έγκυρη εγγραφή αποτυγχάνει στην επαλήθευση. Χρησιμοποιούμε το RFC 8785 (JSON Canonicalization Scheme), που καθηλώνει τη μορφοποίηση των αριθμών, τη σειρά των κλειδιών κατά μονάδα κώδικα UTF-16 και την απόρριψη μη πεπερασμένων τιμών. Το δοκιμάσαμε έναντι JSON με αφελώς ταξινομημένα κλειδιά και έναντι CBOR.
| Προσέγγιση | Python vs Node | Κόστος |
|---|---|---|
| Αφελώς ταξινομημένο JSON | αποκλίνει σε Unicode και αριθμούς | βάση |
| RFC 8785 JCS | πανομοιότυπο ανά byte | περίπου 3x στη σειριοποίηση |
| CBOR | δυαδικό, χρειάζεται αποκωδικοποιητή για να διαβαστεί | συμπαγές |
Το αφελές JSON αποκλίνει μεταξύ περιβαλλόντων εκτέλεσης στη διάταξη Unicode και τη μορφοποίηση αριθμών, οπότε σπάει σιωπηλά την επαλήθευση μεταξύ γλωσσών. Το JCS είναι πανομοιότυπο ανά byte και στα δύο, σε περίπου 3x τον χρόνο σειριοποίησης, που πληρώνεται μία φορά ανά κατακερματισμό και είναι αμελητέος δίπλα στον κατακερματισμό και το δικτυακό έργο γύρω. Κρατάμε ακριβώς μία υλοποίησή του. Όταν το εγγενές μας άρθρωμα έφερε τον δικό του σειριοποιητή JSON, τον αφαιρέσαμε, γιατί ένας δεύτερος κανονικοποιητής που συμφωνεί σε συνήθεις εισόδους και αποκλίνει στις οριακές περιπτώσεις που μπορεί να επιλέξει ένας αντίπαλος είναι χειρότερος από κανέναν.
Κατακερματισμός και ο εγγενής πυρήνας
Τα φύλλα κατακερματίζονται με BLAKE3 και οι κόμβοι Merkle με keccak-256, αμφότερα με διαχωρισμό πεδίων RFC 6962 (διακριτά προθέματα για φύλλα και εσωτερικούς κόμβους) ώστε να κλείσει η κλάση δεύτερης προεικόνας όπου ένας εσωτερικός κόμβος παρουσιάζεται ως φύλλο. Το keccak-256 δεν είναι το SHA3-256· τα δύο μοιράζονται μια μετάθεση αλλά διαφέρουν σε ένα byte γεμίσματος, και ο επαληθευτής on-chain μιλά keccak, οπότε μια σιωπηλή αντικατάσταση θα έκανε κάθε ρίζα να αποτυγχάνει on-chain. Το στρώμα κατακερματισμού επομένως υπολογίζει τον ζητούμενο αλγόριθμο ή ρίχνει σφάλμα, ποτέ έναν σωσία, και καταγράφει τον αλγόριθμο σε κάθε κάψουλα ώστε ένας επαληθευτής να αναπαράγει την ακριβή συνάρτηση αντί να τη συμπεραίνει από ό,τι είναι εγκατεστημένο τοπικά.
| Πρωτογενές, είσοδος 64 B | Ρυθμαπόδοση | Σημείωση |
|---|---|---|
| SHA-256 (stdlib) | 1,50M ops/s | βάση, πάντα παρόν |
| BLAKE3 (pip) | 1,44M ops/s | ισοδύναμο με SHA-256 |
| keccak-256 (pip) | 99k ops/s | 15x πιο αργό, το σημείο συμφόρησης |
| keccak-256 (πυρήνας Rust) | 1,69M ops/s | 17x πιο γρήγορο από pip |
Το keccak δεν έχει υλοποίηση στην τυπική βιβλιοθήκη, κάτι που έκανε το εγγενές άρθρωμα να αξίζει να χτιστεί. Το χάσμα είναι μεγαλύτερο σε εισόδους μεγέθους συμβάντος γιατί το κόστος του pycryptodome εκεί είναι η δέσμευση αντικειμένων ανά κλήση και όχι ο κατακερματισμός, ακριβώς το μέγεθος που ένα ίχνος ελέγχου κατακερματίζει περισσότερο· από άκρη σε άκρη, ο εγγενής πυρήνας ανεβάζει τη σφράγιση μιας εκτέλεσης 1000 συμβάντων από 78 σε 491 σφραγίσεις ανά δευτερόλεπτο, περίπου 6x. Κρίσιμο: η ενεργοποίηση του επιταχυντή δεν μπορεί να αλλάξει το αποτέλεσμα: η σφράγιση της ίδιας εκτέλεσης με και χωρίς το crate δίνει μια ρίζα πανομοιότυπη ανά bit, μια σουίτα συμμόρφωσης ελέγχει και τα δύο μονοπάτια έναντι των διανυσμάτων που δημοσίευσαν οι συγγραφείς του αλγορίθμου, και το CI χτίζει έναν μόνο τροχό σταθερού ABI (abi3) και επαληθεύει αυτό το μοναδικό τεχνούργημα σε κάθε Python από 3.10 έως 3.14. Η εγκατάστασή του αλλάζει την ταχύτητα και τίποτε άλλο.
Υπογραφές για μια εγγραφή μιας δεκαετίας
Μια υπογραφή σε μια εγγραφή ελέγχου πρέπει να κρατά όσο η εγγραφή μπορεί να αμφισβητηθεί, και οι υποχρεώσεις καταγραφής της ΤΝ τείνουν πλέον προς τη δεκαετία. Αν το σχήμα σπάσει ενώ μια εγγραφή είναι ακόμη στο παράθυρο διατήρησής της, ένας αντίπαλος μπορεί να την πλαστογραφήσει και να την αντεχρονολογήσει, οπότε ολόκληρο το αρχείο αποτυγχάνει μονομιάς. Συγκρίναμε το Ed25519 έναντι των ECDSA, BLS και της μετακβαντικής οικογένειας ML-DSA του NIST, σε ρυθμαπόδοση και μεγέθη.
| Σχήμα | Επαλήθευση (ops/s) | Μέγεθος υπογραφής |
|---|---|---|
| ECDSA P-256 | 16 887 | ~72 B |
| ML-DSA-44 (μετακβαντικό) | 10 725 | 2420 B |
| Ed25519 | 10 375 | 64 B |
| BLS12-381 | μόνο υλοποίηση αναφοράς | 96 B, συναθροίζει |
Δύο αποτελέσματα ανέτρεψαν παραδοχές που κουβαλούσαμε, και κρατάμε και τα δύο στο αρχείο. Το Ed25519 δεν είναι το ταχύτερο στην επαλήθευση: το ECDSA P-256 μέτρησε περίπου 1,6x πιο γρήγορα, πάνω σε βελτιστοποιημένη assembly βιβλιοθήκης. Το Ed25519 παραμένει προεπιλογή στο μέγεθος υπογραφής και στον ντετερμινισμό (RFC 8032), που αφαιρεί τη βλάβη επαναχρησιμοποίησης nonce του ECDSA, η οποία διαρρέει ένα ιδιωτικό κλειδί ενώ οι υπογραφές ακόμη επαληθεύονται. Και οι μετακβαντικές υπογραφές δεν είναι αργές στην επαλήθευση: το ML-DSA-44 προσπέρασε οριακά το Ed25519, με το κόστος του στην υπογραφή (περίπου 13x πιο αργή) και στο μέγεθος (περίπου 38x τα byte). Επειδή ένα ίχνος υπογράφεται μία φορά και επαληθεύεται για χρόνια, η ακριβή πλευρά είναι αυτή που κάνουμε λιγότερο. Το BLS απορρίφθηκε σε μια απαίτηση, όχι σε έναν αριθμό: η συνάθροιση συμπτύσσει πολλές υπογραφές σε 96 byte, αλλά ένα σύνολο επαληθεύεται όλα-ή-τίποτα και χρειάζεται κάθε κλειδί και μήνυμα, το οποίο είναι ασύμβατο με την αποκάλυψη ενός κλάδου μιας απόφασης χωρίς να φανερωθεί το υπόλοιπο.
Η αναπλαισίωση που μετρά: οι υπογραφές δεν είναι εμπιστευτικές, οπότε το «συγκόμισε τώρα, αποκρυπτογράφησε αργότερα» δεν ισχύει, και η πραγματική απειλή είναι η αναδρομική πλαστογράφηση εντός του παραθύρου διατήρησης. Η αγκύρωση μιας ρίζας σε ένα δημόσιο καθολικό αποδεικνύει πότε υπήρξε μια εγγραφή ανεξάρτητα από το σχήμα υπογραφής, οπότε ένα σπασμένο κλειδί κοστίζει τη δυνατότητα να αποδειχθεί ποιος σφράγισε μια εγγραφή αλλά όχι πότε ή τι. Η αγκύρωση είναι επομένως στο κρίσιμο μονοπάτι και το ML-DSA είναι κλιμακωμένο πίσω της, με τον φάκελο DSSE να κρατιέται ευέλικτος ως προς τον αλγόριθμο και τον αλγόριθμο να καταγράφεται ανά κάψουλα.
Το ίχνος ελέγχου ανά εκτέλεση
Κάθε εκτέλεση είναι μια ακολουθία συμβάντων μόνο-προσάρτησης και αλυσοδεμένη με κατακερματισμό (κλήσεις LLM, κλήσεις εργαλείων, ανακτήσεις, δημιουργίες υποπρακτόρων, ανθρώπινες εγκρίσεις, αποφάσεις κιγκλιδώματος), με μια ρίζα Merkle πάνω από όλα και μια υπογραφή DSSE πάνω από τη ρίζα. Αποθηκεύονται μόνο κατακερματισμοί εισόδων και εξόδων, ποτέ τα ωφέλιμα φορτία, οπότε το ίχνος διαφυλάσσει το απόρρητο εξ ορισμού. Ο επαληθευτής δεν επιστρέφει απλώς έγκυρο ή άκυρο· κατονομάζει την εγγύηση που έσπασε.
| Λειτουργία (εκτέλεση 102 συμβάντων) | Κόστος | Κλιμάκωση |
|---|---|---|
| Καταγραφή ενός συμβάντος | ~121 us | επίπεδο, κάτω από 0,01% ενός βήματος |
| Σφράγιση, χτίσιμο της ρίζας | 0,15 ms | γραμμικό στα συμβάντα |
| Επαλήθευση, επανυπολογισμός όλων των φύλλων | 9,2 ms | γραμμικό, περίπου 60x η σφράγιση |
Η καταγραφή είναι λιγότερο από ένα εκατοστό του τοις εκατό ενός τυπικού βήματος πράκτορα, το οποίο κυριαρχείται από τη διαδρομή μετ' επιστροφής προς το μοντέλο, οπότε η επιβάρυνση δεν είναι λόγος για δειγματοληψία και το ίχνος παραμένει πλήρες αντί στατιστικό. Η επαλήθευση είναι πολύ βαρύτερη από τη σφράγιση γιατί επανυπολογίζει κάθε φύλλο από το περιεχόμενο ενώ η σφράγιση χτίζει το δέντρο μόνο πάνω σε κατακερματισμούς ήδη παρόντες. Αυτή η ασυμμετρία είναι σωστή για ένα ίχνος ελέγχου, όπου η καταγραφή είναι σταθερή και η επαλήθευση σπάνια, και σημειώνει τη μαζική επανεπαλήθευση ως το σημείο όπου ο εγγενής πυρήνας και ο παραλληλισμός αποδίδουν στη συνέχεια. Για να γίνουν συγκεκριμένες οι εγγυήσεις, ένα demo επιτίθεται σε μια σφραγισμένη εκτέλεση με οκτώ τρόπους, καθένας απορριπτόμενος με συγκεκριμένη αιτία:
| Επίθεση | Απορρίφθηκε ως |
|---|---|
| Επανεγγραφή μιας εξόδου μοντέλου | αναντιστοιχία κατακερματισμού φύλλου |
| Διαγραφή ή εισαγωγή συμβάντος | αναντιστοιχία πλήθους συμβάντων |
| Αναδιάταξη συμβάντων | ρήξη αλυσίδας |
| Περικοπή της εκτέλεσης | αναντιστοιχία κεφαλής αλυσίδας |
| Υποβάθμιση του αλγορίθμου κατακερματισμού | αναντιστοιχία κατακερματισμού φύλλου |
| Παρουσίαση υπό λάθος κλειδί | η υπογραφή δεν επαληθεύεται |
Το δια-εκτελεσιακό αρχείο διαφάνειας
Μια έγκυρη κάψουλα ανά εκτέλεση δεν μπορεί να αποδείξει ότι το σύνολο των εκτελέσεων είναι πλήρες. Ένας χειριστής που πετά μια άβολη εκτέλεση κρατά μια συλλογή ατομικά τέλειων καψουλών και μια ιστορία με μια τρύπα. Το κλείνουμε αυτό με ένα μοναδικό αρχείο μόνο-προσάρτησης ριζών καψουλών που υποστηρίζει αποδείξεις συμπερίληψης (μια εκτέλεση είναι στο αρχείο) και αποδείξεις συνέπειας (το αρχείο σε προγενέστερο μέγεθος είναι ακριβές πρόθεμα του αρχείου τώρα). Η συνέπεια πιάνει τη λογοκρισία: ένα ανακατασκευασμένο αρχείο με μια εκτέλεση αφαιρεμένη είναι εσωτερικά καλοσχηματισμένο και κάθε επιζώσα κάψουλα ακόμη επαληθεύεται, αλλά αποτυγχάνει σε έλεγχο συνέπειας έναντι της ρίζας που δημοσιεύτηκε πριν, και ο ίδιος έλεγχος απορρίπτει μια αντεχρονολογημένη εισαγωγή. Αυτό λειτουργεί μόνο έναντι μιας ρίζας δεσμευμένης πριν την παραποίηση, οπότε αυτή η ρίζα πρέπει να ζει κάπου που ο χειριστής δεν μπορεί να αναθεωρήσει, όπως μια δημόσια άγκυρα.
Χρησιμοποιούμε ένα Merkle Mountain Range αντί για ένα απλό δέντρο Merkle γιατί μια προσάρτηση μόνο προσθέτει κόμβους και ποτέ δεν ξαναγράφει κάποιον, πράγμα που αφήνει το αρχείο να ζει σε αποθήκευση εγγραφής-μία-φορά ή αντικειμένων και κρατά τις παλιές αποδείξεις συμπερίληψης έγκυρες καθώς το αρχείο μεγαλώνει. Αυτή η ιδιότητα είναι σχεδόν βέλτιστη, όχι απλώς βολική: το ePrint 2025/234 αποδεικνύει ότι κάθε συνοπτική δέσμευση μόνο-προσάρτησης πρέπει να επάγει υπεργραμμικό αριθμό ενημερώσεων μαρτύρων, και μια κοντινή παραλλαγή MMR ουσιαστικά φτάνει το φράγμα.
| Μέγεθος αρχείου | Χτίσιμο MMR | Απλή ανακατασκευή |
|---|---|---|
| 500 | 1,3 ms | 158 ms (120x) |
| 1000 | 2,6 ms | 618 ms (237x) |
| 2000 | 5,2 ms | 2491 ms (483x) |
Η προσάρτηση παραμένει επίπεδη καθώς το αρχείο μεγαλώνει (περίπου 390k προσαρτήσεις ανά δευτερόλεπτο τόσο στις 100 όσο και στις 10 000 εγγραφές) ενώ η ανακατασκευή ενός απλού δέντρου πέφτει από 8176 σε 84 ρίζες ανά δευτερόλεπτο στο ίδιο εύρος, η υπογραφή τετραγωνικού-έναντι-γραμμοαριθμικού. Η αποθήκευση σταθεροποιείται στους 2,00 αποθηκευμένους κατακερματισμούς ανά εγγραφή και οι αποδείξεις συμπερίληψης μεγαλώνουν μόνο από 7 σε 17 βήματα ανάμεσα σε 100 και 100 000 εγγραφές. Πού χάνουμε: οι αποδείξεις συνέπειάς μας είναι O(log^2 n), κουβαλώντας ένα μονοπάτι ανά κορυφή του παλιού αρχείου αντί του εφικτού O(log n), λίγα kilobyte στις 100 000 εγγραφές και μια σκόπιμη ανταλλαγή για μια κατασκευή που ένας κριτής μπορεί να ελέγξει διαβάζοντας.
Όρια που δεν διασχίζουμε
Ο κατακερματισμός δίνει ένδειξη παραποίησης, όχι απαραβίαστο. Ένας αντίπαλος που ανακατασκευάζει μια εκτέλεση και επανυπολογίζει τη ρίζα της παράγει μια αυτοσυνεπή εγγραφή, γιατί μια ρίζα Merkle αποδεικνύει ότι τα συμβάντα ταιριάζουν με τη ρίζα, όχι ότι η ρίζα είναι αυτή που δημοσιεύτηκε. Η υπογραφή το πιάνει αυτό, και η δημόσια άγκυρα αποδεικνύει τον χρόνο. Το αρχείο διαφάνειας στηρίζεται στο μεγάλο στην ίδια παραδοχή: ανιχνεύει μια διαγραμμένη εκτέλεση μόνο σχετικά με μια ρίζα δεσμευμένη πριν τη διαγραφή. Δηλώνουμε αυτά τα όρια στο προϊόν και στα demo του, και ένα τεστ βεβαιώνει ότι το demo πλαστογράφησης συνεχίζει να δείχνει την επίθεση που δεν σταματάμε, ώστε αυτή η έντιμη περίπτωση να μην μπορεί να εξαφανιστεί σιωπηλά.
Αναπαραγωγιμότητα
Κάθε αριθμός εδώ προέρχεται από ένα δεσμευμένο μετροπρόγραμμα που ξανατρέχει με μία εντολή, δίπλα στα τεστ, τις σουίτες συμμόρφωσης μεταξύ γλωσσών και μεταξύ υλοποιήσεων, και τη μήτρα CI που ασκεί Python 3.10 έως 3.14 με και χωρίς τον εγγενή πυρήνα. Η μεθοδολογία, το ακατέργαστο JSON και οι σημειώσεις απόφασης (περιλαμβανομένων των διαστάσεων όπου κάθε επιλογή χάνει) είναι στο αποθετήριο, γιατί ένα στρώμα που κάνει την ΤΝ ελέγξιμη πρέπει το ίδιο να είναι ελέγξιμο.