El runtime de Surfpool expone tres helpers de viaje en el tiempo. Cada uno mueve
el reloj local a un objetivo absoluto (no un desplazamiento relativo) y devuelve
el EpochInfo actualizado para que las pruebas puedan verificar que el runtime
realmente alcanzó el tiempo solicitado.
| Helper | Objetivo | Devuelve |
|---|---|---|
time_travel_to_slot / timeTravelToSlot | Número de slot absoluto | EpochInfo con el nuevo absolute_slot |
time_travel_to_epoch / timeTravelToEpoch | Número de epoch absoluto | EpochInfo con el nuevo epoch |
time_travel_to_timestamp / timeTravelToTimestamp | Marca de tiempo Unix en milisegundos | EpochInfo que refleja el slot implícito |
Solo hacia adelante
El viaje en el tiempo solo puede mover el reloj hacia adelante. Un objetivo en el pasado se
rechaza en lugar de ignorarse — los helpers de Rust devuelven un Err y los helpers de JS
lanzan una excepción. El mensaje indica qué unidad retrocedió, por ejemplo
Cannot travel to past slot: target=1000, current=2000.
Saltar a 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);
Saltar a un Epoch
let info = cheats.time_travel_to_epoch(420)?;assert_eq!(info.epoch, 420);
Saltar a una Marca de Tiempo Unix
Pasa la marca de tiempo en milisegundos desde el epoch Unix — la misma unidad que
Date.now() de JavaScript — no en segundos. El entorno de ejecución calcula el slot
más cercano a esa marca de tiempo.
// 2030-01-01T00:00:00Zlet info = cheats.time_travel_to_timestamp(1_893_456_000_000)?;
El reloj reporta segundos, no milisegundos
Cada marca de tiempo que leas desde el entorno de ejecución está en segundos — el
unix_timestamp del sysvar Clock en cadena, y el
ClockValue que lleva
el evento systemClockUpdated. Si introduces uno de esos valores directamente en
un helper de viaje en el tiempo, se resuelve a un momento de principios de 1970, por lo que la llamada se
rechaza como objetivo en el pasado. Multiplica por 1000 primero.
// 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);
Pausar y Reanudar el Reloj
El viaje en el tiempo salta a un objetivo; pausar detiene por completo el avance del reloj.
surfnet_pauseClock detiene la producción de slots y el progreso del tiempo hasta que
surfnet_resumeClock se ejecute — úsalo cuando una prueba necesite que el slot y
la marca de tiempo permanezcan fijos a lo largo de varias aserciones.
Ni el SDK de Rust ni la clase Surfnet en @solana/surfpool encapsulan estos
dos cheatcodes. Llámalos a través del
plugin Kit, o directamente mediante 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();
Ambos cheatcodes devuelven un EpochInfo — el estado del reloj en el momento de la
pausa y después de reanudarse. Ten en cuenta las dos cosas que esa estructura no contiene:
- Sin marca de tiempo.
EpochInfono tiene campo de tiempo. Lee el tiempo Unix simulado desde el sysvarClocken su lugar: usagetAccountInfoenSysvarC1ock11111111111111111111111111111111con codificaciónjsonParsed— de nuevo, en segundos. - Sin indicador de pausa. Ningún método RPC informa si el reloj está actualmente
pausado. Desde los SDKs, observa el
evento
clockUpdate, que se activa con unclockCommandcada vez que se ejecuta una pausa, reanudación o cambio de intervalo. De lo contrario, lleva el seguimiento del estado en tu prueba.
Patrones Comunes
Probar un Bloqueo o Ventana de Adquisición
El ejemplo a continuación es un esquema — assertWithdrawFails e
assertWithdrawSucceeds son marcadores de posición para los helpers del lado
del cliente que utilice tu suite de pruebas para verificar el comportamiento de
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();
Ejecutar un escenario de múltiples epoch
Los slot intermedios se omiten
El viaje en el tiempo salta directamente al destino: moverse del epoch 1 al
epoch 5 omite los slot intermedios. Si tu programa necesita que se active cada
límite de epoch intermedio (por ejemplo, para acreditar recompensas por
epoch), llama a time_travel_to_epoch una vez por epoch con las transacciones
necesarias entre medias.
for epoch in 2..=5 {cheats.time_travel_to_epoch(epoch)?;surfnet.rpc_client().send_transaction(&claim_rewards_tx)?;}
Estructura de EpochInfo
Ambos SDKs devuelven un objeto con la forma EpochInfo con estos campos:
| Campo | Rust | JS |
|---|---|---|
| Slot absoluto | absolute_slot: u64 | absoluteSlot: number |
| Slot dentro del epoch | slot_index: u64 | slotIndex: number |
| Slot por epoch | slots_in_epoch: u64 | slotsInEpoch: number |
| Número de epoch | epoch: u64 | epoch: number |
| Altura del bloque | block_height: u64 | blockHeight: number |
| Recuento de transacciones | transaction_count: Option<u64> | transactionCount?: number |
Is this page helpful?