Πίνακας ελέγχου QA που παρακολουθεί την κατάσταση του έξυπνου συμβολαίου  Η προηγούμενη ανάρτηση περιέγραψε μια ολοκληρωμένη υλοποίηση: ένα ελάχιστο συμβόλαιο token, εκτός αλυσίδας ανακατασκευή κατάστασηςΠίνακας ελέγχου QA που παρακολουθεί την κατάσταση του έξυπνου συμβολαίου  Η προηγούμενη ανάρτηση περιέγραψε μια ολοκληρωμένη υλοποίηση: ένα ελάχιστο συμβόλαιο token, εκτός αλυσίδας ανακατασκευή κατάστασης

Κατάσταση Λογαριασμού Ethereum: Διαδικασία Διασφάλισης Ποιότητας για ένα Ελάχιστο Token

2026/04/09 13:48
Ανάγνωση 9 λεπτών
Για feedback ή ανησυχίες σας σχετικά με αυτό το περιεχόμενο, επικοινωνήστε μαζί μας στη διεύθυνση [email protected]
Πίνακας ελέγχου QA για παρακολούθηση κατάστασης έξυπνου συμβολαίου 

Η προηγούμενη ανάρτηση περιέγραψε μια ολοκληρωμένη υλοποίηση: ένα ελάχιστο συμβόλαιο token, ανακατασκευή κατάστασης εκτός αλυσίδας και ένα React frontend — από το `mint()` μέχρι το MetaMask. Αυτή η ανάρτηση συνεχίζει από εκεί που σταμάτησε: πώς κάνετε QA σε κάτι τέτοιο;

Δεν είμαι μηχανικός blockchain (ακόμα), αλλά τα μοτίβα QA λειτουργούν καλά σε όλους τους τομείς, και το δανεισμό αυτού που ήδη λειτουργεί αλλού είναι ο ταχύτερος τρόπος μάθησης.

Το συμβόλαιο κάνει μόνο τρία πράγματα: `mint`, `transfer` και `burn`, αλλά ακόμη και αυτό είναι αρκετό για να εξασκηθεί η πλήρης αλυσίδα εργαλείων QA: στατική ανάλυση, δοκιμές μετάλλαξης, προφίλ gas, επίσημη επαλήθευση.

Ο κώδικας βρίσκεται στο `egpivo/ethereum-account-state`.

Πυραμίδα QA Blockchain: από τη στατική ανάλυση στη βάση έως την επίσημη επαλήθευση στην κορυφή 

Με τι ξεκινήσαμε

Πριν προσθέσουμε κάτι νέο, το έργο είχε ήδη:

  • 21 δοκιμές μονάδας Foundry που καλύπτουν κάθε μετάβαση κατάστασης (επιτυχία, επαναφορά σε παράνομη είσοδο, εκπομπή συμβάντος)
  • 3 δοκιμές αναλλοίωτου μέσω ενός `TokenHandler` που εκτελεί τυχαίες ακολουθίες `mint`/`transfer`/`burn` σε 10 δράστες (128k κλήσεις η κάθε)
  • Δοκιμές Fuzz που ελέγχουν `sum(balances) == totalSupply` για τυχαία ποσά
  • Δοκιμές τομέα TypeScript (Vitest) που αντικατοπτρίζουν τη μηχανή κατάστασης on-chain
  • CI: compile, test, lint (Prettiersolhint)

Όλες οι δοκιμές πέρασαν. Η κάλυψη φαινόταν καλή. Γιατί λοιπόν να ενδιαφερθούμε για περισσότερα;

Επειδή το "όλες οι δοκιμές περνάνε" δεν σημαίνει "όλα τα σφάλματα εντοπίζονται". Η κάλυψη γραμμής 100% μπορεί ακόμα να χάσει ένα πραγματικό σφάλμα αν καμία διαβεβαίωση δεν ελέγχει το σωστό πράγμα.

Φάση 1: Στατική ανάλυση έξυπνου συμβολαίου και κάλυψη

Slither

Το Slither(Trail of Bits) εντοπίζει ζητήματα που είναι αόρατα στις δοκιμές: reentrancy, μη ελεγμένες τιμές επιστροφής, αναντιστοιχίες διεπαφής.

./scripts/run-qa.sh slither

Αποτέλεσμα: 1 Μεσαίο εύρημα: `erc20-interface`: το `transfer()` δεν επιστρέφει `bool`.

Αυτό ήταν αναμενόμενο. Το συμβόλαιο σκόπιμα δεν είναι πλήρες ERC20: είναι μια εκπαιδευτική μηχανή κατάστασης. Αλλά το εύρημα δεν είναι ακαδημαϊκό:

Εάν κάποιος αργότερα εισαγάγει αυτό το token σε ένα πρωτόκολλο που αναμένει ERC20, η αναντιστοιχία διεπαφής θα αποτύχει σιωπηλά. Το Slither το επισημαίνει τώρα, ώστε η απόφαση να είναι συνειδητή.

Κάλυψη

./scripts/run-qa.sh coverageΑποτέλεσμα κάλυψης.

Μία συνάρτηση χωρίς κάλυψη: `BalanceLib.gt()`. Θα επιστρέψουμε σε αυτό.

Έξοδος forge coverage: 24 δοκιμές πέρασαν, πίνακας κάλυψης Token.sol 

Στιγμιότυπα Gas

./scripts/run-qa.sh gas

Βασικό κόστος gas για τις τρεις λειτουργίες:

Gas σε όρους λειτουργιών

Σε επόμενες εκτελέσεις, το `forge snapshot — diff` συγκρίνει με τη βάση. Μια υποχώρηση gas 20% στο `transfer()` είναι πραγματικό κόστος για κάθε χρήστη — το να το εντοπίσεις πριν από τη συγχώνευση είναι φθηνό.

Φάση 2: Δοκιμές μετάλλαξης και επίσημη επαλήθευση

Δοκιμές μετάλλαξης (Gambit)

Εδώ τα πράγματα έγιναν ενδιαφέροντα. Το Gambit(Certora) δημιουργεί μεταλλάγματα: αντίγραφα του `Token.sol` με μικρά σκόπιμα σφάλματα (`+=` σε `-=`, `>=` σε `>`, συνθήκες αντεστραμμένες). Η διαδικασία εκτελεί την πλήρη σουίτα δοκιμών κατά κάθε μετάλλαγμα. Εάν ένα μετάλλαγμα επιβιώσει (όλες οι δοκιμές εξακολουθούν να περνάνε), αυτό είναι ένα συγκεκριμένο κενό δοκιμής.

./scripts/run-qa.sh mutation

Αποτέλεσμα: 97.0% σκορ μετάλλαξης — 32 εξοντώθηκαν, 1 επέζησε από 33 μεταλλάγματα.

Το αρχείο εξόδου του Gambit δείχνει κάθε μετάλλαγμα και τι άλλαξε. Μερικά παραδείγματα:

Δημιουργήθηκε μετάλλαγμα #7: BinaryOpMutation — Token.sol:168
totalSupply = totalSupply.add(amountBalance) → totalSupply = totalSupply.sub(amountBalance)
KILLED από test_Mint_Success
Δημιουργήθηκε μετάλλαγμα #19: RelationalOpMutation — Token.sol:196
if (!fromBalance.gte(amountBalance)) → if (fromBalance.gte(amountBalance))
KILLED από test_Transfer_Success
Δημιουργήθηκε μετάλλαγμα #28: SwapArgumentsMutation — Token.sol:81
return Balance.unwrap(a) > Balance.unwrap(b) → return Balance.unwrap(b) > Balance.unwrap(a)
SURVIVED ← καμία δοκιμή δεν το έπιασεΔοκιμές μετάλλαξης Gambit: 32 εξοντώθηκαν, 1 επέζησε, σκορ μετάλλαξης 97.0%

Το μετάλλαγμα που επέζησε άλλαξε το `a > b` σε `b > a` στο `BalanceLib.gt()`. Καμία δοκιμή δεν το έπιασε επειδή το `gt()` είναι νεκρός κώδικας. Δεν καλείται ποτέ πουθενά στο `Token.sol`.

Η κάλυψη επισήμανε 91.67% συναρτήσεις αλλά δεν μπορούσε να εξηγήσει το κενό. Οι δοκιμές μετάλλαξης το έκαναν: το `gt()` είναι νεκρός κώδικας, τίποτα δεν το καλεί και κανείς δεν θα παρατηρούσε αν ήταν λάθος.

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

Η συνάρτηση δεν προοριζόταν να καλείται, αλλά κανείς δεν δοκίμασε αυτή την υπόθεση. Το `gt()` μας είναι αβλαβές συγκριτικά, αλλά το μοτίβο είναι το ίδιο: κώδικας που υπάρχει αλλά δεν εκτελείται ποτέ είναι κώδικας που κανείς δεν παρακολουθεί.

Επίσημη επαλήθευση (Halmos)

Το Halmos(a16z) συλλογίζεται για όλες τις πιθανές εισόδους συμβολικά. Ενώ οι δοκιμές fuzz δειγματίζουν τυχαίες τιμές και ελπίζουν να χτυπήσουν ακραίες περιπτώσεις, το Halmos αποδεικνύει ιδιότητες εξαντλητικά.

./scripts/run-qa.sh halmos

Αποτέλεσμα: 9/9 συμβολικές δοκιμές περνούν — όλες οι ιδιότητες αποδεικνύονται για όλες τις εισόδους.

Επαληθευμένες ιδιότητες:

Επαληθευμένες ιδιότητες

Μια πρακτική σημείωση: Το Halmos 0.3.3 δεν υποστηρίζει `vm.expectRevert()`, οπότε δεν μπορούσα να γράψω δοκιμές επαναφοράς με τον κανονικό τρόπο του Foundry. Η λύση είναι ένα μοτίβο try/catch — εάν η κλήση επιτύχει όταν θα έπρεπε να επαναφερθεί, το `assert(false)` αποτυγχάνει την απόδειξη:

function check_mint_reverts_on_zero_address(uint256 amount) public {
vm.assume(amount > 0);
try token.mint(address(0), amount) {
assert(false); // δεν πρέπει να φτάσει εδώ
} catch {
// αναμενόμενη επαναφορά - το Halmos αποδεικνύει ότι αυτή η διαδρομή λαμβάνεται πάντα
}
}

Όχι το πιο όμορφο, αλλά λειτουργεί — το Halmos εξακολουθεί να αποδεικνύει την ιδιότητα για όλες τις εισόδους. Αυτό είναι το είδος του πράγματος που ανακαλύπτεις μόνο εκτελώντας πραγματικά το εργαλείο.

Για πλαίσιο σχετικά με το γιατί έχει σημασία η επίσημη επαλήθευση:

Η ευπάθεια ήταν στον κώδικα, αναθεωρήσιμη από οποιονδήποτε, αλλά κανένα εργαλείο ή δοκιμή δεν το έπιασε πριν από την ανάπτυξη. Οι συμβολικοί αποδεικτές όπως το Halmos υπάρχουν ακριβώς για να κλείσουν αυτό το κενό — δεν δειγματίζουν· εξαντλούν τον χώρο εισόδου.

Έξοδος Halmos: 9 δοκιμές πέρασαν, 0 απέτυχαν, αποτελέσματα συμβολικών δοκιμών 

Το αρχείο δοκιμής είναι `contracts/test/Token.halmos.t.sol`.

Φάση 3: Δοκιμές ιδιοτήτων διασταυρούμενου επιπέδου

Η αρχιτεκτονική της πρώτης ανάρτησης έχει ένα επίπεδο τομέα TypeScript που αντικατοπτρίζει τη μηχανή κατάστασης on-chain. Αυτή η φάση δοκιμάζει αν τα δύο πράγματι συμφωνούν.

Δοκιμές βασισμένες σε ιδιότητες με fast-check

Πρόσθεσα δοκιμές ιδιοτήτων fast-check για το επίπεδο τομέα TypeScript, αντικατοπτρίζοντας αυτό που κάνει το fuzzer του Foundry για τη Solidity:

npm test - tests/unit/property.test.ts

Αποτέλεσμα: 9/9 δοκιμές ιδιοτήτων περνούν μετά τη διόρθωση ενός πραγματικού σφάλματος.

Δοκιμασμένες ιδιότητες:

  • `Balance`: μεταθετικότητα, προσεταιριστικότητα, ταυτότητα, αντίστροφο, συνέπεια σύγκρισης
  • `Token`: αναλλοίωτο `sum(balances) == totalSupply` υπό τυχαίες ακολουθίες λειτουργιών (200 εκτελέσεις, 50 λειτουργίες η κάθε)
  • `Token`: `totalSupply` μη αρνητικό μετά από τυχαίες ακολουθίες
  • `mint` επιτυγχάνει πάντα για έγκυρες εισόδους
  • `transfer` διατηρεί `totalSupply`

Το σφάλμα που βρήκε το fast-check

Το fast-check βρήκε ένα πραγματικό σφάλμα συνέπειας διασταυρούμενου επιπέδου στο `Token.ts` `transfer()`. Το συρρικνωμένο αντιπαράδειγμα ήταν αμέσως σαφές:

Η ιδιότητα απέτυχε μετά από 3 δοκιμές
Συρρίκνωση 2 φορά/φορές
Αντιπαράδειγμα: transfer(from=0xaaa…, to=0xaaa…, amount=1n)
→ from == to (αυτομεταφορά)
→ verifyInvariant() επέστρεψε false

Η αυτομεταφορά (`from == to`) έσπασε το αναλλοίωτο `sum(balances) == totalSupply`. Το `toBalance` διαβάστηκε πριν ενημερωθεί το `fromBalance`, οπότε όταν `from == to`, η παρωχημένη τιμή αντικατέστησε την αφαίρεση:

// Πριν (με σφάλμα)
const fromBalance = this.getBalance(from);
const toBalance = this.getBalance(to); // ← παρωχημένο όταν from == to
this.accounts.set(from.getValue(), fromBalance.subtract(amount));
this.accounts.set(to.getValue(), toBalance.add(amount)); // ← αντικαθιστά την αφαίρεση

Διόρθωση: διάβασε το `toBalance` μετά τη γραφή του `fromBalance`, ταιριάζοντας τη σημασιολογία αποθήκευσης της Solidity:

// Μετά (διορθωμένο)
const fromBalance = this.getBalance(from);
this.accounts.set(from.getValue(), fromBalance.subtract(amount));
const toBalance = this.getBalance(to); // ← τώρα διαβάζει την ενημερωμένη τιμή
this.accounts.set(to.getValue(), toBalance.add(amount));

Το συμβόλαιο Solidity δεν επηρεάστηκε: επαναδιαβάζει την αποθήκευση μετά από κάθε εγγραφή. Αλλά ο καθρέφτης TypeScript είχε μια λεπτή εξάρτηση σειράς που καμία υπάρχουσα δοκιμή μονάδας δεν κάλυπτε.

Αναντιστοιχίες διασταυρούμενων επιπέδων σε μεγαλύτερη κλίμακα έχουν υπάρξει καταστροφικές.

Το σφάλμα αυτομεταφοράς μας δεν θα είχε χάσει χρήματα σε κανέναν, αλλά ο τρόπος αποτυχίας είναι δομικά ο ίδιος: δύο επίπεδα που υποτίθεται ότι συμφωνούν, δεν συμφωνούν.

Παγίδες που συναντήθηκαν στην πορεία

Η εκτέλεση εργαλείων QA σε ένα υπάρχον έργο δεν είναι ποτέ απλά "εγκατάσταση και εκτέλεση". Μερικά πράγματα έσπασαν πριν λειτουργήσουν:

  • 0% κάλυψη επειδή το `foundry.toml` δεν είχε διαδρομή δοκιμής: Η πρώτη εκτέλεση `forge coverage` επέστρεψε 0% σε όλα. Αποδείχθηκε ότι το `foundry.toml` δεν όριζε `test = "contracts/test"` ή `script = "contracts/script"`, οπότε το Forge δεν ανακάλυπτε καμία δοκιμή. Η εντολή κάλυψης πέτυχε σιωπηλά — απλά δεν είχε τίποτα να καλύψει. Αυτή ήταν η πιο παραπλανητική αποτυχία: μια πράσινη εκτέλεση χωρίς χρήσιμη έξοδο.
  • Εισαγωγή `InvariantTest` έχει εξαφανιστεί στο forge-std v1.14.0: Το `Invariant.t.sol` εισήγαγε το `InvariantTest` από το `forge-std`, το οποίο αφαιρέθηκε σε μια πρόσφατη έκδοση. Η μεταγλώττιση απέτυχε με ένα αδιαφανές σφάλμα "σύμβολο δεν βρέθηκε". Η λύση ήταν να αφαιρεθεί η εισαγωγή — το `Test` μόνο του είναι αρκετό για τις δοκιμές αναλλοίωτου του Foundry τώρα.
  • `uint256(token.totalSupply())` vs `Balance.unwrap()`: Οι δοκιμές χρησιμοποιούσαν ρητή μετατροπή για να εξαγάγουν το υποκείμενο `uint256` από τον ορισμένο από τον χρήστη τύπο `Balance`. Μεταγλωττίστηκε, αλλά είναι λάθος ιδίωμα — το `Balance.unwrap(token.totalSupply())` είναι αυτό για το οποίο έχει σχεδιαστεί το σύστημα UDVT. Εφαρμόστηκε σε `Token.t.sol`, `Invariant.t.sol` και `DeploySepolia.s.sol`.

Σχεδιασμός διοχέτευσης

Όλα εκτελούνται μέσω δύο σεναρίων:

  • scripts/setup-qa-tools.sh`: εγκαθιστά Slither, Halmos, Gambit (ισχυρό)
  • `scripts/run-qa.sh`: εκτελεί ελέγχους, αποθηκεύει αποτελέσματα με χρονική σήμανση στο `qa-results/`

./scripts/run-qa.sh slither gas # μόνο στατική ανάλυση + gas
./scripts/run-qa.sh mutation # μόνο δοκιμές μετάλλαξης
./scripts/run-qa.sh all # όλα

Όχι κάθε έλεγχος είναι γρήγορος. Το Slither και η κάλυψη εκτελούνται σε κάθε commit. Οι δοκιμές μετάλλαξης και το Halmos είναι πιο αργά — πιο κατάλληλα για εβδομαδιαίες ή εκτελέσεις πριν την κυκλοφορία.

Σύνοψη

Αλυσίδα εργαλείων QA Blockchain: τι πιάνει κάθε επίπεδο — από τη στατική ανάλυση έως τις δοκιμές ιδιοτήτων διασταυρούμενου επιπέδου 

Πέντε επίπεδα QA, κάθε ένα πιάνει μια διαφορετική κατηγορία προβλήματος.

Επεξήγηση επιπέδου

Το Gambit και το fast-check έδωσαν τα πιο εφαρμόσιμα αποτελέσματα σε αυτό τον γύρο.

Διοχέτευση CI

Οι έλεγχοι QA είναι τώρα συνδεδεμένοι στο GitHub Actions ως διοχέτευση έξι σταδίων:

Διοχέτευση CI: Build & Lint διακλαδώνεται σε στάδια Test, Coverage, Gas, Slither και Audit 

Διοχέτευση GitHub Actions: Build & Lint περιορίζει όλα τα downstream στάδια.

Επεξήγηση σταδίου

Αναφορές

  • Πηγή Ethereum Account State: [github.com/egpivo/ethereum-account-state](https://github.com/egpivo/ethereum-account-state)
  • Προηγούμενη ανάρτηση: Ethereum Account State
  • Slither: github.com/crytic/slither
  • Gambit: github.com/Certora/gambit
  • Halmos: github.com/a16z/halmos
  • fast-check: github.com/dubzzz/fast-check
  • Foundry: getfoundry.sh

Σημειώσεις

  • Αυτή η ανάρτηση είναι προσαρμοσμένη από την αρχική ανάρτησή μου στο blog.

Το Ethereum Account State: QA Pipeline for a Minimal Token δημοσιεύτηκε αρχικά στο Coinmonks στο Medium, όπου οι άνθρωποι συνεχίζουν τη συζήτηση επισημαίνοντας και απαντώντας σε αυτή την ιστορία.

Αποποίηση ευθύνης: Τα άρθρα που αναδημοσιεύονται σε αυτόν τον ιστότοπο προέρχονται από δημόσιες πλατφόρμες και παρέχονται μόνο για ενημερωτικούς σκοπούς. Δεν αντικατοπτρίζουν απαραίτητα τις απόψεις της MEXC. Όλα τα πνευματικά δικαιώματα ανήκουν στους αρχικούς συγγραφείς. Εάν πιστεύετε ότι οποιοδήποτε περιεχόμενο παραβιάζει τα δικαιώματα τρίτου μέρους, επικοινωνήστε με τη διεύθυνση [email protected] για την αφαίρεσή του. Η MEXC δεν παρέχει εγγυήσεις σχετικά με την ακρίβεια, την πληρότητα ή την επικαιρότητα του περιεχομένου και δεν ευθύνεται για οποιεσδήποτε ενέργειες που γίνονται με βάση τις παρεχόμενες πληροφορίες. Το περιεχόμενο δεν αποτελεί οικονομική, νομική ή άλλη επαγγελματική συμβουλή, ούτε θα πρέπει να θεωρηθεί σύσταση ή προώθηση της MEXC.

Μπορεί επίσης να σας αρέσει

Το Ιράν φέρεται να ζήτησε από τα δεξαμενόπλοια να πληρώσουν τα διόδια του Στενού του Ορμούζ σε Bitcoin

Το Ιράν φέρεται να ζήτησε από τα δεξαμενόπλοια να πληρώσουν τα διόδια του Στενού του Ορμούζ σε Bitcoin

Μια αναφερόμενη οδηγία που σχετίζεται με το Στενό του Χορμούζ ανέφερε ότι τα πετρελαιοφόρα θα πρέπει να πληρώνουν διόδια 1 δολαρίου ανά βαρέλι σε bitcoin. Η δομή πληρωμής φαινόταν να έχει σχεδιαστεί για να
Κοινοποίηση
Crypto News Flash2026/04/09 15:08
Εάν το Αίτημα Bitcoin του Ιράν μέσω του Στενού του Χορμούζ Γινόταν Πραγματικότητα, Πόσα BTC θα Έλαβε το Ιράν; Ο Ασυνήθιστος Υπολογισμός

Εάν το Αίτημα Bitcoin του Ιράν μέσω του Στενού του Χορμούζ Γινόταν Πραγματικότητα, Πόσα BTC θα Έλαβε το Ιράν; Ο Ασυνήθιστος Υπολογισμός

Ποιες θα ήταν οι συνέπειες εάν το Ιράν απαιτούσε Bitcoin αξίας 1$ ανά βαρέλι από κάθε δεξαμενόπλοιο πετρελαίου που διέρχεται από τα Στενά του Χορμούζ; Συνέχιση Ανάγνωσης: Εάν
Κοινοποίηση
Bitcoinsistemi2026/04/09 15:08
Το EUR/USD διστάζει στο 1.1660 με την κατάπαυση του πυρός του Ιράν σε αβεβαιότητα

Το EUR/USD διστάζει στο 1.1660 με την κατάπαυση του πυρός του Ιράν σε αβεβαιότητα

Η ανάρτηση EUR/USD διστάζει στο 1.1660 με την κατάπαυση πυρός του Ιράν σε αβεβαιότητα εμφανίστηκε στο BitcoinEthereumNews.com. Το (EUR) διαπραγματεύεται σχεδόν επίπεδα, ακριβώς πάνω από
Κοινοποίηση
BitcoinEthereumNews2026/04/09 15:23

$30,000 in PRL + 15,000 USDT

$30,000 in PRL + 15,000 USDT$30,000 in PRL + 15,000 USDT

Deposit & trade PRL to boost your rewards!