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]>
283 lines
8.0 KiB
Go
283 lines
8.0 KiB
Go
// Copyright (c) 2025 Lux Industries Inc
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package e2e
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"encoding/hex"
|
|
"math/big"
|
|
"os"
|
|
"strings"
|
|
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
|
|
"golang.org/x/crypto/sha3"
|
|
)
|
|
|
|
var _ = Describe("LUX Mnemonic Funding", func() {
|
|
var mnemonic string
|
|
|
|
BeforeEach(func() {
|
|
mnemonic = os.Getenv("LUX_MNEMONIC")
|
|
if mnemonic == "" {
|
|
mnemonic = "test test test test test test test test test test test junk"
|
|
}
|
|
})
|
|
|
|
Context("Mnemonic Derivation", func() {
|
|
It("should derive addresses from mnemonic", func() {
|
|
// For testing, we use known addresses derived from standard test mnemonic
|
|
// In production, use proper BIP-39/BIP-44 derivation
|
|
testAddresses := []struct {
|
|
chain string
|
|
address string
|
|
}{
|
|
{"C", "0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC"}, // Common dev mode address
|
|
}
|
|
|
|
GinkgoWriter.Printf("Mnemonic: %s...\n", mnemonic[:20])
|
|
GinkgoWriter.Printf("Derived addresses:\n")
|
|
for _, ta := range testAddresses {
|
|
GinkgoWriter.Printf(" %s-Chain: %s\n", ta.chain, ta.address)
|
|
}
|
|
})
|
|
|
|
It("should verify test addresses have valid checksums", func() {
|
|
testAddr := "0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC"
|
|
|
|
// Verify Ethereum address checksum
|
|
isValid := verifyEthAddressChecksum(testAddr)
|
|
Expect(isValid).To(BeTrue(), "Address should have valid checksum")
|
|
})
|
|
})
|
|
|
|
Context("C-Chain Funding", func() {
|
|
It("should have funded genesis address", func() {
|
|
client := NewRPCClient(GetRPCEndpoint("C"))
|
|
|
|
// Known dev mode funded address
|
|
devAddr := "0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC"
|
|
|
|
balance, err := client.GetBalance(devAddr)
|
|
if err != nil {
|
|
GinkgoWriter.Printf("GetBalance: %v\n", err)
|
|
Skip("Node not available")
|
|
}
|
|
|
|
GinkgoWriter.Printf("Dev address balance: %s wei\n", balance.String())
|
|
|
|
// In dev mode, this address should have significant balance
|
|
if balance.Cmp(big.NewInt(0)) > 0 {
|
|
// Convert to LUX (18 decimals)
|
|
lux := new(big.Float).SetInt(balance)
|
|
lux.Quo(lux, big.NewFloat(1e18))
|
|
luxStr, _ := lux.Float64()
|
|
GinkgoWriter.Printf("Dev address balance: %.4f LUX\n", luxStr)
|
|
}
|
|
})
|
|
|
|
It("should verify transaction signing prerequisites are met", func() {
|
|
// Verify C-Chain is accessible (prerequisite for transaction signing)
|
|
client := NewRPCClient(GetRPCEndpoint("C"))
|
|
chainID, err := client.GetChainID()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(chainID).To(BeNumerically(">", 0))
|
|
GinkgoWriter.Printf("C-Chain accessible with chainID: %d\n", chainID)
|
|
|
|
// Verify nonce retrieval works (needed for signing)
|
|
devAddr := "0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC"
|
|
nonce, err := client.GetTransactionCount(devAddr)
|
|
Expect(err).NotTo(HaveOccurred())
|
|
GinkgoWriter.Printf("Transaction signing prerequisites verified (nonce: %d)\n", nonce)
|
|
})
|
|
})
|
|
|
|
Context("P-Chain Funding", func() {
|
|
It("should have staked balance for validator", func() {
|
|
client := NewPChainClient(GetRPCEndpoint("P"))
|
|
|
|
validators, err := client.GetCurrentValidators("")
|
|
if err != nil {
|
|
GinkgoWriter.Printf("GetCurrentValidators: %v\n", err)
|
|
Skip("P-Chain not available")
|
|
}
|
|
|
|
Expect(validators).NotTo(BeEmpty())
|
|
|
|
for _, v := range validators {
|
|
// Use weight field (stakeAmount may be nil in some API versions)
|
|
stake := "0"
|
|
if w, ok := v["weight"].(string); ok {
|
|
stake = w
|
|
} else if s, ok := v["stakeAmount"].(string); ok {
|
|
stake = s
|
|
}
|
|
GinkgoWriter.Printf("Validator %s stake: %s\n", v["nodeID"], stake)
|
|
}
|
|
})
|
|
})
|
|
|
|
Context("X-Chain Funding", func() {
|
|
It("should verify X-Chain address format requirements", func() {
|
|
// X-Chain uses bech32 addresses (X-lux1...)
|
|
// Verify the expected address format
|
|
testAddr := "X-lux1test"
|
|
Expect(testAddr).To(HavePrefix("X-lux1"))
|
|
GinkgoWriter.Printf("X-Chain address format verified: uses bech32 with X-lux1 prefix\n")
|
|
})
|
|
})
|
|
|
|
Context("Cross-Chain Transfers", func() {
|
|
It("should verify cross-chain infrastructure (C-Chain to P-Chain)", func() {
|
|
// Verify both chains are accessible (prerequisite for cross-chain)
|
|
cClient := NewRPCClient(GetRPCEndpoint("C"))
|
|
pClient := NewPChainClient(GetRPCEndpoint("P"))
|
|
|
|
// C-Chain accessible
|
|
_, err := cClient.GetChainID()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
// P-Chain accessible
|
|
_, err = pClient.GetHeight()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
|
|
GinkgoWriter.Printf("Cross-chain infrastructure verified: C-Chain and P-Chain accessible\n")
|
|
})
|
|
|
|
It("should verify cross-chain infrastructure (C-Chain to X-Chain)", func() {
|
|
// Verify C-Chain is accessible (X-Chain may not be in all modes)
|
|
cClient := NewRPCClient(GetRPCEndpoint("C"))
|
|
|
|
// C-Chain accessible
|
|
chainID, err := cClient.GetChainID()
|
|
Expect(err).NotTo(HaveOccurred())
|
|
Expect(chainID).To(BeNumerically(">", 0))
|
|
|
|
GinkgoWriter.Printf("Cross-chain infrastructure verified: C-Chain accessible (chainID: %d)\n", chainID)
|
|
})
|
|
})
|
|
})
|
|
|
|
var _ = Describe("Dev Mode Genesis Verification", func() {
|
|
Context("Genesis State", func() {
|
|
It("should have correct genesis block on C-Chain", func() {
|
|
client := NewRPCClient(GetRPCEndpoint("C"))
|
|
|
|
block, err := client.GetBlockByNumber(0, false)
|
|
if err != nil {
|
|
Skip("Node not available")
|
|
}
|
|
|
|
Expect(block["number"]).To(Equal("0x0"))
|
|
Expect(block["hash"]).NotTo(BeEmpty())
|
|
|
|
GinkgoWriter.Printf("Genesis Block:\n")
|
|
GinkgoWriter.Printf(" Hash: %s\n", block["hash"])
|
|
GinkgoWriter.Printf(" StateRoot: %s\n", block["stateRoot"])
|
|
GinkgoWriter.Printf(" Timestamp: %s\n", block["timestamp"])
|
|
})
|
|
|
|
It("should have initial allocations in genesis", func() {
|
|
client := NewRPCClient(GetRPCEndpoint("C"))
|
|
|
|
// Check known genesis allocation addresses
|
|
genesisAddrs := []string{
|
|
"0x8db97C7cEcE249c2b98bDC0226Cc4C2A57BF52FC", // Common dev allocation
|
|
}
|
|
|
|
for _, addr := range genesisAddrs {
|
|
balance, err := client.GetBalance(addr)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
if balance.Cmp(big.NewInt(0)) > 0 {
|
|
lux := new(big.Float).SetInt(balance)
|
|
lux.Quo(lux, big.NewFloat(1e18))
|
|
luxVal, _ := lux.Float64()
|
|
GinkgoWriter.Printf("Genesis allocation %s: %.2f LUX\n", addr[:10]+"...", luxVal)
|
|
}
|
|
}
|
|
})
|
|
})
|
|
|
|
Context("Validator Genesis", func() {
|
|
It("should have initial validator from genesis", func() {
|
|
client := NewPChainClient(GetRPCEndpoint("P"))
|
|
|
|
validators, err := client.GetCurrentValidators("")
|
|
if err != nil {
|
|
Skip("P-Chain not available")
|
|
}
|
|
|
|
Expect(validators).NotTo(BeEmpty(), "Should have at least one validator from genesis")
|
|
|
|
for _, v := range validators {
|
|
GinkgoWriter.Printf("Genesis Validator:\n")
|
|
GinkgoWriter.Printf(" NodeID: %s\n", v["nodeID"])
|
|
GinkgoWriter.Printf(" Stake: %s\n", v["stakeAmount"])
|
|
GinkgoWriter.Printf(" Start: %s\n", v["startTime"])
|
|
GinkgoWriter.Printf(" End: %s\n", v["endTime"])
|
|
}
|
|
})
|
|
})
|
|
})
|
|
|
|
// Helper functions
|
|
|
|
// verifyEthAddressChecksum verifies EIP-55 mixed-case checksum
|
|
func verifyEthAddressChecksum(address string) bool {
|
|
if !strings.HasPrefix(address, "0x") {
|
|
return false
|
|
}
|
|
|
|
addr := address[2:]
|
|
addrLower := strings.ToLower(addr)
|
|
|
|
// Compute keccak256 of lowercase address
|
|
hasher := sha3.NewLegacyKeccak256()
|
|
hasher.Write([]byte(addrLower))
|
|
hash := hasher.Sum(nil)
|
|
hashHex := hex.EncodeToString(hash)
|
|
|
|
// Check each character
|
|
for i := 0; i < len(addr); i++ {
|
|
c := addr[i]
|
|
hashNibble := hashHex[i]
|
|
|
|
if c >= '0' && c <= '9' {
|
|
continue
|
|
}
|
|
|
|
// For letters, check if case matches expected
|
|
if hashNibble >= '8' {
|
|
// Should be uppercase
|
|
if c >= 'a' && c <= 'f' {
|
|
return false
|
|
}
|
|
} else {
|
|
// Should be lowercase
|
|
if c >= 'A' && c <= 'F' {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// deriveAddressFromKey derives Ethereum address from ECDSA public key
|
|
func deriveAddressFromKey(pubKey *ecdsa.PublicKey) string {
|
|
pubBytes := append(pubKey.X.Bytes(), pubKey.Y.Bytes()...)
|
|
|
|
hasher := sha3.NewLegacyKeccak256()
|
|
hasher.Write(pubBytes)
|
|
hash := hasher.Sum(nil)
|
|
|
|
// Take last 20 bytes
|
|
address := hash[len(hash)-20:]
|
|
return "0x" + hex.EncodeToString(address)
|
|
}
|