mirror of
https://github.com/luxfi/indexer.git
synced 2026-09-24 00:25:37 +00:00
Lux Partners Limited is not the entity behind this work. Every place the old name stood — copyright lines, site footers, brand config, legal and compliance text — now names the company that actually holds it. lux.fund is the exception and is not renamed to match: that site belongs to Lux Assets Management LLC, a different company, and it says so. Two kinds of line were left for a person rather than rewritten. A dated news item recording that Lux Partners Limited was established in the Isle of Man is a historical fact, and renaming it would falsify the record. And a sentence asserting that the company is domiciled in the Isle of Man is a claim about a jurisdiction, not a name — an Inc or an LLC is not an Isle of Man company, so swapping the name there would assert something untrue. Co-authored-by: Hanzo Dev <[email protected]>
393 lines
9.9 KiB
Go
393 lines
9.9 KiB
Go
// Copyright (c) 2025 Lux Industries Inc
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package evm
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestNewBlobIndexer tests BlobIndexer creation
|
|
func TestNewBlobIndexer(t *testing.T) {
|
|
adapter := &Adapter{}
|
|
storageURI := "https://blob-storage.lux.network"
|
|
indexer := NewBlobIndexer(adapter, nil, storageURI)
|
|
|
|
if indexer == nil {
|
|
t.Fatal("NewBlobIndexer returned nil")
|
|
}
|
|
if indexer.adapter != adapter {
|
|
t.Error("adapter not set correctly")
|
|
}
|
|
if indexer.storageURI != storageURI {
|
|
t.Errorf("storageURI = %s, want %s", indexer.storageURI, storageURI)
|
|
}
|
|
}
|
|
|
|
// TestBlobConstants tests EIP-4844 constants
|
|
func TestBlobConstants(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
got uint64
|
|
want uint64
|
|
}{
|
|
{"BlobTxType", BlobTxType, 3},
|
|
{"BlobGasPerBlob", BlobGasPerBlob, 131072},
|
|
{"MaxBlobsPerBlock", MaxBlobsPerBlock, 6},
|
|
{"BlobFieldElementSize", BlobFieldElementSize, 32},
|
|
{"BlobSize", BlobSize, 131072},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.got != uint64(tt.want) {
|
|
t.Errorf("%s = %d, want %d", tt.name, tt.got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Verify BlobSize calculation
|
|
expectedSize := 4096 * BlobFieldElementSize
|
|
if BlobSize != expectedSize {
|
|
t.Errorf("BlobSize = %d, want %d (4096 * 32)", BlobSize, expectedSize)
|
|
}
|
|
}
|
|
|
|
// TestBlobTransactionSerialization tests BlobTransaction JSON serialization
|
|
func TestBlobTransactionSerializationComplete(t *testing.T) {
|
|
tx := BlobTransaction{
|
|
Hash: "0xblobtx123",
|
|
BlockNumber: 1000,
|
|
BlockHash: "0xblock123",
|
|
From: "0xfrom",
|
|
To: "0xto",
|
|
Value: "1000000000000000000",
|
|
MaxFeePerBlobGas: "1000000000",
|
|
BlobGasUsed: 262144, // 2 blobs
|
|
BlobGasPrice: "100",
|
|
BlobVersionedHashes: []string{"0xhash1", "0xhash2"},
|
|
BlobCount: 2,
|
|
TransactionIndex: 5,
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
data, err := json.Marshal(tx)
|
|
if err != nil {
|
|
t.Fatalf("Marshal failed: %v", err)
|
|
}
|
|
|
|
var decoded BlobTransaction
|
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
|
t.Fatalf("Unmarshal failed: %v", err)
|
|
}
|
|
|
|
if decoded.BlobCount != 2 {
|
|
t.Errorf("BlobCount = %d, want 2", decoded.BlobCount)
|
|
}
|
|
if decoded.BlobGasUsed != 262144 {
|
|
t.Errorf("BlobGasUsed = %d, want 262144", decoded.BlobGasUsed)
|
|
}
|
|
if len(decoded.BlobVersionedHashes) != 2 {
|
|
t.Errorf("BlobVersionedHashes length = %d, want 2", len(decoded.BlobVersionedHashes))
|
|
}
|
|
if decoded.TransactionIndex != 5 {
|
|
t.Errorf("TransactionIndex = %d, want 5", decoded.TransactionIndex)
|
|
}
|
|
}
|
|
|
|
// TestBlobDataSerialization tests BlobData JSON serialization
|
|
func TestBlobDataSerialization(t *testing.T) {
|
|
blob := BlobData{
|
|
VersionedHash: "0x01hash123",
|
|
Commitment: "0xcommitment",
|
|
Proof: "0xproof",
|
|
Data: "0xblobdata...",
|
|
Size: BlobSize,
|
|
TransactionHash: "0xtx123",
|
|
BlockNumber: 1000,
|
|
BlobIndex: 0,
|
|
StorageURI: "https://blob-storage.lux.network/0x01hash123",
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
data, err := json.Marshal(blob)
|
|
if err != nil {
|
|
t.Fatalf("Marshal failed: %v", err)
|
|
}
|
|
|
|
var decoded BlobData
|
|
if err := json.Unmarshal(data, &decoded); err != nil {
|
|
t.Fatalf("Unmarshal failed: %v", err)
|
|
}
|
|
|
|
if decoded.Size != BlobSize {
|
|
t.Errorf("Size = %d, want %d", decoded.Size, BlobSize)
|
|
}
|
|
if decoded.BlobIndex != 0 {
|
|
t.Errorf("BlobIndex = %d, want 0", decoded.BlobIndex)
|
|
}
|
|
if decoded.StorageURI == "" {
|
|
t.Error("StorageURI should not be empty")
|
|
}
|
|
}
|
|
|
|
// TestBlobGasCalculation tests blob gas calculation
|
|
func TestBlobGasCalculation(t *testing.T) {
|
|
tests := []struct {
|
|
blobCount int
|
|
expected uint64
|
|
}{
|
|
{1, BlobGasPerBlob},
|
|
{2, 2 * BlobGasPerBlob},
|
|
{3, 3 * BlobGasPerBlob},
|
|
{6, 6 * BlobGasPerBlob}, // Max blobs per block
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
gasUsed := uint64(tt.blobCount) * BlobGasPerBlob
|
|
if gasUsed != tt.expected {
|
|
t.Errorf("gas for %d blobs = %d, want %d", tt.blobCount, gasUsed, tt.expected)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestVersionedHashFormat tests versioned hash format
|
|
func TestVersionedHashFormat(t *testing.T) {
|
|
// EIP-4844 versioned hashes start with 0x01
|
|
tests := []struct {
|
|
hash string
|
|
valid bool
|
|
}{
|
|
{"0x01abcdef1234567890abcdef1234567890abcdef1234567890abcdef12345678", true},
|
|
{"0x00abcdef1234567890abcdef1234567890abcdef1234567890abcdef12345678", false}, // Wrong version
|
|
{"0x01", false}, // Too short
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
isValid := len(tt.hash) == 66 && tt.hash[:4] == "0x01"
|
|
if isValid != tt.valid {
|
|
t.Errorf("hash %s validity = %v, want %v", tt.hash[:10], isValid, tt.valid)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestFakeExponential tests blob gas price calculation
|
|
func TestFakeExponential(t *testing.T) {
|
|
tests := []struct {
|
|
excessBlobGas uint64
|
|
description string
|
|
}{
|
|
{0, "zero excess"},
|
|
{131072, "one blob worth"},
|
|
{262144, "two blobs worth"},
|
|
{1000000, "high excess"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.description, func(t *testing.T) {
|
|
price := fakeExponential(tt.excessBlobGas)
|
|
if price == "" {
|
|
t.Error("price should not be empty")
|
|
}
|
|
if price == "0" {
|
|
t.Error("price should be at least 1")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestFakeExponentialZeroExcess tests price at zero excess
|
|
func TestFakeExponentialZeroExcess(t *testing.T) {
|
|
price := fakeExponential(0)
|
|
|
|
if price != "1" {
|
|
t.Errorf("price at zero excess = %s, want 1", price)
|
|
}
|
|
}
|
|
|
|
// TestBlobGasUsedPerBlob tests gas used per blob calculation
|
|
func TestBlobGasUsedPerBlob(t *testing.T) {
|
|
// Each blob uses exactly 2^17 = 131072 gas
|
|
if BlobGasPerBlob != 131072 {
|
|
t.Errorf("BlobGasPerBlob = %d, want 131072", BlobGasPerBlob)
|
|
}
|
|
|
|
// 2^17 calculation
|
|
expected := uint64(1 << 17)
|
|
if BlobGasPerBlob != expected {
|
|
t.Errorf("BlobGasPerBlob = %d, want 2^17 = %d", BlobGasPerBlob, expected)
|
|
}
|
|
}
|
|
|
|
// TestBlobSizeCalculation tests blob size
|
|
func TestBlobSizeCalculation(t *testing.T) {
|
|
// Blob size = 4096 field elements * 32 bytes
|
|
expected := 4096 * 32
|
|
if BlobSize != expected {
|
|
t.Errorf("BlobSize = %d, want %d", BlobSize, expected)
|
|
}
|
|
|
|
// Should be 128KB
|
|
if BlobSize != 131072 {
|
|
t.Errorf("BlobSize = %d, want 131072 (128KB)", BlobSize)
|
|
}
|
|
}
|
|
|
|
// TestMaxBlobsPerBlockTarget tests max blobs per block
|
|
func TestMaxBlobsPerBlockTarget(t *testing.T) {
|
|
// EIP-4844 targets 3 blobs per block, max 6
|
|
if MaxBlobsPerBlock != 6 {
|
|
t.Errorf("MaxBlobsPerBlock = %d, want 6", MaxBlobsPerBlock)
|
|
}
|
|
|
|
// Max blob gas per block = 6 * 131072 = 786432
|
|
maxBlobGas := MaxBlobsPerBlock * BlobGasPerBlob
|
|
expectedMax := uint64(786432)
|
|
if uint64(maxBlobGas) != expectedMax {
|
|
t.Errorf("max blob gas = %d, want %d", maxBlobGas, expectedMax)
|
|
}
|
|
}
|
|
|
|
// TestBlobTransactionFields tests all BlobTransaction fields
|
|
func TestBlobTransactionFields(t *testing.T) {
|
|
now := time.Now()
|
|
tx := BlobTransaction{
|
|
Hash: "0xhash",
|
|
BlockNumber: 100,
|
|
BlockHash: "0xblockhash",
|
|
From: "0xfrom",
|
|
To: "0xto",
|
|
Value: "0",
|
|
MaxFeePerBlobGas: "1000000000",
|
|
BlobGasUsed: 131072,
|
|
BlobGasPrice: "1",
|
|
BlobVersionedHashes: []string{"0x01hash"},
|
|
BlobCount: 1,
|
|
TransactionIndex: 0,
|
|
Timestamp: now,
|
|
}
|
|
|
|
// Verify all fields are set
|
|
if tx.Hash == "" {
|
|
t.Error("Hash should not be empty")
|
|
}
|
|
if tx.BlockNumber == 0 {
|
|
t.Error("BlockNumber should not be 0")
|
|
}
|
|
if tx.From == "" {
|
|
t.Error("From should not be empty")
|
|
}
|
|
if tx.MaxFeePerBlobGas == "" {
|
|
t.Error("MaxFeePerBlobGas should not be empty")
|
|
}
|
|
if tx.BlobGasUsed == 0 {
|
|
t.Error("BlobGasUsed should not be 0")
|
|
}
|
|
if tx.BlobGasPrice == "" {
|
|
t.Error("BlobGasPrice should not be empty")
|
|
}
|
|
if len(tx.BlobVersionedHashes) == 0 {
|
|
t.Error("BlobVersionedHashes should not be empty")
|
|
}
|
|
if tx.BlobCount == 0 {
|
|
t.Error("BlobCount should not be 0")
|
|
}
|
|
if tx.Timestamp.IsZero() {
|
|
t.Error("Timestamp should not be zero")
|
|
}
|
|
}
|
|
|
|
// TestBlobDataFields tests all BlobData fields
|
|
func TestBlobDataFields(t *testing.T) {
|
|
now := time.Now()
|
|
blob := BlobData{
|
|
VersionedHash: "0x01hash",
|
|
Commitment: "0xcommitment",
|
|
Proof: "0xproof",
|
|
Data: "0xdata",
|
|
Size: BlobSize,
|
|
TransactionHash: "0xtx",
|
|
BlockNumber: 100,
|
|
BlobIndex: 0,
|
|
StorageURI: "https://storage/hash",
|
|
Timestamp: now,
|
|
}
|
|
|
|
if blob.VersionedHash == "" {
|
|
t.Error("VersionedHash should not be empty")
|
|
}
|
|
if blob.Size != BlobSize {
|
|
t.Errorf("Size = %d, want %d", blob.Size, BlobSize)
|
|
}
|
|
if blob.TransactionHash == "" {
|
|
t.Error("TransactionHash should not be empty")
|
|
}
|
|
}
|
|
|
|
// Benchmarks
|
|
|
|
func BenchmarkBlobTransactionMarshal(b *testing.B) {
|
|
tx := BlobTransaction{
|
|
Hash: "0xblobtx123",
|
|
BlockNumber: 1000,
|
|
BlockHash: "0xblock123",
|
|
From: "0xfrom",
|
|
To: "0xto",
|
|
Value: "1000000000000000000",
|
|
MaxFeePerBlobGas: "1000000000",
|
|
BlobGasUsed: 262144,
|
|
BlobGasPrice: "100",
|
|
BlobVersionedHashes: []string{"0xhash1", "0xhash2"},
|
|
BlobCount: 2,
|
|
TransactionIndex: 5,
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
json.Marshal(tx)
|
|
}
|
|
}
|
|
|
|
func BenchmarkBlobDataMarshal(b *testing.B) {
|
|
blob := BlobData{
|
|
VersionedHash: "0x01hash123",
|
|
Commitment: "0xcommitment",
|
|
Proof: "0xproof",
|
|
Data: "0xblobdata",
|
|
Size: BlobSize,
|
|
TransactionHash: "0xtx123",
|
|
BlockNumber: 1000,
|
|
BlobIndex: 0,
|
|
StorageURI: "https://blob-storage.lux.network/0x01hash123",
|
|
Timestamp: time.Now(),
|
|
}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
json.Marshal(blob)
|
|
}
|
|
}
|
|
|
|
func BenchmarkFakeExponential(b *testing.B) {
|
|
excessValues := []uint64{0, 131072, 262144, 1000000}
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
for _, excess := range excessValues {
|
|
fakeExponential(excess)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkBlobGasCalculation(b *testing.B) {
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
for blobCount := 1; blobCount <= MaxBlobsPerBlock; blobCount++ {
|
|
_ = uint64(blobCount) * BlobGasPerBlob
|
|
}
|
|
}
|
|
}
|