السفر عبر الزمن

يوفر وقت تشغيل Surfpool ثلاثة أدوات مساعدة للسفر عبر الزمن. كل واحدة منها تنقل الساعة المحلية إلى هدف مطلق (وليس إزاحة نسبية) وتُعيد EpochInfo المحدَّثة حتى تتمكن الاختبارات من التحقق من أن وقت التشغيل وصل فعلاً إلى الوقت المطلوب.

HelperTargetReturns
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 epoch — وهي الوحدة ذاتها التي تستخدمها Date.now() في JavaScript — وليس الثواني. يحسب وقت التشغيل أقرب slot لذلك الطابع الزمني.

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

الساعة تُرجع قيمًا بالثواني، لا بالمللي ثانية

كل طابع زمني تقرأه من وقت التشغيل يكون بوحدة الثواني — سواء unix_timestamp في متغير النظام Clock على السلسلة، أو قيمة 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 يحتوي على هذه الحقول:

الحقلRustJS
الـ slot المطلقabsolute_slot: u64absoluteSlot: number
الـ slot ضمن الـ epochslot_index: u64slotIndex: number
عدد الـ slot في الـ epochslots_in_epoch: u64slotsInEpoch: number
رقم الـ epochepoch: u64epoch: number
ارتفاع الكتلةblock_height: u64blockHeight: number
عدد المعاملاتtransaction_count: Option<u64>transactionCount?: number

Is this page helpful?