Podróż w czasie

Środowisko uruchomieniowe Surfpool udostępnia trzy pomocniki podróży w czasie. Każdy z nich przesuwa lokalny zegar do bezwzględnego celu (nie względnego przesunięcia) i zwraca zaktualizowany EpochInfo, dzięki czemu testy mogą potwierdzić, że środowisko uruchomieniowe rzeczywiście osiągnęło żądany czas.

PomocnikCelZwraca
time_travel_to_slot / timeTravelToSlotBezwzględny numer slotEpochInfo z nowym absolute_slot
time_travel_to_epoch / timeTravelToEpochBezwzględny numer epochEpochInfo z nowym epoch
time_travel_to_timestamp / timeTravelToTimestampZnacznik czasu Unix w milisekundachEpochInfo odzwierciedlający implikowany slot

Tylko do przodu

Podróż w czasie może przesunąć zegar tylko do przodu. Cel w przeszłości jest odrzucany, a nie ignorowany — helpery Rust zwracają Err, a helpery JS zgłaszają wyjątek. Komunikat wskazuje jednostkę, która cofnęła się w czasie, na przykład Cannot travel to past slot: target=1000, current=2000.

Przeskocz do 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);

Przeskocz do epoch

let info = cheats.time_travel_to_epoch(420)?;
assert_eq!(info.epoch, 420);

Przeskocz do znacznika czasu Unix

Przekaż znacznik czasu w milisekundach od epoki Unix — tej samej jednostce co JavaScript's Date.now() — nie w sekundach. Środowisko uruchomieniowe oblicza najbliższy slot dla tego znacznika czasu.

// 2030-01-01T00:00:00Z
let info = cheats.time_travel_to_timestamp(1_893_456_000_000)?;

Zegar podaje sekundy, nie milisekundy

Każdy znacznik czasu odczytany ze środowiska uruchomieniowego jest wyrażony w sekundachunix_timestamp zmiennej systemowej Clock w łańcuchu bloków oraz ClockValue przesyłany przez zdarzenie systemClockUpdated. Bezpośrednie przekazanie jednej z tych wartości do helpera podróży w czasie spowoduje wskazanie momentu z początku 1970 roku, dlatego wywołanie zostanie odrzucone jako cel w przeszłości. Najpierw pomnóż przez 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);

Wstrzymaj i wznów zegar

Podróż w czasie skacze do celu; wstrzymanie całkowicie zatrzymuje zegar. surfnet_pauseClock wstrzymuje produkcję slot i upływ czasu, dopóki nie zostanie uruchomione surfnet_resumeClock — używaj tego, gdy test wymaga, aby slot i znacznik czasu pozostały niezmienione przez kilka asercji.

Ani Rust SDK, ani klasa Surfnet z @solana/surfpool nie opakowują tych dwóch cheatcode'ów. Wywołuj je przez wtyczkę Kit lub bezpośrednio przez 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();

Oba cheatcode'y zwracają EpochInfo — stan zegara w momencie wstrzymania oraz po wznowieniu. Zwróć uwagę na dwie rzeczy, których ta struktura nie zawiera:

  • Brak znacznika czasu. EpochInfo nie posiada pola czasu. Odczytaj symulowany czas Unix ze zmiennej systemowej Clock: użyj getAccountInfo na SysvarC1ock11111111111111111111111111111111 z kodowaniem jsonParsed — ponownie, w sekundach.
  • Brak flagi wstrzymania. Żadna metoda RPC nie informuje, czy zegar jest aktualnie wstrzymany. W SDK obserwuj zdarzenie clockUpdate, które jest wyzwalane z clockCommand przy każdym wstrzymaniu, wznowieniu lub zmianie interwału. W przeciwnym razie śledź stan samodzielnie w swoim teście.

Typowe wzorce

Testowanie blokady lub okna nabywania uprawnień

Poniższy przykład to szkic — assertWithdrawFails i assertWithdrawSucceeds są symbolami zastępczymi dla dowolnych pomocników po stronie klienta używanych przez zestaw testów do weryfikacji zachowania 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();

Symuluj Scenariusz Wielu Epoch

Pośrednie slot są pomijane

Podróż w czasie skacze bezpośrednio do celu — przejście z epoch 1 do epoch 5 pomija slot pomiędzy nimi. Jeśli Twój program wymaga, aby każda pośrednia granica epoch została wyzwolona (na przykład w celu naliczenia nagród za epoch), wywołaj time_travel_to_epoch raz na epoch z wymaganymi transakcjami pomiędzy wywołaniami.

for epoch in 2..=5 {
cheats.time_travel_to_epoch(epoch)?;
surfnet.rpc_client().send_transaction(&claim_rewards_tx)?;
}

Struktura EpochInfo

Oba SDK zwracają obiekt w kształcie EpochInfo z następującymi polami:

PoleRustJS
Bezwzględny slotabsolute_slot: u64absoluteSlot: number
slot w epochslot_index: u64slotIndex: number
slot na epochslots_in_epoch: u64slotsInEpoch: number
Numer epochepoch: u64epoch: number
Wysokość blokublock_height: u64blockHeight: number
Liczba transakcjitransaction_count: Option<u64>transactionCount?: number

Is this page helpful?

Spis treści

Edytuj stronę