Il runtime di Surfpool espone tre helper per il viaggio nel tempo. Ognuno sposta
il clock locale verso un obiettivo assoluto (non un offset relativo) e
restituisce il valore aggiornato EpochInfo così i test possono verificare che
il runtime abbia effettivamente raggiunto il tempo richiesto.
| Helper | Obiettivo | Restituisce |
|---|---|---|
time_travel_to_slot / timeTravelToSlot | Numero di slot assoluto | EpochInfo con il nuovo absolute_slot |
time_travel_to_epoch / timeTravelToEpoch | Numero di epoch assoluto | EpochInfo con il nuovo epoch |
time_travel_to_timestamp / timeTravelToTimestamp | Timestamp Unix in millisecondi | EpochInfo che riflette lo slot implicito |
Solo in avanti
Il viaggio nel tempo può spostare l'orologio solo in avanti. Un target nel passato viene
rifiutato anziché ignorato — gli helper Rust restituiscono un Err e gli helper JS
generano un'eccezione. Il messaggio indica quale unità è andata a ritroso, ad esempio
Cannot travel to past slot: target=1000, current=2000.
Salta a uno 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);
Salta a un Epoch
let info = cheats.time_travel_to_epoch(420)?;assert_eq!(info.epoch, 420);
Salta a un Timestamp Unix
Passa il timestamp in millisecondi dall'epoch Unix — la stessa unità di
Date.now() in JavaScript — non in secondi. Il runtime calcola lo slot più vicino
a quel timestamp.
// 2030-01-01T00:00:00Zlet info = cheats.time_travel_to_timestamp(1_893_456_000_000)?;
L'orologio riporta i secondi, non i millisecondi
Ogni timestamp riletto dal runtime è in secondi — il
unix_timestamp della sysvar Clock on-chain e il
ClockValue trasportato dall'evento
systemClockUpdated. Passare direttamente uno di questi valori a un helper di viaggio nel tempo
risolve a un momento dell'inizio del 1970, quindi la chiamata viene
rifiutata come target nel passato. Moltiplica prima per 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);
Metti in Pausa e Riprendi l'Orologio
Il viaggio nel tempo salta a un target; la pausa arresta completamente l'avanzamento dell'orologio.
surfnet_pauseClock blocca la produzione di slot e il progresso del tempo fino a quando
non viene eseguito surfnet_resumeClock — usalo quando un test necessita che lo slot e
il timestamp rimangano fermi durante più asserzioni.
Né l'SDK Rust né la classe Surfnet in @solana/surfpool racchiudono questi
due cheatcode. Invocali tramite il
plugin Kit, oppure direttamente 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();
Entrambi i cheatcode restituiscono un EpochInfo — lo stato dell'orologio al momento della
pausa e dopo la ripresa. Nota le due cose che quella struttura non contiene:
- Nessun timestamp.
EpochInfonon ha un campo temporale. Leggi il tempo Unix simulato dalla sysvarClock:getAccountInfosuSysvarC1ock11111111111111111111111111111111con encodingjsonParsed— sempre in secondi. - Nessun flag di pausa. Nessun metodo RPC riporta se l'orologio è attualmente
in pausa. Tramite gli SDK, monitora l'evento
clockUpdate, che si attiva con unclockCommandogni volta che viene eseguita una pausa, una ripresa o una modifica dell'intervallo. In alternativa, tieni traccia dello stato nel tuo test.
Pattern Comuni
Testa un Blocco o una Finestra di Vesting
L'esempio seguente è uno schema — assertWithdrawFails e
assertWithdrawSucceeds sono segnaposto per gli helper lato client che la tua
suite di test utilizza per verificare il comportamento 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();
Gestire uno Scenario Multi-Epoch
Gli slot intermedi vengono saltati
Il salto temporale va direttamente alla destinazione — spostandosi dall'epoch
1 all'epoch 5 vengono saltati gli slot intermedi. Se il tuo programma
necessita che ogni confine di epoch intermedio venga attivato (ad esempio, per
accreditare le ricompense per epoch), chiama time_travel_to_epoch una volta
per epoch con le eventuali transazioni necessarie nel mezzo.
for epoch in 2..=5 {cheats.time_travel_to_epoch(epoch)?;surfnet.rpc_client().send_transaction(&claim_rewards_tx)?;}
Struttura di EpochInfo
Entrambi gli SDK restituiscono un oggetto di tipo EpochInfo con questi campi:
| Campo | Rust | JS |
|---|---|---|
| Slot assoluto | absolute_slot: u64 | absoluteSlot: number |
| Slot nell'epoch | slot_index: u64 | slotIndex: number |
| Slot per epoch | slots_in_epoch: u64 | slotsInEpoch: number |
| Numero epoch | epoch: u64 | epoch: number |
| Altezza blocco | block_height: u64 | blockHeight: number |
| Conteggio transazioni | transaction_count: Option<u64> | transactionCount?: number |
Is this page helpful?