Рантайм Surfpool надає три допоміжні засоби для подорожей у часі. Кожен із них
переміщує локальний годинник до абсолютної цілі (а не відносного зміщення) і
повертає оновлений EpochInfo, щоб тести могли перевірити, що рантайм справді
досяг запитаного часу.
| Помічник | Ціль | Повертає |
|---|---|---|
time_travel_to_slot / timeTravelToSlot | Абсолютний номер slot | EpochInfo з новим absolute_slot |
time_travel_to_epoch / timeTravelToEpoch | Абсолютний номер epoch | EpochInfo з новим epoch |
time_travel_to_timestamp / timeTravelToTimestamp | Мітка часу Unix у мілісекундах | EpochInfo, що відображає відповідний slot |
Лише вперед
Подорож у часі може переміщати годинник лише вперед. Ціль у минулому
відхиляється, а не ігнорується — хелпери Rust повертають Err, а JS-хелпери
кидають виняток. Повідомлення вказує, яка одиниця пішла назад, наприклад:
Cannot travel to past slot: target=1000, current=2000.
Перехід до 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);
Перехід до epoch
let info = cheats.time_travel_to_epoch(420)?;assert_eq!(info.epoch, 420);
Перехід до мітки часу Unix
Передавайте мітку часу в мілісекундах від початку епохи Unix — у тих самих одиницях, що й
JavaScript Date.now() — а не в секундах. Середовище виконання обчислює найближчий slot
для цієї мітки часу.
// 2030-01-01T00:00:00Zlet info = cheats.time_travel_to_timestamp(1_893_456_000_000)?;
Годинник повертає секунди, а не мілісекунди
Кожна мітка часу, яку ви зчитуєте з середовища виконання, вимірюється в секундах —
unix_timestamp системної змінної Clock on-chain, і
ClockValue, що передається
подією systemClockUpdated. Якщо передати одне з цих значень безпосередньо до
хелпера подорожі в часі, воно вказуватиме на момент на початку 1970 року, тому виклик
буде відхилено як ціль у минулому. Попередньо помножте на 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);
Призупинення та відновлення годинника
Подорож у часі переміщає до цілі; призупинення повністю зупиняє рух годинника.
surfnet_pauseClock зупиняє виробництво slot і плин часу до моменту запуску
surfnet_resumeClock — використовуйте це, коли тесту потрібно зафіксувати slot і
мітку часу незмінними протягом кількох перевірок.
Ні Rust SDK, ні клас Surfnet у @solana/surfpool не обгортають ці
два читкоди. Викликайте їх через
плагін Kit або безпосередньо через 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();
Обидва читкоди повертають EpochInfo — стан годинника на момент
призупинення та після відновлення. Зверніть увагу на дві речі, яких ця структура не містить:
- Без мітки часу.
EpochInfoне має поля часу. Зчитуйте симульований час Unix із системної змінноїClock:getAccountInfoдляSysvarC1ock11111111111111111111111111111111з кодуваннямjsonParsed— знову ж у секундах. - Без прапорця призупинення. Жоден метод RPC не повідомляє, чи призупинено годинник наразі.
З боку SDK відстежуйте
подію
clockUpdate, яка спрацьовує зclockCommandщоразу, коли виконується призупинення, відновлення або зміна інтервалу. В іншому випадку відстежуйте стан у своєму тесті.
Поширені шаблони
Тестування блокування або вікна вестингу
Наведений нижче приклад є схематичним — assertWithdrawFails та
assertWithdrawSucceeds є заповнювачами для будь-яких допоміжних засобів на
стороні клієнта, які ваш набір тестів використовує для перевірки поведінки 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();
Керування Сценарієм із Кількох Epoch
Проміжні slot пропускаються
Подорож у часі переходить напряму до цілі — переміщення з epoch 1 до epoch 5
пропускає slot між ними. Якщо вашій програмі потрібно, щоб спрацювала кожна
проміжна межа epoch (наприклад, для нарахування винагород за epoch),
викликайте time_travel_to_epoch один раз на epoch із необхідними
транзакціями між викликами.
for epoch in 2..=5 {cheats.time_travel_to_epoch(epoch)?;surfnet.rpc_client().send_transaction(&claim_rewards_tx)?;}
Структура EpochInfo
Обидва SDK повертають об'єкт типу EpochInfo із такими полями:
| Поле | Rust | JS |
|---|---|---|
| Абсолютний slot | absolute_slot: u64 | absoluteSlot: number |
| Slot у межах epoch | slot_index: u64 | slotIndex: number |
| Slot на epoch | slots_in_epoch: u64 | slotsInEpoch: number |
| Номер epoch | epoch: u64 | epoch: number |
| Висота блоку | block_height: u64 | blockHeight: number |
| Кількість транзакцій | transaction_count: Option<u64> | transactionCount?: number |
Is this page helpful?