Kiểm Thử Dựa Trên Thời Gian

Minh họa cách sử dụng WarpToSlotSetClock để kiểm thử logic chương trình phụ thuộc thời gian.

Thao Tác Đồng Hồ Cơ Bản

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)
}

Kiểm Thử Logic Khóa Thời Gian

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
}

Đọc Lịch 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 tiến đồng hồ ở trường Slot nhưng không tự động cập nhật Epoch hay UnixTimestamp. Dùng SetClock để kiểm soát toàn bộ các trường đồng hồ.

Thao Tác Đồng Hồ Thủ Công

Khi chương trình của bạn đọc Epoch hoặc UnixTimestamp từ sysvar Clock, hãy thiết lập trực tiếp các trường đó bằng 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
}

Điều này phản ánh mẫu set_sysvar(&clock) trong Rust. Dùng WarpToSlot để tiến slot đơn giản, dùng SetClock khi bạn cần kiểm soát epoch hoặc timestamp.

Các Trường Hợp Sử Dụng

Thao tác thời gian hữu ích để kiểm thử:

Kịch bảnCách tiếp cận
Vesting tokenWarp qua ngưỡng / mốc vesting
Kết thúc phiên đấu giáWarp qua slot kết thúc đấu giá
Phần thưởng stakingDùng SetClock để đặt epoch
Rút tiền khóa thời gianWarp qua slot mở khóa
Giới hạn tốc độWarp giữa các khoảng thời gian được phép
Lệnh hết hạnWarp qua thời điểm hết hạn lệnh

WarpToSlot chỉ thay đổi slot. Unix timestamp có thể không cập nhật tương ứng - dùng SetClock nếu chương trình của bạn đọc UnixTimestamp.

Những Điểm Chính

  1. Sysvars mặc định đã bật - litesvm.New() trả về một handle với sysvars đã được khởi tạo; gọi SetSysvars() để đặt lại về mặc định nếu bạn đã thay đổi chúng.
  2. Tiến về phía trước - WarpToSlot(slot) chỉ tiến trường slot.
  3. Kiểm soát toàn bộ - SetClock(Clock{...}) cho phép bạn đặt Epoch, UnixTimestamp và các trường khác.
  4. Đọc - svm.Clock() đọc các giá trị hiện tại.
  5. Mẫu kiểm thử - kiểm thử trước và sau ranh giới thời gian; kỳ vọng thất bại ở một phía và thành công ở phía kia.

Is this page helpful?

Mục lục

Chỉnh sửa trang