Το runtime του Surfpool παρέχει τρία βοηθήματα ταξιδιού στο χρόνο. Το καθένα
μετακινεί το τοπικό ρολόι σε έναν απόλυτο στόχο (όχι σχετική μετατόπιση) και
επιστρέφει το ενημερωμένο EpochInfo ώστε οι δοκιμές να μπορούν να επαληθεύσουν
ότι το runtime έφτασε πράγματι στον ζητούμενο χρόνο.
| Helper | Στόχος | Επιστρέφει |
|---|---|---|
time_travel_to_slot / timeTravelToSlot | Απόλυτος αριθμός slot | EpochInfo με το νέο absolute_slot |
time_travel_to_epoch / timeTravelToEpoch | Απόλυτος αριθμός epoch | EpochInfo με το νέο epoch |
time_travel_to_timestamp / timeTravelToTimestamp | Χρονική σήμανση Unix σε χιλιοστά του δευτερολέπτου | EpochInfo που αντικατοπτρίζει το εννοούμενο slot |
Μόνο προς τα εμπρός
Η χρονοταξιδία μπορεί να μετακινήσει το ρολόι μόνο προς τα εμπρός. Ένας στόχος στο παρελθόν
απορρίπτεται αντί να αγνοείται — οι βοηθοί Rust επιστρέφουν Err και οι
βοηθοί JS εκτοξεύουν εξαίρεση. Το μήνυμα ονομάζει τη μονάδα που κινήθηκε προς τα πίσω, για παράδειγμα
Cannot travel to past slot: target=1000, current=2000.
Μετάβαση σε Slot
use surfpool_sdk::Surfnet;let surfnet = Surfnet::start().await?;let cheats = surfnet.cheatcodes();let info = cheats.time_travel_to_slot(1_000_000)?;assert!(info.absolute_slot >= 1_000_000);
Μετάβαση σε Epoch
let info = cheats.time_travel_to_epoch(420)?;assert_eq!(info.epoch, 420);
Μετάβαση σε Χρονική Σήμανση Unix
Περάστε τη χρονική σήμανση σε χιλιοστά του δευτερολέπτου από την αρχή Unix — η ίδια μονάδα με
τη Date.now() της JavaScript — όχι δευτερόλεπτα. Το runtime υπολογίζει το πλησιέστερο slot
σε αυτή τη χρονική σήμανση.
// 2030-01-01T00:00:00Zlet info = cheats.time_travel_to_timestamp(1_893_456_000_000)?;
Το ρολόι αναφέρει δευτερόλεπτα, όχι χιλιοστά του δευτερολέπτου
Κάθε χρονική σήμανση που διαβάζετε από το runtime είναι σε δευτερόλεπτα — το
unix_timestamp του on-chain Clock sysvar, και το
ClockValue που μεταφέρεται από
το συμβάν systemClockUpdated. Αν τροφοδοτήσετε απευθείας μία από αυτές τις τιμές σε έναν
βοηθό χρονοταξιδίας, αντιστοιχεί σε μια στιγμή στις αρχές του 1970, οπότε η κλήση
απορρίπτεται ως παρελθοντικός στόχος. Πολλαπλασιάστε επί 1000 πρώτα.
// WRONG — `unixTimestamp` is in seconds, so this is a past target.surfnet.timeTravelToTimestamp(clock.unixTimestamp + 3600);// RIGHT — convert to milliseconds.surfnet.timeTravelToTimestamp((clock.unixTimestamp + 3600) * 1000);
Παύση και Συνέχιση του Ρολογιού
Η χρονοταξιδία μεταπηδά σε έναν στόχο· η παύση σταματά εντελώς την εξέλιξη του ρολογιού.
Η surfnet_pauseClock διακόπτει την παραγωγή slot και την πρόοδο του χρόνου έως ότου
εκτελεστεί η surfnet_resumeClock — χρησιμοποιήστε την όταν μια δοκιμή χρειάζεται το slot και
τη χρονική σήμανση να παραμείνουν σταθερά κατά τη διάρκεια πολλών επαληθεύσεων.
Ούτε το Rust SDK ούτε η κλάση Surfnet στο @solana/surfpool περιτυλίγουν αυτά
τα δύο cheatcodes. Καλέστε τα μέσω του
Kit plugin, ή απευθείας μέσω JSON-RPC.
const paused = await client.cheatcodes.pauseClock().send();// Nothing advances until the clock is resumed, so this is the only thing that// moves the slot.await client.cheatcodes.timeTravel({ absoluteSlot: paused.absoluteSlot + 1_000n }).send();await client.cheatcodes.resumeClock().send();
Και τα δύο cheatcodes επιστρέφουν ένα EpochInfo — την κατάσταση του ρολογιού τη στιγμή της
παύσης, και μετά την επανεκκίνηση. Σημειώστε τα δύο πράγματα που το σχήμα αυτό δεν περιέχει:
- Χωρίς χρονική σήμανση. Το
EpochInfoδεν έχει πεδίο χρόνου. Διαβάστε τον προσομοιωμένο χρόνο Unix από τοClocksysvar:getAccountInfoστοSysvarC1ock11111111111111111111111111111111με κωδικοποίησηjsonParsed— και πάλι, σε δευτερόλεπτα. - Χωρίς σημαία παύσης. Καμία μέθοδος RPC δεν αναφέρει αν το ρολόι είναι τρέχοντας
σε παύση. Από τα SDKs, παρακολουθήστε το
συμβάν
clockUpdate, το οποίο ενεργοποιείται με έναclockCommandκάθε φορά που εκτελείται παύση, συνέχιση ή αλλαγή διαστήματος. Διαφορετικά, παρακολουθήστε την κατάσταση στη δοκιμή σας.
Συνήθη Μοτίβα
Δοκιμή Κλειδώματος ή Παραθύρου Αποδέσμευσης
Το παρακάτω παράδειγμα είναι σκίτσο — το assertWithdrawFails και το
assertWithdrawSucceeds είναι placeholders για τα βοηθήματα στην πλευρά του
client που χρησιμοποιεί η σουίτα δοκιμών σας για επαλήθευση συμπεριφοράς RPC.
import { Surfnet } from "@solana/surfpool";const surfnet = Surfnet.start();const beneficiary = Surfnet.newKeypair();// 1. Set up a vesting account that unlocks at slot 1,000,000.surfnet.setAccount(/* ...lockup program state... */);// 2. Verify withdrawal fails before unlock.await assertWithdrawFails(surfnet.rpcUrl, beneficiary);// 3. Travel past the unlock slot.surfnet.timeTravelToSlot(1_000_001);// 4. Verify withdrawal succeeds.await assertWithdrawSucceeds(surfnet.rpcUrl, beneficiary);surfnet.stop();
Οδήγηση Σεναρίου Πολλαπλών Epoch
Τα ενδιάμεσα slot παραλείπονται
Η χρονική μετάβαση γίνεται απευθείας στον στόχο — η μετακίνηση από epoch 1 σε
epoch 5 παραλείπει τα slot ενδιάμεσα. Αν το πρόγραμμά σας χρειάζεται να
ενεργοποιείται κάθε ενδιάμεσο όριο epoch (για παράδειγμα, για να πιστωθούν
ανταμοιβές ανά epoch), καλέστε time_travel_to_epoch μία φορά ανά epoch με
τις απαραίτητες συναλλαγές ενδιάμεσα.
for epoch in 2..=5 {cheats.time_travel_to_epoch(epoch)?;surfnet.rpc_client().send_transaction(&claim_rewards_tx)?;}
Δομή EpochInfo
Και τα δύο SDK επιστρέφουν ένα αντικείμενο τύπου EpochInfo με τα εξής πεδία:
| Field | Rust | JS |
|---|---|---|
| Απόλυτο slot | absolute_slot: u64 | absoluteSlot: number |
| Slot εντός epoch | slot_index: u64 | slotIndex: number |
| Slot ανά epoch | slots_in_epoch: u64 | slotsInEpoch: number |
| Αριθμός epoch | epoch: u64 | epoch: number |
| Ύψος μπλοκ | block_height: u64 | blockHeight: number |
| Αριθμός συναλλαγών | transaction_count: Option<u64> | transactionCount?: number |
Is this page helpful?