Demonstruje użycie WarpToSlot i SetClock do testowania logiki programu
zależnej od czasu.
Podstawowa manipulacja zegarem
package mytestimport ("testing"litesvm "github.com/LiteSVM/litesvm-go")func TestClockWarp(t *testing.T) {svm, err := litesvm.New()if err != nil {t.Fatal(err)}defer svm.Close()// Read the initial clock.initial, err := svm.Clock()if err != nil {t.Fatal(err)}t.Logf("initial state:")t.Logf(" slot: %d", initial.Slot)t.Logf(" epoch: %d", initial.Epoch)t.Logf(" unix timestamp: %d", initial.UnixTimestamp)// Warp to slot 1_000.if err := svm.WarpToSlot(1_000); err != nil {t.Fatal(err)}after, err := svm.Clock()if err != nil {t.Fatal(err)}t.Logf("after warpToSlot(1000):")t.Logf(" slot: %d", after.Slot)t.Logf(" epoch: %d", after.Epoch)// Warp further.if err := svm.WarpToSlot(100_000); err != nil {t.Fatal(err)}after2, err := svm.Clock()if err != nil {t.Fatal(err)}t.Logf("after warpToSlot(100000):")t.Logf(" slot: %d", after2.Slot)t.Logf(" epoch: %d", after2.Epoch)}
Testowanie logiki blokowanej czasowo
package mytestimport ("encoding/binary""testing"litesvm "github.com/LiteSVM/litesvm-go"solana "github.com/gagliardetto/solana-go")func TestTimeLockedVault(t *testing.T) {svm, err := litesvm.New()if err != nil {t.Fatal(err)}defer svm.Close()// Setters are infallible in practice; discarding the return keeps the// example readable._ = svm.SetSigverify(false)_ = svm.SetBlockhashCheck(false)_ = svm.SetSysvars()// Fund a payer.priv, err := solana.NewRandomPrivateKey()if err != nil {t.Fatal(err)}payer := priv.PublicKey()if err := svm.Airdrop(payer, 10_000_000_000); err != nil {t.Fatal(err)}// Set up a time-locked vault account.// Layout: [u8 discriminator, u64 unlock_slot, u64 amount]const unlockSlot = uint64(10_000)const lockedAmount = uint64(5_000_000_000)vaultData := make([]byte, 1+8+8)vaultData[0] = 0x01binary.LittleEndian.PutUint64(vaultData[1:9], unlockSlot)binary.LittleEndian.PutUint64(vaultData[9:17], lockedAmount)programID := solana.NewWallet().PublicKey()vaultAddr := solana.NewWallet().PublicKey()minBalance, err := svm.MinimumBalanceForRentExemption(len(vaultData))if err != nil {t.Fatal(err)}acct, err := litesvm.NewAccount(minBalance+lockedAmount, vaultData, programID, false, 0)if err != nil {t.Fatal(err)}defer acct.Close()if err := svm.SetAccount(vaultAddr, acct); err != nil {t.Fatal(err)}t.Logf("vault created: unlock slot=%d, locked=%.3f SOL",unlockSlot, float64(lockedAmount)/1e9)// --- Test 1: Before unlock ---cBefore, err := svm.Clock()if err != nil {t.Fatal(err)}t.Logf("current slot: %d", cBefore.Slot)// In a real test you would build a withdraw instruction here and assert// it fails because the current slot < unlock_slot.t.Log("withdrawal attempt: EXPECTED TO FAIL (too early)")// --- Test 2: After unlock ---if err := svm.WarpToSlot(unlockSlot + 100); err != nil {t.Fatal(err)}cAfter, err := svm.Clock()if err != nil {t.Fatal(err)}t.Logf("current slot: %d", cAfter.Slot)// Build and submit the withdraw instruction; assert it succeeds.t.Log("withdrawal attempt: EXPECTED TO SUCCEED (after unlock)")// Verify vault state.updated := svm.GetAccount(vaultAddr)if updated == nil {t.Fatal("vault missing")}defer updated.Close()t.Logf("vault balance: %d lamports", updated.Lamports())_ = payer // payer would be referenced when building the withdraw ix}
Odczytywanie harmonogramu epoch
func TestReadEpochSchedule(t *testing.T) {svm, err := litesvm.New()if err != nil {t.Fatal(err)}defer svm.Close()es, err := svm.EpochSchedule()if err != nil {t.Fatal(err)}t.Logf("slots per epoch: %d", es.SlotsPerEpoch)t.Logf("first normal epoch: %d", es.FirstNormalEpoch)t.Logf("first normal slot: %d", es.FirstNormalSlot)}
WarpToSlot przesuwa pole Slot zegara, ale nie aktualizuje automatycznie
pól Epoch ani UnixTimestamp. Użyj SetClock, aby mieć pełną kontrolę nad
polami zegara.
Ręczna manipulacja zegarem
Gdy Twój program odczytuje Epoch lub UnixTimestamp z sysvara Clock,
ustaw te pola bezpośrednio za pomocą SetClock:
func TestSetClock(t *testing.T) {svm, err := litesvm.New()if err != nil {t.Fatal(err)}defer svm.Close()c, err := svm.Clock()if err != nil {t.Fatal(err)}c.Slot = 50_000c.Epoch = 5c.UnixTimestamp = 1_700_000_000if err := svm.SetClock(c); err != nil {t.Fatal(err)}after, err := svm.Clock()if err != nil {t.Fatal(err)}t.Logf("slot: %d", after.Slot) // 50000t.Logf("epoch: %d", after.Epoch) // 5t.Logf("unix: %d", after.UnixTimestamp) // 1700000000}
To odzwierciedla wzorzec set_sysvar(&clock) z języka Rust. Używaj WarpToSlot do prostego
przesuwania slot, a SetClock gdy potrzebujesz kontroli nad epoch lub znacznikiem czasu.
Przypadki użycia
Manipulacja czasem jest przydatna do testowania:
| Scenariusz | Podejście |
|---|---|
| Nabywanie tokenów (vesting) | Przesuń za próg / kamienie milowe nabywania |
| Zakończenia aukcji | Przesuń za slot zakończenia aukcji |
| Nagrody za staking | Użyj SetClock, aby ustawić epoch |
| Wypłaty blokowane czasowo | Przesuń za slot odblokowania |
| Ograniczanie częstotliwości | Przesuń między dozwolonymi interwałami |
| Wygasające zlecenia | Przesuń za czas wygaśnięcia zlecenia |
WarpToSlot zmienia tylko slot. Znacznik czasu unix może nie być aktualizowany
proporcjonalnie — użyj SetClock, jeśli Twój program odczytuje UnixTimestamp.
Kluczowe informacje
- Domyślne sysvary są włączone —
litesvm.New()zwraca uchwyt z zainicjalizowanymi sysvarami; wywołajSetSysvars(), aby zresetować do wartości domyślnych, jeśli zostały zmienione. - Przesuwanie do przodu —
WarpToSlot(slot)przesuwa jedynie pole slot. - Pełna kontrola —
SetClock(Clock{...})pozwala ustawićEpoch,UnixTimestampi pozostałe pola. - Odczyt —
svm.Clock()odczytuje bieżące wartości. - Wzorzec testowy — testuj przed i po granicy czasowej; oczekuj niepowodzenia po jednej stronie i sukcesu po drugiej.
Is this page helpful?