mirror of
https://github.com/luxfi/dex.git
synced 2026-09-24 00:26:31 +00:00
lx: fix data race in LendingPool.Borrow/Repay
Borrow and Repay used a per-AssetPool mutex (pool.mu) while Supply, Withdraw and the read accessors all serialize via the LendingPool-wide lock (lp.mu). The shared LendingPool maps (TotalBorrowed, UtilizationRates, AccruedFees, ReserveFunds) and per-pool fields written by updateInterestRates (UtilizationRate, BorrowRate, SupplyRate) were therefore protected by two different locks, so concurrent Supply + Borrow races on the same asset — reliably reproduced by TestLendingPool_ConcurrentAccess under -race. Use lp.mu in Borrow and Repay so every mutator on a given LendingPool serializes through the same mutex, and drop the now-dead AssetPool.mu field (single way to do everything). Verified with: go test -race -count=50 -run TestLendingPool_ConcurrentAccess
This commit is contained in:
+6
-7
@@ -50,7 +50,6 @@ type AssetPool struct {
|
||||
Suppliers map[string]*SupplyPosition
|
||||
Borrowers map[string]*BorrowPosition
|
||||
Paused bool
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// LendingAccount represents a supplier's account
|
||||
@@ -322,14 +321,14 @@ func (lp *LendingPool) Withdraw(userID, asset string, amount *big.Int) error {
|
||||
// Borrow allows users to borrow against collateral
|
||||
func (lp *LendingPool) Borrow(asset string, amount *big.Int) error {
|
||||
// Implementation handled by MarginEngine
|
||||
lp.mu.Lock()
|
||||
defer lp.mu.Unlock()
|
||||
|
||||
pool, exists := lp.Pools[asset]
|
||||
if !exists {
|
||||
return fmt.Errorf("pool not found for %s", asset)
|
||||
}
|
||||
|
||||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
// Check available liquidity
|
||||
available := new(big.Int).Sub(pool.TotalSupply, pool.TotalBorrow)
|
||||
if amount.Cmp(available) > 0 {
|
||||
@@ -349,14 +348,14 @@ func (lp *LendingPool) Borrow(asset string, amount *big.Int) error {
|
||||
|
||||
// Repay repays borrowed amount with interest
|
||||
func (lp *LendingPool) Repay(asset string, principal, interest *big.Int) error {
|
||||
lp.mu.Lock()
|
||||
defer lp.mu.Unlock()
|
||||
|
||||
pool, exists := lp.Pools[asset]
|
||||
if !exists {
|
||||
return fmt.Errorf("pool not found for %s", asset)
|
||||
}
|
||||
|
||||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
// Update pool
|
||||
pool.TotalBorrow.Sub(pool.TotalBorrow, principal)
|
||||
lp.TotalBorrowed[asset].Sub(lp.TotalBorrowed[asset], principal)
|
||||
|
||||
Reference in New Issue
Block a user