Test Basati sul Tempo

Dimostra l'utilizzo di WarpToSlot e SetClock per testare la logica del programma dipendente dal tempo.

Manipolazione Base del Clock

package mytest
import (
"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)
}

Test della Logica con Blocco Temporale

package mytest
import (
"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] = 0x01
binary.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
}

Lettura dello Schedule degli 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 avanza il campo Slot del clock ma non aggiorna automaticamente Epoch o UnixTimestamp. Usa SetClock per il controllo completo dei campi del clock.

Manipolazione Manuale del Clock

Quando il tuo programma legge Epoch o UnixTimestamp dalla sysvar Clock, impostali direttamente con 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_000
c.Epoch = 5
c.UnixTimestamp = 1_700_000_000
if 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) // 50000
t.Logf("epoch: %d", after.Epoch) // 5
t.Logf("unix: %d", after.UnixTimestamp) // 1700000000
}

Questo rispecchia il pattern Rust set_sysvar(&clock). Usa WarpToSlot per l'avanzamento semplice dello slot, SetClock quando hai bisogno del controllo su epoch o timestamp.

Casi d'Uso

La manipolazione del tempo è utile per testare:

ScenarioApproccio
Token vestingAvanza oltre il cliff/le milestone di vesting
Fine delle asteAvanza oltre lo slot di fine asta
Premi di stakingUsa SetClock per impostare l'epoch
Prelievi con blocco temporaleAvanza oltre lo slot di sblocco
Rate limitingAvanza tra gli intervalli consentiti
Ordini in scadenzaAvanza oltre la scadenza dell'ordine

WarpToSlot modifica solo lo slot. Il timestamp unix potrebbe non aggiornarsi proporzionalmente - usa SetClock se il tuo programma legge UnixTimestamp.

Punti Chiave

  1. Le sysvar predefinite sono attive - litesvm.New() restituisce un handle con le sysvar inizializzate; chiama SetSysvars() per ripristinare i valori predefiniti se le hai modificate.
  2. Avanzamento in avanti - WarpToSlot(slot) avanza solo il campo slot.
  3. Controllo completo - SetClock(Clock{...}) ti permette di impostare Epoch, UnixTimestamp e gli altri campi.
  4. Lettura - svm.Clock() legge i valori correnti.
  5. Pattern di test - testa prima e dopo il limite temporale; aspettati un errore da un lato e il successo dall'altro.

Is this page helpful?

Indice dei contenuti

Modifica pagina
© 2026 Solana Foundation. Tutti i diritti riservati.