Le runtime Surfpool expose trois helpers de voyage dans le temps. Chacun déplace
l'horloge locale vers une cible absolue (et non un décalage relatif) et retourne
le EpochInfo mis à jour afin que les tests puissent vérifier que le runtime a
bien atteint le moment demandé.
| Helper | Cible | Retourne |
|---|---|---|
time_travel_to_slot / timeTravelToSlot | Numéro de slot absolu | EpochInfo avec le nouveau absolute_slot |
time_travel_to_epoch / timeTravelToEpoch | Numéro d'epoch absolu | EpochInfo avec le nouvel epoch |
time_travel_to_timestamp / timeTravelToTimestamp | Horodatage Unix en millisecondes | EpochInfo reflétant le slot implicite |
Vers l'avenir uniquement
Le voyage dans le temps ne peut déplacer l'horloge que vers l'avenir. Une cible dans le passé est
rejetée plutôt qu'ignorée — les helpers Rust retournent une Err et les helpers JS
lèvent une exception. Le message indique l'unité qui est remontée dans le passé, par exemple
Cannot travel to past slot: target=1000, current=2000.
Aller à un 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);
Aller à un epoch
let info = cheats.time_travel_to_epoch(420)?;assert_eq!(info.epoch, 420);
Aller à un horodatage Unix
Passez l'horodatage en millisecondes depuis l'epoch Unix — la même unité que
Date.now() en JavaScript — et non en secondes. Le runtime calcule le slot le plus proche
à cet horodatage.
// 2030-01-01T00:00:00Zlet info = cheats.time_travel_to_timestamp(1_893_456_000_000)?;
L'horloge indique des secondes, pas des millisecondes
Tout horodatage que vous lisez depuis le runtime est en secondes — le
unix_timestamp du sysvar Clock on-chain, et la
ClockValue transportée par
l'événement systemClockUpdated. Réinjecter directement l'une de ces valeurs dans
un helper de voyage dans le temps correspond à un moment au début de 1970, donc l'appel est
rejeté comme cible passée. Multipliez d'abord par 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);
Mettre en pause et reprendre l'horloge
Le voyage dans le temps saute vers une cible ; la mise en pause arrête complètement la progression de l'horloge.
surfnet_pauseClock interrompt la production de slots et l'avancement du temps jusqu'à ce que
surfnet_resumeClock soit exécuté — utilisez-le lorsqu'un test nécessite que le slot et
l'horodatage restent fixes sur plusieurs assertions.
Ni le SDK Rust ni la classe Surfnet de @solana/surfpool n'encapsulent ces
deux cheatcodes. Appelez-les via le
plugin Kit, ou directement via 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();
Les deux cheatcodes retournent un EpochInfo — l'état de l'horloge au moment de la
mise en pause, et après la reprise. Notez les deux informations que cette structure ne contient pas :
- Pas d'horodatage.
EpochInfone possède pas de champ temporel. Lisez le temps Unix simulé depuis le sysvarClock:getAccountInfosurSysvarC1ock11111111111111111111111111111111avec l'encodagejsonParsed— là encore, en secondes. - Pas d'indicateur de pause. Aucune méthode RPC ne signale si l'horloge est actuellement
en pause. Depuis les SDK, surveillez
l'événement
clockUpdate, qui se déclenche avec uneclockCommandà chaque pause, reprise ou changement d'intervalle. Sinon, suivez l'état dans votre test.
Cas d'utilisation courants
Tester une fenêtre de blocage ou d'acquisition
L'exemple ci-dessous est une ébauche — assertWithdrawFails et
assertWithdrawSucceeds sont des espaces réservés pour les helpers côté client
que votre suite de tests utilise pour vérifier le comportement des appels 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();
Simuler un Scénario Multi-Epoch
Les slots intermédiaires sont ignorés
Le voyage dans le temps saute directement vers la cible — passer de l'epoch 1
à l'epoch 5 ignore les slots intermédiaires. Si votre programme a besoin que
chaque limite d'epoch intermédiaire se déclenche (par exemple, pour créditer
des récompenses par epoch), appelez time_travel_to_epoch une fois par epoch
avec les transactions requises entre chaque appel.
for epoch in 2..=5 {cheats.time_travel_to_epoch(epoch)?;surfnet.rpc_client().send_transaction(&claim_rewards_tx)?;}
Structure EpochInfo
Les deux SDK retournent un objet de type EpochInfo avec les champs suivants :
| Champ | Rust | JS |
|---|---|---|
| Slot absolu | absolute_slot: u64 | absoluteSlot: number |
| Slot dans l'epoch | slot_index: u64 | slotIndex: number |
| Slots par epoch | slots_in_epoch: u64 | slotsInEpoch: number |
| Numéro d'epoch | epoch: u64 | epoch: number |
| Hauteur de bloc | block_height: u64 | blockHeight: number |
| Nombre de transactions | transaction_count: Option<u64> | transactionCount?: number |
Is this page helpful?