Files
dex/pkg/lx/bridge_test.go

1427 lines
40 KiB
Go

package lx
import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"math/big"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestNewEnhancedBridge tests bridge creation
func TestNewEnhancedBridge(t *testing.T) {
bridge := NewEnhancedBridge()
assert.NotNil(t, bridge)
assert.NotNil(t, bridge.CrossChainBridge)
assert.NotNil(t, bridge.Chains)
assert.NotNil(t, bridge.ActiveTransfers)
assert.NotNil(t, bridge.TransferHistory)
assert.NotNil(t, bridge.MultisigWallet)
assert.NotNil(t, bridge.FraudProofs)
assert.NotNil(t, bridge.BatchProcessor)
assert.NotNil(t, bridge.Metrics)
assert.NotNil(t, bridge.AlertManager)
// Check CrossChainBridge initialization
assert.NotNil(t, bridge.SupportedAssets)
assert.NotNil(t, bridge.PendingTransfers)
assert.NotNil(t, bridge.CompletedTransfers)
assert.NotNil(t, bridge.FailedTransfers)
assert.NotNil(t, bridge.LiquidityPools)
assert.Equal(t, 15, bridge.RequiredConfirmations)
assert.Equal(t, 24*time.Hour, bridge.ChallengePeriod)
}
// TestInitiateTransfer tests transfer initiation
func TestInitiateTransfer(t *testing.T) {
bridge := NewEnhancedBridge()
// Add test asset
asset := &BridgeAsset{
Symbol: "USDC",
Name: "USD Coin",
Decimals: 6,
MinTransfer: big.NewInt(1000000), // 1 USDC
MaxTransfer: big.NewInt(10000000000), // 10000 USDC
DailyLimit: big.NewInt(100000000000), // 100000 USDC
DailyVolume: big.NewInt(0),
LastReset: time.Now(),
}
bridge.SupportedAssets["USDC"] = asset
// Add test chains
bridge.Chains["1"] = &ChainConfig{
ChainID: "1",
Name: "Ethereum",
Type: ChainTypeEVM,
RPCEndpoint: "https://eth.example.com",
ContractAddress: "0x123",
Confirmations: 12,
Active: true,
}
bridge.Chains["2"] = &ChainConfig{
ChainID: "2",
Name: "Lux",
Type: ChainTypeLux,
RPCEndpoint: "https://lux.example.com",
ContractAddress: "0x456",
Confirmations: 6,
Active: true,
}
// Add liquidity to destination chain for transfers
_, err := bridge.AddLiquidity("2", "USDC", "liquidityProvider", big.NewInt(100000000000))
require.NoError(t, err)
t.Run("ValidTransfer", func(t *testing.T) {
ctx := context.Background()
from := "0xFromAddress"
to := "0xToAddress"
amount := big.NewInt(5000000000) // 5000 USDC
transfer, err := bridge.InitiateTransfer(ctx, "USDC", amount, "1", "2", from, to)
require.NoError(t, err)
assert.NotNil(t, transfer)
assert.NotEmpty(t, transfer.ID)
// Verify transfer was created
bridge.mu.RLock()
activeTransfer, exists := bridge.ActiveTransfers[transfer.ID]
bridge.mu.RUnlock()
assert.True(t, exists)
assert.Equal(t, "USDC", activeTransfer.Asset)
assert.Equal(t, amount, activeTransfer.Amount)
assert.Equal(t, "1", activeTransfer.SourceChain)
assert.Equal(t, "2", activeTransfer.DestChain)
assert.Equal(t, from, activeTransfer.SourceAddress)
assert.Equal(t, to, activeTransfer.DestAddress)
assert.Equal(t, BridgeStatusPending, activeTransfer.Status)
})
t.Run("AmountBelowMinimum", func(t *testing.T) {
ctx := context.Background()
amount := big.NewInt(100) // Below minimum
_, err := bridge.InitiateTransfer(ctx, "USDC", amount, "1", "2", "from", "to")
assert.Error(t, err)
assert.Contains(t, err.Error(), "below minimum")
})
t.Run("AmountAboveMaximum", func(t *testing.T) {
ctx := context.Background()
amount := big.NewInt(100000000000) // Above maximum
_, err := bridge.InitiateTransfer(ctx, "USDC", amount, "1", "2", "from", "to")
assert.Error(t, err)
assert.Contains(t, err.Error(), "exceeds maximum")
})
t.Run("UnsupportedAsset", func(t *testing.T) {
ctx := context.Background()
amount := big.NewInt(1000000)
_, err := bridge.InitiateTransfer(ctx, "UNKNOWN", amount, "1", "2", "from", "to")
assert.Error(t, err)
assert.Contains(t, err.Error(), "not supported")
})
t.Run("InactiveChain", func(t *testing.T) {
// Deactivate destination chain
bridge.Chains["2"].Active = false
ctx := context.Background()
amount := big.NewInt(5000000000)
_, err := bridge.InitiateTransfer(ctx, "USDC", amount, "1", "2", "from", "to")
assert.Error(t, err)
// Reactivate for other tests
bridge.Chains["2"].Active = true
})
}
// TestValidateTransfer tests transfer validation
func TestValidateTransfer(t *testing.T) {
bridge := createTestBridgeWithAssets(t)
// Add test validators BEFORE initiating transfer to avoid race with notifyValidators goroutine
for i := 0; i < 3; i++ {
validator := &BridgeValidator{
Address: "validator" + string(rune('1'+i)),
Stake: big.NewInt(1000000),
Active: true,
JoinedAt: time.Now(),
}
// Generate a key pair for the validator
privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
validator.PublicKey = &privateKey.PublicKey
bridge.BridgeValidators = append(bridge.BridgeValidators, validator)
}
// Create a transfer after validators are set up
ctx := context.Background()
from := "0xFromAddress"
to := "0xToAddress"
amount := big.NewInt(5000000000)
transfer, err := bridge.InitiateTransfer(ctx, "USDC", amount, "1", "2", from, to)
require.NoError(t, err)
t.Run("ValidValidation", func(t *testing.T) {
validatorAddr := "validator1"
signature := []byte("mock-signature")
err := bridge.ValidateTransfer(transfer.ID, validatorAddr, signature)
assert.NoError(t, err)
// Check validation was recorded
bridge.mu.RLock()
activeTransfer := bridge.ActiveTransfers[transfer.ID]
bridge.mu.RUnlock()
assert.NotNil(t, activeTransfer.Validators[validatorAddr])
assert.Equal(t, signature, activeTransfer.Validators[validatorAddr].Signature)
})
t.Run("InvalidTransferID", func(t *testing.T) {
err := bridge.ValidateTransfer("invalid-id", "validator1", []byte("sig"))
assert.Error(t, err)
assert.Contains(t, err.Error(), "transfer not found")
})
t.Run("DuplicateValidation", func(t *testing.T) {
validatorAddr := "validator1"
signature := []byte("mock-signature-2")
err := bridge.ValidateTransfer(transfer.ID, validatorAddr, signature)
assert.Error(t, err)
assert.Contains(t, err.Error(), "already validated this transfer")
})
}
// TestAddLiquidity tests liquidity pool operations
func TestAddLiquidity(t *testing.T) {
bridge := createTestBridgeWithAssets(t)
t.Run("AddValidLiquidity", func(t *testing.T) {
provider := "0xLiquidityProvider"
asset := "USDC"
chainID := "1"
amount := big.NewInt(10000000000) // 10000 USDC
lp, err := bridge.AddLiquidity(chainID, asset, provider, amount)
assert.NoError(t, err)
assert.NotNil(t, lp)
// Check liquidity was added
bridge.mu.RLock()
poolKey := chainID + ":" + asset
pool, exists := bridge.LiquidityPools[poolKey]
bridge.mu.RUnlock()
assert.True(t, exists)
assert.Equal(t, amount, pool.TotalLiquidity)
assert.Equal(t, amount, pool.Providers[provider].Amount)
})
t.Run("AddMoreLiquidity", func(t *testing.T) {
provider := "0xLiquidityProvider"
asset := "USDC"
chainID := "1"
additionalAmount := big.NewInt(5000000000) // 5000 USDC
lp, err := bridge.AddLiquidity(chainID, asset, provider, additionalAmount)
assert.NoError(t, err)
assert.NotNil(t, lp)
// Check liquidity was increased
bridge.mu.RLock()
poolKey := chainID + ":" + asset
pool := bridge.LiquidityPools[poolKey]
bridge.mu.RUnlock()
expectedTotal := big.NewInt(15000000000)
assert.Equal(t, expectedTotal, pool.TotalLiquidity)
assert.Equal(t, expectedTotal, pool.Providers[provider].Amount)
})
}
// TestRemoveLiquidity tests liquidity removal
func TestRemoveLiquidity(t *testing.T) {
bridge := createTestBridgeWithAssets(t)
// First add liquidity
provider := "0xLiquidityProvider"
asset := "USDC"
chainID := "1"
initialAmount := big.NewInt(10000000000) // 10000 USDC
_, err := bridge.AddLiquidity(chainID, asset, provider, initialAmount)
require.NoError(t, err)
t.Run("RemovePartialLiquidity", func(t *testing.T) {
removeAmount := big.NewInt(3000000000) // 3000 USDC
withdrawAmount, err := bridge.RemoveLiquidity(chainID, asset, provider, removeAmount)
assert.NoError(t, err)
assert.NotNil(t, withdrawAmount)
bridge.mu.RLock()
poolKey := chainID + ":" + asset
pool := bridge.LiquidityPools[poolKey]
bridge.mu.RUnlock()
expectedRemaining := big.NewInt(7000000000)
assert.Equal(t, expectedRemaining, pool.TotalLiquidity)
assert.Equal(t, expectedRemaining, pool.Providers[provider].Amount)
})
t.Run("RemoveExcessLiquidity", func(t *testing.T) {
removeAmount := big.NewInt(20000000000) // More than available
_, err := bridge.RemoveLiquidity(chainID, asset, provider, removeAmount)
assert.Error(t, err)
assert.Contains(t, err.Error(), "insufficient")
})
}
// TestSubmitFraudProof tests fraud proof submission
func TestSubmitFraudProof(t *testing.T) {
bridge := createTestBridgeWithAssets(t)
ctx := context.Background()
// Create a transfer
from := "0xFromAddress"
to := "0xToAddress"
amount := big.NewInt(5000000000)
transfer, err := bridge.InitiateTransfer(ctx, "USDC", amount, "1", "2", from, to)
require.NoError(t, err)
t.Run("SubmitValidFraudProof", func(t *testing.T) {
reporter := "0xReporter"
evidence := []byte("fraud-evidence")
err := bridge.SubmitFraudProof(transfer.ID, "InvalidSignature", evidence, reporter)
assert.NoError(t, err)
// Check fraud proof was recorded
bridge.mu.RLock()
proof, exists := bridge.FraudProofs[transfer.ID]
bridge.mu.RUnlock()
assert.True(t, exists)
assert.Equal(t, transfer.ID, proof.TransferID)
assert.Equal(t, "InvalidSignature", proof.ProofType)
assert.Equal(t, evidence, proof.Evidence)
assert.Equal(t, reporter, proof.Submitter)
})
t.Run("SubmitForInvalidTransfer", func(t *testing.T) {
err := bridge.SubmitFraudProof("invalid-id", "InvalidSignature", []byte("evidence"), "reporter")
assert.Error(t, err)
assert.Contains(t, err.Error(), "transfer not found")
})
}
// TestBridgeStructs tests bridge data structures
func TestBridgeStructs(t *testing.T) {
t.Run("BridgeAsset", func(t *testing.T) {
asset := &BridgeAsset{
Symbol: "USDC",
Name: "USD Coin",
Decimals: 6,
MinTransfer: big.NewInt(1000000),
MaxTransfer: big.NewInt(10000000000),
DailyLimit: big.NewInt(100000000000),
DailyVolume: big.NewInt(0),
LastReset: time.Now(),
Paused: false,
}
assert.Equal(t, "USDC", asset.Symbol)
assert.Equal(t, uint8(6), asset.Decimals)
assert.False(t, asset.Paused)
})
t.Run("BridgeTransfer", func(t *testing.T) {
transfer := &BridgeTransfer{
ID: "transfer-123",
Asset: "USDC",
Amount: big.NewInt(5000000000),
Fee: big.NewInt(5000000),
SourceChain: "1",
DestChain: "2",
SourceAddress: "0xFrom",
DestAddress: "0xTo",
Status: BridgeStatusPending,
Validators: make(map[string]*BridgeSignature),
Nonce: 1,
InitiatedAt: time.Now(),
}
assert.Equal(t, "transfer-123", transfer.ID)
assert.Equal(t, BridgeStatusPending, transfer.Status)
assert.Equal(t, uint64(1), transfer.Nonce)
})
t.Run("ChainConfig", func(t *testing.T) {
config := &ChainConfig{
ChainID: "1",
Name: "Ethereum",
Type: ChainTypeEVM,
RPCEndpoint: "https://eth.example.com",
ContractAddress: "0x123",
Confirmations: 12,
BlockTime: 15 * time.Second,
GasPrice: big.NewInt(30000000000),
Active: true,
}
assert.Equal(t, "1", config.ChainID)
assert.Equal(t, ChainTypeEVM, config.Type)
assert.True(t, config.Active)
})
t.Run("BridgeValidator", func(t *testing.T) {
validator := &BridgeValidator{
Address: "0xValidator",
Stake: big.NewInt(1000000),
Active: true,
Slashed: false,
JoinedAt: time.Now(),
}
assert.Equal(t, "0xValidator", validator.Address)
assert.True(t, validator.Active)
assert.False(t, validator.Slashed)
})
t.Run("MultisigBridge", func(t *testing.T) {
multisig := &MultisigBridge{
RequiredSigs: 2,
TotalSigners: 3,
Signers: make(map[string]*BridgeSigner),
PendingTxs: make(map[string]*MultisigTx),
ExecutedTxs: make(map[string]*MultisigTx),
TimeoutDuration: 24 * time.Hour,
}
assert.Equal(t, 2, multisig.RequiredSigs)
assert.Equal(t, 3, multisig.TotalSigners)
assert.Equal(t, 24*time.Hour, multisig.TimeoutDuration)
})
t.Run("BatchBridgeProcessor", func(t *testing.T) {
batch := &BatchBridgeProcessor{
BatchSize: 100,
BatchInterval: 10 * time.Second,
PendingBatch: make([]*BridgeTransfer, 0),
}
assert.Equal(t, 100, batch.BatchSize)
assert.Equal(t, 10*time.Second, batch.BatchInterval)
})
t.Run("BridgeMetrics", func(t *testing.T) {
metrics := &BridgeMetrics{
TotalTransfers: 1000,
TotalVolume: big.NewInt(1000000000000),
AverageTime: 5 * time.Minute,
SuccessRate: 0.99,
ActiveValidators: 10,
TotalLiquidity: big.NewInt(50000000000000),
DailyVolume: make(map[string]*big.Int),
}
assert.Equal(t, uint64(1000), metrics.TotalTransfers)
assert.Equal(t, 0.99, metrics.SuccessRate)
assert.Equal(t, 10, metrics.ActiveValidators)
})
}
// TestConcurrentBridgeOperations tests concurrent bridge operations
func TestConcurrentBridgeOperations(t *testing.T) {
bridge := createTestBridgeWithAssets(t)
ctx := context.Background()
// Add initial liquidity to destination chain
_, err := bridge.AddLiquidity("2", "USDC", "provider1", big.NewInt(100000000000))
require.NoError(t, err)
// Run concurrent transfers
done := make(chan bool, 10)
for i := 0; i < 10; i++ {
go func(index int) {
from := "0xFrom" + string(rune('0'+index))
to := "0xTo" + string(rune('0'+index))
amount := big.NewInt(int64(1000000000 + index*100000000))
_, err := bridge.InitiateTransfer(ctx, "USDC", amount, "1", "2", from, to)
assert.NoError(t, err)
done <- true
}(i)
}
// Wait for all goroutines to complete
for i := 0; i < 10; i++ {
<-done
}
// Verify all transfers were created
bridge.mu.RLock()
transferCount := len(bridge.ActiveTransfers)
bridge.mu.RUnlock()
assert.Equal(t, 10, transferCount)
}
// Helper function to create a test bridge with assets
func createTestBridgeWithAssets(t testing.TB) *EnhancedBridge {
bridge := NewEnhancedBridge()
// Add test asset
asset := &BridgeAsset{
Symbol: "USDC",
Name: "USD Coin",
Decimals: 6,
MinTransfer: big.NewInt(1000000),
MaxTransfer: big.NewInt(100000000000),
DailyLimit: big.NewInt(1000000000000),
DailyVolume: big.NewInt(0),
LastReset: time.Now(),
}
bridge.SupportedAssets["USDC"] = asset
// Add test chains
bridge.Chains["1"] = &ChainConfig{
ChainID: "1",
Name: "Ethereum",
Type: ChainTypeEVM,
RPCEndpoint: "https://eth.example.com",
ContractAddress: "0x123",
Confirmations: 12,
Active: true,
}
bridge.Chains["2"] = &ChainConfig{
ChainID: "2",
Name: "Lux",
Type: ChainTypeLux,
RPCEndpoint: "https://lux.example.com",
ContractAddress: "0x456",
Confirmations: 6,
Active: true,
}
// Add initial liquidity to destination chain
bridge.AddLiquidity("2", "USDC", "initialProvider", big.NewInt(100000000000))
return bridge
}
// TestFindValidator tests the findValidator function (0% coverage target)
func TestFindValidator(t *testing.T) {
bridge := NewEnhancedBridge()
// Add test validators
privateKey1, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
privateKey2, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
validator1 := &BridgeValidator{
Address: "0xValidator1",
PublicKey: &privateKey1.PublicKey,
Stake: big.NewInt(1000000000000000000),
Active: true,
Slashed: false,
JoinedAt: time.Now(),
}
validator2 := &BridgeValidator{
Address: "0xValidator2",
PublicKey: &privateKey2.PublicKey,
Stake: big.NewInt(2000000000000000000),
Active: true,
Slashed: false,
JoinedAt: time.Now(),
}
bridge.BridgeValidators = append(bridge.BridgeValidators, validator1, validator2)
t.Run("FindExistingValidator", func(t *testing.T) {
found := bridge.findValidator("0xValidator1")
require.NotNil(t, found)
assert.Equal(t, "0xValidator1", found.Address)
assert.True(t, found.Active)
})
t.Run("FindSecondValidator", func(t *testing.T) {
found := bridge.findValidator("0xValidator2")
require.NotNil(t, found)
assert.Equal(t, "0xValidator2", found.Address)
assert.Equal(t, big.NewInt(2000000000000000000), found.Stake)
})
t.Run("FindNonExistentValidator", func(t *testing.T) {
found := bridge.findValidator("0xNonExistent")
assert.Nil(t, found)
})
t.Run("FindValidatorEmptyAddress", func(t *testing.T) {
found := bridge.findValidator("")
assert.Nil(t, found)
})
t.Run("FindValidatorEmptyList", func(t *testing.T) {
emptyBridge := NewEnhancedBridge()
found := emptyBridge.findValidator("0xAnyAddress")
assert.Nil(t, found)
})
}
// TestFindValidatorLocked tests the findValidatorLocked function
func TestFindValidatorLocked(t *testing.T) {
bridge := NewEnhancedBridge()
privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
validator := &BridgeValidator{
Address: "0xLockedValidator",
PublicKey: &privateKey.PublicKey,
Stake: big.NewInt(500000000000000000),
Active: true,
JoinedAt: time.Now(),
}
bridge.BridgeValidators = append(bridge.BridgeValidators, validator)
t.Run("FindWithManualLock", func(t *testing.T) {
bridge.mu.Lock()
found := bridge.findValidatorLocked("0xLockedValidator")
bridge.mu.Unlock()
require.NotNil(t, found)
assert.Equal(t, "0xLockedValidator", found.Address)
})
}
// TestFindTransfer tests the findTransfer function (0% coverage target)
func TestFindTransfer(t *testing.T) {
bridge := NewEnhancedBridge()
// Create transfers in different states
pendingTransfer := &BridgeTransfer{
ID: "pending-001",
Asset: "USDC",
Amount: big.NewInt(1000000),
SourceChain: "1",
DestChain: "2",
SourceAddress: "0xFrom",
DestAddress: "0xTo",
Status: BridgeStatusPending,
InitiatedAt: time.Now(),
Validators: make(map[string]*BridgeSignature),
}
completedTransfer := &BridgeTransfer{
ID: "completed-001",
Asset: "ETH",
Amount: big.NewInt(1000000000000000000),
SourceChain: "1",
DestChain: "2",
SourceAddress: "0xFrom",
DestAddress: "0xTo",
Status: BridgeStatusCompleted,
InitiatedAt: time.Now().Add(-1 * time.Hour),
CompletedAt: time.Now(),
Validators: make(map[string]*BridgeSignature),
}
failedTransfer := &BridgeTransfer{
ID: "failed-001",
Asset: "DAI",
Amount: big.NewInt(5000000000000000000),
SourceChain: "1",
DestChain: "2",
SourceAddress: "0xFrom",
DestAddress: "0xTo",
Status: BridgeStatusFailed,
InitiatedAt: time.Now().Add(-2 * time.Hour),
CompletedAt: time.Now().Add(-1 * time.Hour),
Validators: make(map[string]*BridgeSignature),
}
// Store transfers
bridge.PendingTransfers[pendingTransfer.ID] = pendingTransfer
bridge.CompletedTransfers[completedTransfer.ID] = completedTransfer
bridge.FailedTransfers[failedTransfer.ID] = failedTransfer
t.Run("FindPendingTransfer", func(t *testing.T) {
found := bridge.findTransfer("pending-001")
require.NotNil(t, found)
assert.Equal(t, "pending-001", found.ID)
assert.Equal(t, BridgeStatusPending, found.Status)
assert.Equal(t, "USDC", found.Asset)
})
t.Run("FindCompletedTransfer", func(t *testing.T) {
found := bridge.findTransfer("completed-001")
require.NotNil(t, found)
assert.Equal(t, "completed-001", found.ID)
assert.Equal(t, BridgeStatusCompleted, found.Status)
assert.Equal(t, "ETH", found.Asset)
})
t.Run("FindFailedTransfer", func(t *testing.T) {
found := bridge.findTransfer("failed-001")
require.NotNil(t, found)
assert.Equal(t, "failed-001", found.ID)
assert.Equal(t, BridgeStatusFailed, found.Status)
assert.Equal(t, "DAI", found.Asset)
})
t.Run("FindNonExistentTransfer", func(t *testing.T) {
found := bridge.findTransfer("non-existent-id")
assert.Nil(t, found)
})
t.Run("FindTransferEmptyID", func(t *testing.T) {
found := bridge.findTransfer("")
assert.Nil(t, found)
})
t.Run("FindTransferEmptyBridge", func(t *testing.T) {
emptyBridge := NewEnhancedBridge()
found := emptyBridge.findTransfer("any-id")
assert.Nil(t, found)
})
}
// TestFindTransferLocked tests the findTransferLocked function
func TestFindTransferLocked(t *testing.T) {
bridge := NewEnhancedBridge()
transfer := &BridgeTransfer{
ID: "locked-transfer-001",
Asset: "USDC",
Amount: big.NewInt(1000000),
Status: BridgeStatusPending,
InitiatedAt: time.Now(),
Validators: make(map[string]*BridgeSignature),
}
bridge.PendingTransfers[transfer.ID] = transfer
t.Run("FindWithManualLock", func(t *testing.T) {
bridge.mu.Lock()
found := bridge.findTransferLocked("locked-transfer-001")
bridge.mu.Unlock()
require.NotNil(t, found)
assert.Equal(t, "locked-transfer-001", found.ID)
})
t.Run("NotFoundWithLock", func(t *testing.T) {
bridge.mu.Lock()
found := bridge.findTransferLocked("non-existent")
bridge.mu.Unlock()
assert.Nil(t, found)
})
}
// TestGetBridgeStatus tests the GetBridgeStatus function (0% coverage target)
func TestGetBridgeStatus(t *testing.T) {
t.Run("DefaultBridgeStatus", func(t *testing.T) {
bridge := NewEnhancedBridge()
status := bridge.GetBridgeStatus()
require.NotNil(t, status)
assert.True(t, status["active"].(bool))
assert.Equal(t, 0, status["pending_transfers"].(int))
assert.Equal(t, uint64(0), status["total_transfers"].(uint64))
assert.Equal(t, "0", status["total_volume"].(string))
assert.Equal(t, "0", status["total_liquidity"].(string))
assert.Equal(t, 0, status["active_validators"].(int))
assert.Equal(t, 0, status["chains"].(int))
assert.Equal(t, 0, status["assets"].(int))
})
t.Run("BridgeStatusWithData", func(t *testing.T) {
bridge := createTestBridgeWithAssets(t)
// Add a pending transfer
transfer := &BridgeTransfer{
ID: "status-test-001",
Asset: "USDC",
Amount: big.NewInt(5000000000),
Status: BridgeStatusPending,
InitiatedAt: time.Now(),
Validators: make(map[string]*BridgeSignature),
}
bridge.PendingTransfers[transfer.ID] = transfer
// Set metrics
bridge.Metrics.TotalTransfers = 100
bridge.Metrics.TotalVolume = big.NewInt(1000000000000)
bridge.Metrics.TotalLiquidity = big.NewInt(500000000000)
bridge.Metrics.ActiveValidators = 10
status := bridge.GetBridgeStatus()
require.NotNil(t, status)
assert.True(t, status["active"].(bool))
assert.Equal(t, 1, status["pending_transfers"].(int))
assert.Equal(t, uint64(100), status["total_transfers"].(uint64))
assert.Equal(t, "1000000000000", status["total_volume"].(string))
assert.Equal(t, "500000000000", status["total_liquidity"].(string))
assert.Equal(t, 10, status["active_validators"].(int))
assert.Equal(t, 2, status["chains"].(int))
assert.Equal(t, 1, status["assets"].(int))
})
t.Run("BridgeStatusPaused", func(t *testing.T) {
bridge := NewEnhancedBridge()
bridge.EmergencyPaused = true
status := bridge.GetBridgeStatus()
assert.False(t, status["active"].(bool))
})
t.Run("BridgeStatusConcurrent", func(t *testing.T) {
bridge := NewEnhancedBridge()
done := make(chan bool, 10)
// Run concurrent GetBridgeStatus calls
for i := 0; i < 10; i++ {
go func() {
status := bridge.GetBridgeStatus()
assert.NotNil(t, status)
done <- true
}()
}
// Wait for all goroutines
for i := 0; i < 10; i++ {
<-done
}
})
}
// TestDistributeFees tests the distributeFees function (40% coverage target)
func TestDistributeFees(t *testing.T) {
bridge := NewEnhancedBridge()
t.Run("DistributeFeesEmptyPool", func(t *testing.T) {
pool := &BridgeLiquidityPool{
Asset: "USDC",
ChainID: "1",
TotalLiquidity: big.NewInt(0),
AvailableLiquidity: big.NewInt(0),
LockedLiquidity: big.NewInt(0),
Providers: make(map[string]*LiquidityProvider),
Fees: big.NewInt(0),
}
fee := big.NewInt(1000000) // 1 USDC fee
// Should not panic with empty providers
bridge.distributeFees(pool, fee)
// Fees should not be added to pool with no providers
assert.Equal(t, big.NewInt(0), pool.Fees)
})
t.Run("DistributeFeesSingleProvider", func(t *testing.T) {
pool := &BridgeLiquidityPool{
Asset: "USDC",
ChainID: "1",
TotalLiquidity: big.NewInt(10000000000),
AvailableLiquidity: big.NewInt(10000000000),
LockedLiquidity: big.NewInt(0),
Providers: make(map[string]*LiquidityProvider),
Fees: big.NewInt(0),
}
pool.Providers["provider1"] = &LiquidityProvider{
Address: "provider1",
Amount: big.NewInt(10000000000),
ShareTokens: big.NewInt(10000000000),
JoinedAt: time.Now(),
Rewards: big.NewInt(0),
}
fee := big.NewInt(1000000) // 1 USDC fee
bridge.distributeFees(pool, fee)
// Single provider should get all fees
assert.Equal(t, big.NewInt(1000000), pool.Providers["provider1"].Rewards)
assert.Equal(t, big.NewInt(1000000), pool.Fees)
})
t.Run("DistributeFeesMultipleProviders", func(t *testing.T) {
pool := &BridgeLiquidityPool{
Asset: "USDC",
ChainID: "1",
TotalLiquidity: big.NewInt(10000000000),
AvailableLiquidity: big.NewInt(10000000000),
LockedLiquidity: big.NewInt(0),
Providers: make(map[string]*LiquidityProvider),
Fees: big.NewInt(0),
}
// Provider 1 has 60% shares
pool.Providers["provider1"] = &LiquidityProvider{
Address: "provider1",
Amount: big.NewInt(6000000000),
ShareTokens: big.NewInt(6000000000),
JoinedAt: time.Now(),
Rewards: big.NewInt(0),
}
// Provider 2 has 40% shares
pool.Providers["provider2"] = &LiquidityProvider{
Address: "provider2",
Amount: big.NewInt(4000000000),
ShareTokens: big.NewInt(4000000000),
JoinedAt: time.Now(),
Rewards: big.NewInt(0),
}
fee := big.NewInt(10000000) // 10 USDC fee
bridge.distributeFees(pool, fee)
// Provider 1 should get 60% of fees (6M)
assert.Equal(t, big.NewInt(6000000), pool.Providers["provider1"].Rewards)
// Provider 2 should get 40% of fees (4M)
assert.Equal(t, big.NewInt(4000000), pool.Providers["provider2"].Rewards)
// Total fees should be added to pool
assert.Equal(t, big.NewInt(10000000), pool.Fees)
})
t.Run("DistributeFeesMultipleTimes", func(t *testing.T) {
pool := &BridgeLiquidityPool{
Asset: "USDC",
ChainID: "1",
TotalLiquidity: big.NewInt(10000000000),
AvailableLiquidity: big.NewInt(10000000000),
LockedLiquidity: big.NewInt(0),
Providers: make(map[string]*LiquidityProvider),
Fees: big.NewInt(0),
}
pool.Providers["provider1"] = &LiquidityProvider{
Address: "provider1",
Amount: big.NewInt(10000000000),
ShareTokens: big.NewInt(10000000000),
JoinedAt: time.Now(),
Rewards: big.NewInt(0),
}
// Distribute fees multiple times
bridge.distributeFees(pool, big.NewInt(1000000))
bridge.distributeFees(pool, big.NewInt(2000000))
bridge.distributeFees(pool, big.NewInt(3000000))
// Rewards should accumulate
assert.Equal(t, big.NewInt(6000000), pool.Providers["provider1"].Rewards)
assert.Equal(t, big.NewInt(6000000), pool.Fees)
})
t.Run("DistributeZeroFees", func(t *testing.T) {
pool := &BridgeLiquidityPool{
Asset: "USDC",
ChainID: "1",
TotalLiquidity: big.NewInt(10000000000),
AvailableLiquidity: big.NewInt(10000000000),
LockedLiquidity: big.NewInt(0),
Providers: make(map[string]*LiquidityProvider),
Fees: big.NewInt(0),
}
pool.Providers["provider1"] = &LiquidityProvider{
Address: "provider1",
Amount: big.NewInt(10000000000),
ShareTokens: big.NewInt(10000000000),
JoinedAt: time.Now(),
Rewards: big.NewInt(0),
}
// Distribute zero fees
bridge.distributeFees(pool, big.NewInt(0))
// Rewards should still be zero
assert.Equal(t, big.NewInt(0), pool.Providers["provider1"].Rewards)
})
}
// TestSlashValidators tests the slashValidators function (30% coverage target)
func TestSlashValidators(t *testing.T) {
t.Run("SlashNoValidators", func(t *testing.T) {
bridge := NewEnhancedBridge()
transfer := &BridgeTransfer{
ID: "slash-test-001",
Asset: "USDC",
Amount: big.NewInt(1000000),
Validators: make(map[string]*BridgeSignature),
}
// Should not panic with empty validators
bridge.mu.Lock()
bridge.slashValidators(transfer, big.NewInt(1000000))
bridge.mu.Unlock()
})
t.Run("SlashSingleValidator", func(t *testing.T) {
bridge := NewEnhancedBridge()
privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
stake, _ := new(big.Int).SetString("10000000000000000000", 10) // 10 ETH
validator := &BridgeValidator{
Address: "validator1",
PublicKey: &privateKey.PublicKey,
Stake: stake,
Active: true,
Slashed: false,
JoinedAt: time.Now(),
}
bridge.BridgeValidators = append(bridge.BridgeValidators, validator)
transfer := &BridgeTransfer{
ID: "slash-single-001",
Asset: "USDC",
Amount: big.NewInt(1000000),
Validators: make(map[string]*BridgeSignature),
}
transfer.Validators["validator1"] = &BridgeSignature{
Validator: "validator1",
Signature: []byte("sig"),
Timestamp: time.Now(),
}
slashAmount, _ := new(big.Int).SetString("1000000000000000000", 10) // 1 ETH
bridge.mu.Lock()
bridge.slashValidators(transfer, slashAmount)
bridge.mu.Unlock()
// Validator should be slashed
assert.True(t, validator.Slashed)
assert.False(t, validator.Active)
// Stake should be reduced
expectedStake, _ := new(big.Int).SetString("9000000000000000000", 10) // 9 ETH
assert.Equal(t, expectedStake, validator.Stake)
})
t.Run("SlashMultipleValidators", func(t *testing.T) {
bridge := NewEnhancedBridge()
// Add 3 validators
for i := 1; i <= 3; i++ {
privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
stake, _ := new(big.Int).SetString("10000000000000000000", 10) // 10 ETH each
validator := &BridgeValidator{
Address: "validator" + string(rune('0'+i)),
PublicKey: &privateKey.PublicKey,
Stake: stake,
Active: true,
Slashed: false,
JoinedAt: time.Now(),
}
bridge.BridgeValidators = append(bridge.BridgeValidators, validator)
}
transfer := &BridgeTransfer{
ID: "slash-multi-001",
Asset: "USDC",
Amount: big.NewInt(1000000),
Validators: make(map[string]*BridgeSignature),
}
// All 3 validators signed the fraudulent transfer
for i := 1; i <= 3; i++ {
addr := "validator" + string(rune('0'+i))
transfer.Validators[addr] = &BridgeSignature{
Validator: addr,
Signature: []byte("sig"),
Timestamp: time.Now(),
}
}
slashAmount, _ := new(big.Int).SetString("3000000000000000000", 10) // 3 ETH total (1 ETH each)
bridge.mu.Lock()
bridge.slashValidators(transfer, slashAmount)
bridge.mu.Unlock()
// All validators should be slashed
expectedStake, _ := new(big.Int).SetString("9000000000000000000", 10) // 9 ETH
for _, v := range bridge.BridgeValidators {
assert.True(t, v.Slashed)
assert.False(t, v.Active)
// Each validator should have 1 ETH deducted
assert.Equal(t, expectedStake, v.Stake)
}
})
t.Run("SlashValidatorNotFound", func(t *testing.T) {
bridge := NewEnhancedBridge()
transfer := &BridgeTransfer{
ID: "slash-notfound-001",
Asset: "USDC",
Amount: big.NewInt(1000000),
Validators: make(map[string]*BridgeSignature),
}
transfer.Validators["nonexistent"] = &BridgeSignature{
Validator: "nonexistent",
Signature: []byte("sig"),
Timestamp: time.Now(),
}
// Should not panic when validator not found
bridge.mu.Lock()
bridge.slashValidators(transfer, big.NewInt(1000000))
bridge.mu.Unlock()
})
t.Run("SlashZeroAmount", func(t *testing.T) {
bridge := NewEnhancedBridge()
privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
validator := &BridgeValidator{
Address: "validator1",
PublicKey: &privateKey.PublicKey,
Stake: func() *big.Int { v, _ := new(big.Int).SetString("10000000000000000000", 10); return v }(),
Active: true,
Slashed: false,
JoinedAt: time.Now(),
}
bridge.BridgeValidators = append(bridge.BridgeValidators, validator)
transfer := &BridgeTransfer{
ID: "slash-zero-001",
Validators: make(map[string]*BridgeSignature),
}
transfer.Validators["validator1"] = &BridgeSignature{
Validator: "validator1",
Signature: []byte("sig"),
Timestamp: time.Now(),
}
bridge.mu.Lock()
bridge.slashValidators(transfer, big.NewInt(0))
bridge.mu.Unlock()
// Validator should be slashed but stake unchanged
assert.True(t, validator.Slashed)
assert.False(t, validator.Active)
})
}
// TestCheckDailyLimit tests the checkDailyLimit function (62.5% coverage target)
func TestCheckDailyLimit(t *testing.T) {
bridge := NewEnhancedBridge()
t.Run("WithinDailyLimit", func(t *testing.T) {
asset := &BridgeAsset{
Symbol: "USDC",
DailyLimit: big.NewInt(100000000000), // 100k USDC
DailyVolume: big.NewInt(0),
LastReset: time.Now(),
}
amount := big.NewInt(1000000000) // 1k USDC
err := bridge.checkDailyLimit(asset, amount)
assert.NoError(t, err)
// Volume should be updated
assert.Equal(t, amount, asset.DailyVolume)
})
t.Run("ExceedsDailyLimit", func(t *testing.T) {
asset := &BridgeAsset{
Symbol: "USDC",
DailyLimit: big.NewInt(100000000000), // 100k USDC
DailyVolume: big.NewInt(99000000000), // 99k already used
LastReset: time.Now(),
}
amount := big.NewInt(2000000000) // 2k USDC (would exceed limit)
err := bridge.checkDailyLimit(asset, amount)
assert.Error(t, err)
assert.Contains(t, err.Error(), "exceeds daily limit")
})
t.Run("ExactlyAtDailyLimit", func(t *testing.T) {
asset := &BridgeAsset{
Symbol: "USDC",
DailyLimit: big.NewInt(100000000000), // 100k USDC
DailyVolume: big.NewInt(99000000000), // 99k already used
LastReset: time.Now(),
}
amount := big.NewInt(1000000000) // 1k USDC (exactly at limit)
err := bridge.checkDailyLimit(asset, amount)
assert.NoError(t, err)
})
t.Run("DailyVolumeReset", func(t *testing.T) {
asset := &BridgeAsset{
Symbol: "USDC",
DailyLimit: big.NewInt(100000000000), // 100k USDC
DailyVolume: big.NewInt(100000000000), // At limit
LastReset: time.Now().Add(-25 * time.Hour), // More than 24h ago
}
amount := big.NewInt(1000000000) // 1k USDC
err := bridge.checkDailyLimit(asset, amount)
assert.NoError(t, err)
// Volume should be reset and equal to new amount
assert.Equal(t, amount, asset.DailyVolume)
// LastReset should be updated
assert.True(t, time.Since(asset.LastReset) < time.Second)
})
t.Run("MultipleTransfersWithinLimit", func(t *testing.T) {
asset := &BridgeAsset{
Symbol: "USDC",
DailyLimit: big.NewInt(100000000000), // 100k USDC
DailyVolume: big.NewInt(0),
LastReset: time.Now(),
}
// Multiple transfers
amounts := []*big.Int{
big.NewInt(10000000000), // 10k
big.NewInt(20000000000), // 20k
big.NewInt(30000000000), // 30k
}
for _, amount := range amounts {
err := bridge.checkDailyLimit(asset, amount)
assert.NoError(t, err)
}
// Total should be 60k
assert.Equal(t, big.NewInt(60000000000), asset.DailyVolume)
})
t.Run("MultipleTransfersExceedLimit", func(t *testing.T) {
asset := &BridgeAsset{
Symbol: "USDC",
DailyLimit: big.NewInt(100000000000), // 100k USDC
DailyVolume: big.NewInt(0),
LastReset: time.Now(),
}
// First two should succeed
err := bridge.checkDailyLimit(asset, big.NewInt(50000000000))
assert.NoError(t, err)
err = bridge.checkDailyLimit(asset, big.NewInt(40000000000))
assert.NoError(t, err)
// Third should fail (would exceed)
err = bridge.checkDailyLimit(asset, big.NewInt(20000000000))
assert.Error(t, err)
assert.Contains(t, err.Error(), "exceeds daily limit")
// Volume should still be 90k
assert.Equal(t, big.NewInt(90000000000), asset.DailyVolume)
})
t.Run("ZeroAmount", func(t *testing.T) {
asset := &BridgeAsset{
Symbol: "USDC",
DailyLimit: big.NewInt(100000000000),
DailyVolume: big.NewInt(0),
LastReset: time.Now(),
}
err := bridge.checkDailyLimit(asset, big.NewInt(0))
assert.NoError(t, err)
assert.Equal(t, big.NewInt(0), asset.DailyVolume)
})
t.Run("ResetAtJustUnder24Hours", func(t *testing.T) {
asset := &BridgeAsset{
Symbol: "USDC",
DailyLimit: big.NewInt(100000000000),
DailyVolume: big.NewInt(99000000000),
LastReset: time.Now().Add(-24*time.Hour + time.Minute), // Just under 24h (should NOT reset)
}
amount := big.NewInt(2000000000) // Would exceed
err := bridge.checkDailyLimit(asset, amount)
// At under 24h, it should NOT reset (> not >=), so error expected
assert.Error(t, err)
assert.Contains(t, err.Error(), "exceeds daily limit")
})
t.Run("ResetJustPast24Hours", func(t *testing.T) {
asset := &BridgeAsset{
Symbol: "USDC",
DailyLimit: big.NewInt(100000000000),
DailyVolume: big.NewInt(99000000000),
LastReset: time.Now().Add(-24*time.Hour - time.Minute), // Just past 24h
}
amount := big.NewInt(2000000000) // Would exceed without reset
err := bridge.checkDailyLimit(asset, amount)
assert.NoError(t, err)
// Volume should be reset
assert.Equal(t, amount, asset.DailyVolume)
})
}
// TestBridgePausedState tests bridge operations when paused
func TestBridgePausedState(t *testing.T) {
t.Run("InitiateTransferWhenPaused", func(t *testing.T) {
bridge := createTestBridgeWithAssets(t)
bridge.EmergencyPaused = true
ctx := context.Background()
amount := big.NewInt(5000000000)
_, err := bridge.InitiateTransfer(ctx, "USDC", amount, "1", "2", "from", "to")
assert.Error(t, err)
assert.Contains(t, err.Error(), "bridge is paused")
})
t.Run("AssetPaused", func(t *testing.T) {
bridge := createTestBridgeWithAssets(t)
bridge.SupportedAssets["USDC"].Paused = true
ctx := context.Background()
amount := big.NewInt(5000000000)
_, err := bridge.InitiateTransfer(ctx, "USDC", amount, "1", "2", "from", "to")
assert.Error(t, err)
assert.Contains(t, err.Error(), "is paused")
})
}
// BenchmarkBridgeOperations benchmarks bridge operations
func BenchmarkBridgeOperations(b *testing.B) {
bridge := createTestBridgeWithAssets(b)
ctx := context.Background()
// Add liquidity for benchmarks to destination chain
bridge.AddLiquidity("2", "USDC", "provider", big.NewInt(1000000000000))
b.Run("InitiateTransfer", func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
bridge.InitiateTransfer(ctx, "USDC", big.NewInt(10000000), "1", "2", "from", "to")
}
})
b.Run("AddLiquidity", func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
bridge.AddLiquidity("1", "USDC", "provider", big.NewInt(1000000))
}
})
b.Run("FindValidator", func(b *testing.B) {
privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
bridge.BridgeValidators = append(bridge.BridgeValidators, &BridgeValidator{
Address: "benchValidator",
PublicKey: &privateKey.PublicKey,
Stake: big.NewInt(1000000),
Active: true,
})
b.ResetTimer()
for i := 0; i < b.N; i++ {
bridge.findValidator("benchValidator")
}
})
b.Run("FindTransfer", func(b *testing.B) {
bridge.PendingTransfers["benchTransfer"] = &BridgeTransfer{
ID: "benchTransfer",
Asset: "USDC",
Amount: big.NewInt(1000000),
Validators: make(map[string]*BridgeSignature),
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
bridge.findTransfer("benchTransfer")
}
})
b.Run("GetBridgeStatus", func(b *testing.B) {
b.ResetTimer()
for i := 0; i < b.N; i++ {
bridge.GetBridgeStatus()
}
})
b.Run("DistributeFees", func(b *testing.B) {
pool := &BridgeLiquidityPool{
Asset: "USDC",
TotalLiquidity: big.NewInt(1000000000),
AvailableLiquidity: big.NewInt(1000000000),
Providers: make(map[string]*LiquidityProvider),
Fees: big.NewInt(0),
}
pool.Providers["p1"] = &LiquidityProvider{
ShareTokens: big.NewInt(500000000),
Rewards: big.NewInt(0),
}
pool.Providers["p2"] = &LiquidityProvider{
ShareTokens: big.NewInt(500000000),
Rewards: big.NewInt(0),
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
bridge.distributeFees(pool, big.NewInt(10000))
}
})
b.Run("CheckDailyLimit", func(b *testing.B) {
asset := &BridgeAsset{
DailyLimit: big.NewInt(1000000000000),
DailyVolume: big.NewInt(0),
LastReset: time.Now(),
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
asset.DailyVolume = big.NewInt(0) // Reset for each iteration
bridge.checkDailyLimit(asset, big.NewInt(1000000))
}
})
}