Voyage dans le temps

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é.

HelperCibleRetourne
time_travel_to_slot / timeTravelToSlotNuméro de slot absoluEpochInfo avec le nouveau absolute_slot
time_travel_to_epoch / timeTravelToEpochNuméro d'epoch absoluEpochInfo avec le nouvel epoch
time_travel_to_timestamp / timeTravelToTimestampHorodatage Unix en millisecondesEpochInfo 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:00Z
let 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. EpochInfo ne possède pas de champ temporel. Lisez le temps Unix simulé depuis le sysvar Clock : getAccountInfo sur SysvarC1ock11111111111111111111111111111111 avec l'encodage jsonParsed — 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 une clockCommand à 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 :

ChampRustJS
Slot absoluabsolute_slot: u64absoluteSlot: number
Slot dans l'epochslot_index: u64slotIndex: number
Slots par epochslots_in_epoch: u64slotsInEpoch: number
Numéro d'epochepoch: u64epoch: number
Hauteur de blocblock_height: u64blockHeight: number
Nombre de transactionstransaction_count: Option<u64>transactionCount?: number

Is this page helpful?

Table des matières

Modifier la page
© 2026 Fondation Solana. Tous droits réservés.