mirror of
https://github.com/lightninglabs/loop.git
synced 2026-08-13 12:33:03 +02:00
staticaddr/deposit: guard confirmation height access
Document deposit lock ownership for mutable confirmation state and route production reads through deposit accessors. Keep store persistence on no-lock helpers while callers hold the deposit lock, preserving the existing transition behavior without leaving direct field reads in user-facing paths.
This commit is contained in:
parent
f468f24e6f
commit
58fbe2230e
9 changed files with 59 additions and 31 deletions
|
|
@ -1896,7 +1896,7 @@ func (s *swapClientServer) ListStaticAddressWithdrawals(ctx context.Context,
|
|||
Id: d.ID[:],
|
||||
Outpoint: d.OutPoint.String(),
|
||||
Value: int64(d.Value),
|
||||
ConfirmationHeight: d.ConfirmationHeight,
|
||||
ConfirmationHeight: d.GetConfirmationHeight(),
|
||||
State: toClientDepositState(
|
||||
d.GetState(),
|
||||
),
|
||||
|
|
@ -1987,7 +1987,8 @@ func (s *swapClientServer) ListStaticAddressSwaps(ctx context.Context,
|
|||
protoDeposits = make([]*looprpc.Deposit, 0, len(ds))
|
||||
for _, d := range ds {
|
||||
state := toClientDepositState(d.GetState())
|
||||
blocksUntilExpiry := d.ConfirmationHeight +
|
||||
confirmationHeight := d.GetConfirmationHeight()
|
||||
blocksUntilExpiry := confirmationHeight +
|
||||
int64(addrParams.Expiry) -
|
||||
int64(lndInfo.BlockHeight)
|
||||
|
||||
|
|
@ -1996,7 +1997,7 @@ func (s *swapClientServer) ListStaticAddressSwaps(ctx context.Context,
|
|||
State: state,
|
||||
Outpoint: d.OutPoint.String(),
|
||||
Value: int64(d.Value),
|
||||
ConfirmationHeight: d.ConfirmationHeight,
|
||||
ConfirmationHeight: confirmationHeight,
|
||||
SwapHash: d.SwapHash[:],
|
||||
BlocksUntilExpiry: blocksUntilExpiry,
|
||||
}
|
||||
|
|
@ -2304,7 +2305,7 @@ func filter(deposits []*deposit.Deposit, f filterFunc) []*looprpc.Deposit {
|
|||
),
|
||||
Outpoint: outpoint,
|
||||
Value: int64(d.Value),
|
||||
ConfirmationHeight: d.ConfirmationHeight,
|
||||
ConfirmationHeight: d.GetConfirmationHeight(),
|
||||
SwapHash: swapHash,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ func (f *FSM) WaitForExpirySweepAction(ctx context.Context,
|
|||
|
||||
spendChan, errSpendChan, err := f.cfg.ChainNotifier.RegisterConfirmationsNtfn( //nolint:lll
|
||||
ctx, txID, f.deposit.TimeOutSweepPkScript, DefaultConfTarget,
|
||||
int32(f.deposit.ConfirmationHeight),
|
||||
int32(f.deposit.GetConfirmationHeight()),
|
||||
)
|
||||
if err != nil {
|
||||
return f.HandleError(err)
|
||||
|
|
|
|||
|
|
@ -33,6 +33,9 @@ func (r *ID) FromByteSlice(b []byte) error {
|
|||
// Lock order: if both Manager.mu and a Deposit lock are needed, acquire
|
||||
// Manager.mu before Deposit.Lock. Never acquire Manager.mu while holding a
|
||||
// Deposit lock.
|
||||
//
|
||||
// The state and ConfirmationHeight fields are mutable and protected by the
|
||||
// deposit lock.
|
||||
type Deposit struct {
|
||||
sync.Mutex
|
||||
|
||||
|
|
@ -98,6 +101,10 @@ func (d *Deposit) GetState() fsm.StateType {
|
|||
return d.state
|
||||
}
|
||||
|
||||
func (d *Deposit) getStateNoLock() fsm.StateType {
|
||||
return d.state
|
||||
}
|
||||
|
||||
func (d *Deposit) SetState(state fsm.StateType) {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
|
@ -105,7 +112,7 @@ func (d *Deposit) SetState(state fsm.StateType) {
|
|||
d.state = state
|
||||
}
|
||||
|
||||
func (d *Deposit) SetStateNoLock(state fsm.StateType) {
|
||||
func (d *Deposit) setStateNoLock(state fsm.StateType) {
|
||||
d.state = state
|
||||
}
|
||||
|
||||
|
|
@ -116,10 +123,24 @@ func (d *Deposit) IsInState(state fsm.StateType) bool {
|
|||
return d.state == state
|
||||
}
|
||||
|
||||
func (d *Deposit) IsInStateNoLock(state fsm.StateType) bool {
|
||||
func (d *Deposit) isInStateNoLock(state fsm.StateType) bool {
|
||||
return d.state == state
|
||||
}
|
||||
|
||||
// GetConfirmationHeight returns the deposit confirmation height.
|
||||
func (d *Deposit) GetConfirmationHeight() int64 {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
return d.ConfirmationHeight
|
||||
}
|
||||
|
||||
// GetConfirmationHeightNoLock returns the deposit confirmation height without
|
||||
// acquiring the deposit lock.
|
||||
func (d *Deposit) GetConfirmationHeightNoLock() int64 {
|
||||
return d.ConfirmationHeight
|
||||
}
|
||||
|
||||
// GetRandomDepositID generates a random deposit ID.
|
||||
func GetRandomDepositID() (ID, error) {
|
||||
var id ID
|
||||
|
|
|
|||
|
|
@ -452,17 +452,14 @@ func (f *FSM) updateDeposit(ctx context.Context,
|
|||
return
|
||||
}
|
||||
|
||||
type checkStateFunc func(state fsm.StateType) bool
|
||||
type setStateFunc func(state fsm.StateType)
|
||||
checkFunc := checkStateFunc(f.deposit.IsInState)
|
||||
setFunc := setStateFunc(f.deposit.SetState)
|
||||
if _, ok := lockedEvents[notification.Event]; ok {
|
||||
checkFunc = f.deposit.IsInStateNoLock
|
||||
setFunc = f.deposit.SetStateNoLock
|
||||
_, alreadyLocked := lockedEvents[notification.Event]
|
||||
if !alreadyLocked {
|
||||
f.deposit.Lock()
|
||||
defer f.deposit.Unlock()
|
||||
}
|
||||
|
||||
setFunc(notification.NextState)
|
||||
if isUpdateSkipped(notification, checkFunc) {
|
||||
f.deposit.setStateNoLock(notification.NextState)
|
||||
if isUpdateSkipped(notification, f.deposit.isInStateNoLock) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -438,7 +438,7 @@ func (m *Manager) GetActiveDepositsInState(stateFilter fsm.StateType) (
|
|||
|
||||
filteredDeposits := make([]*Deposit, 0, len(deposits))
|
||||
for _, d := range deposits {
|
||||
if !d.IsInStateNoLock(stateFilter) {
|
||||
if !d.isInStateNoLock(stateFilter) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -446,8 +446,8 @@ func (m *Manager) GetActiveDepositsInState(stateFilter fsm.StateType) (
|
|||
}
|
||||
|
||||
sort.Slice(filteredDeposits, func(i, j int) bool {
|
||||
return filteredDeposits[i].ConfirmationHeight <
|
||||
filteredDeposits[j].ConfirmationHeight
|
||||
return filteredDeposits[i].GetConfirmationHeightNoLock() <
|
||||
filteredDeposits[j].GetConfirmationHeightNoLock()
|
||||
})
|
||||
|
||||
return filteredDeposits, nil
|
||||
|
|
@ -481,7 +481,7 @@ func (m *Manager) AllOutpointsActiveDeposits(outpoints []wire.OutPoint,
|
|||
lockDeposits(deposits)
|
||||
defer unlockDeposits(deposits)
|
||||
for _, d := range deposits {
|
||||
if !d.IsInStateNoLock(targetState) {
|
||||
if !d.isInStateNoLock(targetState) {
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
|
@ -543,7 +543,8 @@ func (m *Manager) TransitionDeposits(ctx context.Context, deposits []*Deposit,
|
|||
for _, deposit := range deposits {
|
||||
if deposit.isInFinalStateNoLock() {
|
||||
return fmt.Errorf("deposit %v is no longer active in "+
|
||||
"state %v", deposit.OutPoint, deposit.state)
|
||||
"state %v", deposit.OutPoint,
|
||||
deposit.getStateNoLock())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -597,6 +598,9 @@ func (m *Manager) GetAllDeposits(ctx context.Context) ([]*Deposit, error) {
|
|||
|
||||
// UpdateDeposit overrides all fields of the deposit with given ID in the store.
|
||||
func (m *Manager) UpdateDeposit(ctx context.Context, d *Deposit) error {
|
||||
d.Lock()
|
||||
defer d.Unlock()
|
||||
|
||||
return m.cfg.Store.UpdateDeposit(ctx, d)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ func (s *SqlStore) CreateDeposit(ctx context.Context, deposit *Deposit) error {
|
|||
TxHash: deposit.Hash[:],
|
||||
OutIndex: int32(deposit.Index),
|
||||
Amount: int64(deposit.Value),
|
||||
ConfirmationHeight: deposit.ConfirmationHeight,
|
||||
ConfirmationHeight: deposit.GetConfirmationHeight(),
|
||||
TimeoutSweepPkScript: deposit.TimeOutSweepPkScript,
|
||||
}
|
||||
|
||||
|
|
@ -69,11 +69,15 @@ func (s *SqlStore) CreateDeposit(ctx context.Context, deposit *Deposit) error {
|
|||
}
|
||||
|
||||
// UpdateDeposit updates the deposit in the database.
|
||||
//
|
||||
// Callers that pass a live deposit must hold the deposit lock while calling
|
||||
// this method. The deposit FSM already does this for state transitions, and
|
||||
// Manager.UpdateDeposit wraps external callers with the same lock.
|
||||
func (s *SqlStore) UpdateDeposit(ctx context.Context, deposit *Deposit) error {
|
||||
insertUpdateArgs := sqlc.InsertDepositUpdateParams{
|
||||
DepositID: deposit.ID[:],
|
||||
UpdateTimestamp: s.clock.Now().UTC(),
|
||||
UpdateState: string(deposit.state),
|
||||
UpdateState: string(deposit.getStateNoLock()),
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -83,7 +87,7 @@ func (s *SqlStore) UpdateDeposit(ctx context.Context, deposit *Deposit) error {
|
|||
Valid: true,
|
||||
}
|
||||
confirmationHeight = sql.NullInt64{
|
||||
Int64: deposit.ConfirmationHeight,
|
||||
Int64: deposit.GetConfirmationHeightNoLock(),
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -234,9 +234,9 @@ func filterAutoloopCandidateDeposits(maxAmount btcutil.Amount,
|
|||
continue
|
||||
}
|
||||
|
||||
confirmationHeight := candidateDeposit.GetConfirmationHeight()
|
||||
swappable := IsSwappable(
|
||||
uint32(candidateDeposit.ConfirmationHeight),
|
||||
blockHeight, csvExpiry,
|
||||
uint32(confirmationHeight), blockHeight, csvExpiry,
|
||||
)
|
||||
if !swappable {
|
||||
continue
|
||||
|
|
@ -246,7 +246,7 @@ func filterAutoloopCandidateDeposits(maxAmount btcutil.Amount,
|
|||
continue
|
||||
}
|
||||
|
||||
residualLife := candidateDeposit.ConfirmationHeight +
|
||||
residualLife := confirmationHeight +
|
||||
int64(csvExpiry) - int64(blockHeight)
|
||||
|
||||
eligibleDeposits = append(
|
||||
|
|
|
|||
|
|
@ -861,8 +861,9 @@ func SelectDeposits(targetAmount btcutil.Amount,
|
|||
// Filter out deposits that are too close to expiry to be swapped.
|
||||
var deposits []*deposit.Deposit
|
||||
for _, d := range unfilteredDeposits {
|
||||
confirmationHeight := d.GetConfirmationHeight()
|
||||
if !IsSwappable(
|
||||
uint32(d.ConfirmationHeight), blockHeight, csvExpiry,
|
||||
uint32(confirmationHeight), blockHeight, csvExpiry,
|
||||
) {
|
||||
|
||||
log.Debugf("Skipping deposit %s as it expires before "+
|
||||
|
|
@ -878,9 +879,9 @@ func SelectDeposits(targetAmount btcutil.Amount,
|
|||
// blocks-until-expiry in ascending order.
|
||||
sort.Slice(deposits, func(i, j int) bool {
|
||||
if deposits[i].Value == deposits[j].Value {
|
||||
iExp := uint32(deposits[i].ConfirmationHeight) +
|
||||
iExp := uint32(deposits[i].GetConfirmationHeight()) +
|
||||
csvExpiry - blockHeight
|
||||
jExp := uint32(deposits[j].ConfirmationHeight) +
|
||||
jExp := uint32(deposits[j].GetConfirmationHeight()) +
|
||||
csvExpiry - blockHeight
|
||||
|
||||
return iExp < jExp
|
||||
|
|
|
|||
|
|
@ -669,7 +669,7 @@ func (m *Manager) handleWithdrawal(ctx context.Context,
|
|||
d := deposits[0]
|
||||
spentChan, errChan, err := m.cfg.ChainNotifier.RegisterSpendNtfn(
|
||||
ctx, &d.OutPoint, addrParams.PkScript,
|
||||
int32(d.ConfirmationHeight),
|
||||
int32(d.GetConfirmationHeight()),
|
||||
)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to register spend ntfn: %w", err)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue