Ταξίδι στο Χρόνο

Το runtime του Surfpool παρέχει τρία βοηθήματα ταξιδιού στο χρόνο. Το καθένα μετακινεί το τοπικό ρολόι σε έναν απόλυτο στόχο (όχι σχετική μετατόπιση) και επιστρέφει το ενημερωμένο EpochInfo ώστε οι δοκιμές να μπορούν να επαληθεύσουν ότι το runtime έφτασε πράγματι στον ζητούμενο χρόνο.

HelperΣτόχοςΕπιστρέφει
time_travel_to_slot / timeTravelToSlotΑπόλυτος αριθμός slotEpochInfo με το νέο absolute_slot
time_travel_to_epoch / timeTravelToEpochΑπόλυτος αριθμός epochEpochInfo με το νέο 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:00Z
let 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 από το Clock sysvar: 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 με τα εξής πεδία:

FieldRustJS
Απόλυτο slotabsolute_slot: u64absoluteSlot: number
Slot εντός epochslot_index: u64slotIndex: number
Slot ανά epochslots_in_epoch: u64slotsInEpoch: number
Αριθμός epochepoch: u64epoch: number
Ύψος μπλοκblock_height: u64blockHeight: number
Αριθμός συναλλαγώνtransaction_count: Option<u64>transactionCount?: number

Is this page helpful?

© 2026 Ίδρυμα Solana. Με επιφύλαξη παντός δικαιώματος.