Viaje en el Tiempo

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.

HelperObjetivoDevuelve
time_travel_to_slot / timeTravelToSlotNúmero de slot absolutoEpochInfo con el nuevo absolute_slot
time_travel_to_epoch / timeTravelToEpochNúmero de epoch absolutoEpochInfo con el nuevo epoch
time_travel_to_timestamp / timeTravelToTimestampMarca de tiempo Unix en milisegundosEpochInfo 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:00Z
let 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. EpochInfo no tiene campo de tiempo. Lee el tiempo Unix simulado desde el sysvar Clock en su lugar: usa getAccountInfo en SysvarC1ock11111111111111111111111111111111 con codificación jsonParsed — 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 un clockCommand cada 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:

CampoRustJS
Slot absolutoabsolute_slot: u64absoluteSlot: number
Slot dentro del epochslot_index: u64slotIndex: number
Slot por epochslots_in_epoch: u64slotsInEpoch: number
Número de epochepoch: u64epoch: number
Altura del bloqueblock_height: u64blockHeight: number
Recuento de transaccionestransaction_count: Option<u64>transactionCount?: number

Is this page helpful?

Tabla de Contenidos

Editar Página