github.com/cryptogateway/go-paymex@v0.0.0-20210204174735-96277fb1e602/les/lespay/server/balance.go (about)

     1  // Copyright 2019 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package server
    18  
    19  import (
    20  	"errors"
    21  	"math"
    22  	"sync"
    23  	"time"
    24  
    25  	"github.com/cryptogateway/go-paymex/common/mclock"
    26  	"github.com/cryptogateway/go-paymex/les/utils"
    27  	"github.com/cryptogateway/go-paymex/p2p/enode"
    28  	"github.com/cryptogateway/go-paymex/p2p/nodestate"
    29  )
    30  
    31  var errBalanceOverflow = errors.New("balance overflow")
    32  
    33  const maxBalance = math.MaxInt64 // maximum allowed balance value
    34  
    35  const (
    36  	balanceCallbackUpdate = iota // called when priority drops below the last minimum estimate
    37  	balanceCallbackZero          // called when priority drops to zero (positive balance exhausted)
    38  	balanceCallbackCount         // total number of balance callbacks
    39  )
    40  
    41  // PriceFactors determine the pricing policy (may apply either to positive or
    42  // negative balances which may have different factors).
    43  // - TimeFactor is cost unit per nanosecond of connection time
    44  // - CapacityFactor is cost unit per nanosecond of connection time per 1000000 capacity
    45  // - RequestFactor is cost unit per request "realCost" unit
    46  type PriceFactors struct {
    47  	TimeFactor, CapacityFactor, RequestFactor float64
    48  }
    49  
    50  // timePrice returns the price of connection per nanosecond at the given capacity
    51  func (p PriceFactors) timePrice(cap uint64) float64 {
    52  	return p.TimeFactor + float64(cap)*p.CapacityFactor/1000000
    53  }
    54  
    55  // NodeBalance keeps track of the positive and negative balances of a connected
    56  // client and calculates actual and projected future priority values.
    57  // Implements nodePriority interface.
    58  type NodeBalance struct {
    59  	bt                               *BalanceTracker
    60  	lock                             sync.RWMutex
    61  	node                             *enode.Node
    62  	connAddress                      string
    63  	active                           bool
    64  	priority                         bool
    65  	capacity                         uint64
    66  	balance                          balance
    67  	posFactor, negFactor             PriceFactors
    68  	sumReqCost                       uint64
    69  	lastUpdate, nextUpdate, initTime mclock.AbsTime
    70  	updateEvent                      mclock.Timer
    71  	// since only a limited and fixed number of callbacks are needed, they are
    72  	// stored in a fixed size array ordered by priority threshold.
    73  	callbacks [balanceCallbackCount]balanceCallback
    74  	// callbackIndex maps balanceCallback constants to callbacks array indexes (-1 if not active)
    75  	callbackIndex [balanceCallbackCount]int
    76  	callbackCount int // number of active callbacks
    77  }
    78  
    79  // balance represents a pair of positive and negative balances
    80  type balance struct {
    81  	pos, neg utils.ExpiredValue
    82  }
    83  
    84  // balanceCallback represents a single callback that is activated when client priority
    85  // reaches the given threshold
    86  type balanceCallback struct {
    87  	id        int
    88  	threshold int64
    89  	callback  func()
    90  }
    91  
    92  // GetBalance returns the current positive and negative balance.
    93  func (n *NodeBalance) GetBalance() (uint64, uint64) {
    94  	n.lock.Lock()
    95  	defer n.lock.Unlock()
    96  
    97  	now := n.bt.clock.Now()
    98  	n.updateBalance(now)
    99  	return n.balance.pos.Value(n.bt.posExp.LogOffset(now)), n.balance.neg.Value(n.bt.negExp.LogOffset(now))
   100  }
   101  
   102  // GetRawBalance returns the current positive and negative balance
   103  // but in the raw(expired value) format.
   104  func (n *NodeBalance) GetRawBalance() (utils.ExpiredValue, utils.ExpiredValue) {
   105  	n.lock.Lock()
   106  	defer n.lock.Unlock()
   107  
   108  	now := n.bt.clock.Now()
   109  	n.updateBalance(now)
   110  	return n.balance.pos, n.balance.neg
   111  }
   112  
   113  // AddBalance adds the given amount to the positive balance and returns the balance
   114  // before and after the operation. Exceeding maxBalance results in an error (balance is
   115  // unchanged) while adding a negative amount higher than the current balance results in
   116  // zero balance.
   117  func (n *NodeBalance) AddBalance(amount int64) (uint64, uint64, error) {
   118  	var (
   119  		err      error
   120  		old, new uint64
   121  	)
   122  	n.bt.ns.Operation(func() {
   123  		var (
   124  			callbacks   []func()
   125  			setPriority bool
   126  		)
   127  		n.bt.updateTotalBalance(n, func() bool {
   128  			now := n.bt.clock.Now()
   129  			n.updateBalance(now)
   130  
   131  			// Ensure the given amount is valid to apply.
   132  			offset := n.bt.posExp.LogOffset(now)
   133  			old = n.balance.pos.Value(offset)
   134  			if amount > 0 && (amount > maxBalance || old > maxBalance-uint64(amount)) {
   135  				err = errBalanceOverflow
   136  				return false
   137  			}
   138  
   139  			// Update the total positive balance counter.
   140  			n.balance.pos.Add(amount, offset)
   141  			callbacks = n.checkCallbacks(now)
   142  			setPriority = n.checkPriorityStatus()
   143  			new = n.balance.pos.Value(offset)
   144  			n.storeBalance(true, false)
   145  			return true
   146  		})
   147  		for _, cb := range callbacks {
   148  			cb()
   149  		}
   150  		if setPriority {
   151  			n.bt.ns.SetStateSub(n.node, n.bt.PriorityFlag, nodestate.Flags{}, 0)
   152  		}
   153  		n.signalPriorityUpdate()
   154  	})
   155  	if err != nil {
   156  		return old, old, err
   157  	}
   158  
   159  	return old, new, nil
   160  }
   161  
   162  // SetBalance sets the positive and negative balance to the given values
   163  func (n *NodeBalance) SetBalance(pos, neg uint64) error {
   164  	if pos > maxBalance || neg > maxBalance {
   165  		return errBalanceOverflow
   166  	}
   167  	n.bt.ns.Operation(func() {
   168  		var (
   169  			callbacks   []func()
   170  			setPriority bool
   171  		)
   172  		n.bt.updateTotalBalance(n, func() bool {
   173  			now := n.bt.clock.Now()
   174  			n.updateBalance(now)
   175  
   176  			var pb, nb utils.ExpiredValue
   177  			pb.Add(int64(pos), n.bt.posExp.LogOffset(now))
   178  			nb.Add(int64(neg), n.bt.negExp.LogOffset(now))
   179  			n.balance.pos = pb
   180  			n.balance.neg = nb
   181  			callbacks = n.checkCallbacks(now)
   182  			setPriority = n.checkPriorityStatus()
   183  			n.storeBalance(true, true)
   184  			return true
   185  		})
   186  		for _, cb := range callbacks {
   187  			cb()
   188  		}
   189  		if setPriority {
   190  			n.bt.ns.SetStateSub(n.node, n.bt.PriorityFlag, nodestate.Flags{}, 0)
   191  		}
   192  		n.signalPriorityUpdate()
   193  	})
   194  	return nil
   195  }
   196  
   197  // RequestServed should be called after serving a request for the given peer
   198  func (n *NodeBalance) RequestServed(cost uint64) uint64 {
   199  	n.lock.Lock()
   200  	var callbacks []func()
   201  	defer func() {
   202  		n.lock.Unlock()
   203  		if callbacks != nil {
   204  			n.bt.ns.Operation(func() {
   205  				for _, cb := range callbacks {
   206  					cb()
   207  				}
   208  			})
   209  		}
   210  	}()
   211  
   212  	now := n.bt.clock.Now()
   213  	n.updateBalance(now)
   214  	fcost := float64(cost)
   215  
   216  	posExp := n.bt.posExp.LogOffset(now)
   217  	var check bool
   218  	if !n.balance.pos.IsZero() {
   219  		if n.posFactor.RequestFactor != 0 {
   220  			c := -int64(fcost * n.posFactor.RequestFactor)
   221  			cc := n.balance.pos.Add(c, posExp)
   222  			if c == cc {
   223  				fcost = 0
   224  			} else {
   225  				fcost *= 1 - float64(cc)/float64(c)
   226  			}
   227  			check = true
   228  		} else {
   229  			fcost = 0
   230  		}
   231  	}
   232  	if fcost > 0 {
   233  		if n.negFactor.RequestFactor != 0 {
   234  			n.balance.neg.Add(int64(fcost*n.negFactor.RequestFactor), n.bt.negExp.LogOffset(now))
   235  			check = true
   236  		}
   237  	}
   238  	if check {
   239  		callbacks = n.checkCallbacks(now)
   240  	}
   241  	n.sumReqCost += cost
   242  	return n.balance.pos.Value(posExp)
   243  }
   244  
   245  // Priority returns the actual priority based on the current balance
   246  func (n *NodeBalance) Priority(now mclock.AbsTime, capacity uint64) int64 {
   247  	n.lock.Lock()
   248  	defer n.lock.Unlock()
   249  
   250  	n.updateBalance(now)
   251  	return n.balanceToPriority(n.balance, capacity)
   252  }
   253  
   254  // EstMinPriority gives a lower estimate for the priority at a given time in the future.
   255  // An average request cost per time is assumed that is twice the average cost per time
   256  // in the current session.
   257  // If update is true then a priority callback is added that turns UpdateFlag on and off
   258  // in case the priority goes below the estimated minimum.
   259  func (n *NodeBalance) EstMinPriority(at mclock.AbsTime, capacity uint64, update bool) int64 {
   260  	n.lock.Lock()
   261  	defer n.lock.Unlock()
   262  
   263  	var avgReqCost float64
   264  	dt := time.Duration(n.lastUpdate - n.initTime)
   265  	if dt > time.Second {
   266  		avgReqCost = float64(n.sumReqCost) * 2 / float64(dt)
   267  	}
   268  	pri := n.balanceToPriority(n.reducedBalance(at, capacity, avgReqCost), capacity)
   269  	if update {
   270  		n.addCallback(balanceCallbackUpdate, pri, n.signalPriorityUpdate)
   271  	}
   272  	return pri
   273  }
   274  
   275  // PosBalanceMissing calculates the missing amount of positive balance in order to
   276  // connect at targetCapacity, stay connected for the given amount of time and then
   277  // still have a priority of targetPriority
   278  func (n *NodeBalance) PosBalanceMissing(targetPriority int64, targetCapacity uint64, after time.Duration) uint64 {
   279  	n.lock.Lock()
   280  	defer n.lock.Unlock()
   281  
   282  	now := n.bt.clock.Now()
   283  	if targetPriority < 0 {
   284  		timePrice := n.negFactor.timePrice(targetCapacity)
   285  		timeCost := uint64(float64(after) * timePrice)
   286  		negBalance := n.balance.neg.Value(n.bt.negExp.LogOffset(now))
   287  		if timeCost+negBalance < uint64(-targetPriority) {
   288  			return 0
   289  		}
   290  		if uint64(-targetPriority) > negBalance && timePrice > 1e-100 {
   291  			if negTime := time.Duration(float64(uint64(-targetPriority)-negBalance) / timePrice); negTime < after {
   292  				after -= negTime
   293  			} else {
   294  				after = 0
   295  			}
   296  		}
   297  		targetPriority = 0
   298  	}
   299  	timePrice := n.posFactor.timePrice(targetCapacity)
   300  	posRequired := uint64(float64(targetPriority)*float64(targetCapacity)+float64(after)*timePrice) + 1
   301  	if posRequired >= maxBalance {
   302  		return math.MaxUint64 // target not reachable
   303  	}
   304  	posBalance := n.balance.pos.Value(n.bt.posExp.LogOffset(now))
   305  	if posRequired > posBalance {
   306  		return posRequired - posBalance
   307  	}
   308  	return 0
   309  }
   310  
   311  // SetPriceFactors sets the price factors. TimeFactor is the price of a nanosecond of
   312  // connection while RequestFactor is the price of a request cost unit.
   313  func (n *NodeBalance) SetPriceFactors(posFactor, negFactor PriceFactors) {
   314  	n.lock.Lock()
   315  	now := n.bt.clock.Now()
   316  	n.updateBalance(now)
   317  	n.posFactor, n.negFactor = posFactor, negFactor
   318  	callbacks := n.checkCallbacks(now)
   319  	n.lock.Unlock()
   320  	if callbacks != nil {
   321  		n.bt.ns.Operation(func() {
   322  			for _, cb := range callbacks {
   323  				cb()
   324  			}
   325  		})
   326  	}
   327  }
   328  
   329  // GetPriceFactors returns the price factors
   330  func (n *NodeBalance) GetPriceFactors() (posFactor, negFactor PriceFactors) {
   331  	n.lock.Lock()
   332  	defer n.lock.Unlock()
   333  
   334  	return n.posFactor, n.negFactor
   335  }
   336  
   337  // activate starts time/capacity cost deduction.
   338  func (n *NodeBalance) activate() {
   339  	n.bt.updateTotalBalance(n, func() bool {
   340  		if n.active {
   341  			return false
   342  		}
   343  		n.active = true
   344  		n.lastUpdate = n.bt.clock.Now()
   345  		return true
   346  	})
   347  }
   348  
   349  // deactivate stops time/capacity cost deduction and saves the balances in the database
   350  func (n *NodeBalance) deactivate() {
   351  	n.bt.updateTotalBalance(n, func() bool {
   352  		if !n.active {
   353  			return false
   354  		}
   355  		n.updateBalance(n.bt.clock.Now())
   356  		if n.updateEvent != nil {
   357  			n.updateEvent.Stop()
   358  			n.updateEvent = nil
   359  		}
   360  		n.storeBalance(true, true)
   361  		n.active = false
   362  		return true
   363  	})
   364  }
   365  
   366  // updateBalance updates balance based on the time factor
   367  func (n *NodeBalance) updateBalance(now mclock.AbsTime) {
   368  	if n.active && now > n.lastUpdate {
   369  		n.balance = n.reducedBalance(now, n.capacity, 0)
   370  		n.lastUpdate = now
   371  	}
   372  }
   373  
   374  // storeBalance stores the positive and/or negative balance of the node in the database
   375  func (n *NodeBalance) storeBalance(pos, neg bool) {
   376  	if pos {
   377  		n.bt.storeBalance(n.node.ID().Bytes(), false, n.balance.pos)
   378  	}
   379  	if neg {
   380  		n.bt.storeBalance([]byte(n.connAddress), true, n.balance.neg)
   381  	}
   382  }
   383  
   384  // addCallback sets up a one-time callback to be called when priority reaches
   385  // the threshold. If it has already reached the threshold the callback is called
   386  // immediately.
   387  // Note: should be called while n.lock is held
   388  // Note 2: the callback function runs inside a NodeStateMachine operation
   389  func (n *NodeBalance) addCallback(id int, threshold int64, callback func()) {
   390  	n.removeCallback(id)
   391  	idx := 0
   392  	for idx < n.callbackCount && threshold > n.callbacks[idx].threshold {
   393  		idx++
   394  	}
   395  	for i := n.callbackCount - 1; i >= idx; i-- {
   396  		n.callbackIndex[n.callbacks[i].id]++
   397  		n.callbacks[i+1] = n.callbacks[i]
   398  	}
   399  	n.callbackCount++
   400  	n.callbackIndex[id] = idx
   401  	n.callbacks[idx] = balanceCallback{id, threshold, callback}
   402  	now := n.bt.clock.Now()
   403  	n.updateBalance(now)
   404  	n.scheduleCheck(now)
   405  }
   406  
   407  // removeCallback removes the given callback and returns true if it was active
   408  // Note: should be called while n.lock is held
   409  func (n *NodeBalance) removeCallback(id int) bool {
   410  	idx := n.callbackIndex[id]
   411  	if idx == -1 {
   412  		return false
   413  	}
   414  	n.callbackIndex[id] = -1
   415  	for i := idx; i < n.callbackCount-1; i++ {
   416  		n.callbackIndex[n.callbacks[i+1].id]--
   417  		n.callbacks[i] = n.callbacks[i+1]
   418  	}
   419  	n.callbackCount--
   420  	return true
   421  }
   422  
   423  // checkCallbacks checks whether the threshold of any of the active callbacks
   424  // have been reached and returns triggered callbacks.
   425  // Note: checkCallbacks assumes that the balance has been recently updated.
   426  func (n *NodeBalance) checkCallbacks(now mclock.AbsTime) (callbacks []func()) {
   427  	if n.callbackCount == 0 || n.capacity == 0 {
   428  		return
   429  	}
   430  	pri := n.balanceToPriority(n.balance, n.capacity)
   431  	for n.callbackCount != 0 && n.callbacks[n.callbackCount-1].threshold >= pri {
   432  		n.callbackCount--
   433  		n.callbackIndex[n.callbacks[n.callbackCount].id] = -1
   434  		callbacks = append(callbacks, n.callbacks[n.callbackCount].callback)
   435  	}
   436  	n.scheduleCheck(now)
   437  	return
   438  }
   439  
   440  // scheduleCheck sets up or updates a scheduled event to ensure that it will be called
   441  // again just after the next threshold has been reached.
   442  func (n *NodeBalance) scheduleCheck(now mclock.AbsTime) {
   443  	if n.callbackCount != 0 {
   444  		d, ok := n.timeUntil(n.callbacks[n.callbackCount-1].threshold)
   445  		if !ok {
   446  			n.nextUpdate = 0
   447  			n.updateAfter(0)
   448  			return
   449  		}
   450  		if n.nextUpdate == 0 || n.nextUpdate > now+mclock.AbsTime(d) {
   451  			if d > time.Second {
   452  				// Note: if the scheduled update is not in the very near future then we
   453  				// schedule the update a bit earlier. This way we do need to update a few
   454  				// extra times but don't need to reschedule every time a processed request
   455  				// brings the expected firing time a little bit closer.
   456  				d = ((d - time.Second) * 7 / 8) + time.Second
   457  			}
   458  			n.nextUpdate = now + mclock.AbsTime(d)
   459  			n.updateAfter(d)
   460  		}
   461  	} else {
   462  		n.nextUpdate = 0
   463  		n.updateAfter(0)
   464  	}
   465  }
   466  
   467  // updateAfter schedules a balance update and callback check in the future
   468  func (n *NodeBalance) updateAfter(dt time.Duration) {
   469  	if n.updateEvent == nil || n.updateEvent.Stop() {
   470  		if dt == 0 {
   471  			n.updateEvent = nil
   472  		} else {
   473  			n.updateEvent = n.bt.clock.AfterFunc(dt, func() {
   474  				var callbacks []func()
   475  				n.lock.Lock()
   476  				if n.callbackCount != 0 {
   477  					now := n.bt.clock.Now()
   478  					n.updateBalance(now)
   479  					callbacks = n.checkCallbacks(now)
   480  				}
   481  				n.lock.Unlock()
   482  				if callbacks != nil {
   483  					n.bt.ns.Operation(func() {
   484  						for _, cb := range callbacks {
   485  							cb()
   486  						}
   487  					})
   488  				}
   489  			})
   490  		}
   491  	}
   492  }
   493  
   494  // balanceExhausted should be called when the positive balance is exhausted (priority goes to zero/negative)
   495  // Note: this function should run inside a NodeStateMachine operation
   496  func (n *NodeBalance) balanceExhausted() {
   497  	n.lock.Lock()
   498  	n.storeBalance(true, false)
   499  	n.priority = false
   500  	n.lock.Unlock()
   501  	n.bt.ns.SetStateSub(n.node, nodestate.Flags{}, n.bt.PriorityFlag, 0)
   502  }
   503  
   504  // checkPriorityStatus checks whether the node has gained priority status and sets the priority
   505  // callback and flag if necessary. It assumes that the balance has been recently updated.
   506  // Note that the priority flag has to be set by the caller after the mutex has been released.
   507  func (n *NodeBalance) checkPriorityStatus() bool {
   508  	if !n.priority && !n.balance.pos.IsZero() {
   509  		n.priority = true
   510  		n.addCallback(balanceCallbackZero, 0, func() { n.balanceExhausted() })
   511  		return true
   512  	}
   513  	return false
   514  }
   515  
   516  // signalPriorityUpdate signals that the priority fell below the previous minimum estimate
   517  // Note: this function should run inside a NodeStateMachine operation
   518  func (n *NodeBalance) signalPriorityUpdate() {
   519  	n.bt.ns.SetStateSub(n.node, n.bt.UpdateFlag, nodestate.Flags{}, 0)
   520  	n.bt.ns.SetStateSub(n.node, nodestate.Flags{}, n.bt.UpdateFlag, 0)
   521  }
   522  
   523  // setCapacity updates the capacity value used for priority calculation
   524  // Note: capacity should never be zero
   525  // Note 2: this function should run inside a NodeStateMachine operation
   526  func (n *NodeBalance) setCapacity(capacity uint64) {
   527  	n.lock.Lock()
   528  	now := n.bt.clock.Now()
   529  	n.updateBalance(now)
   530  	n.capacity = capacity
   531  	callbacks := n.checkCallbacks(now)
   532  	n.lock.Unlock()
   533  	for _, cb := range callbacks {
   534  		cb()
   535  	}
   536  }
   537  
   538  // balanceToPriority converts a balance to a priority value. Lower priority means
   539  // first to disconnect. Positive balance translates to positive priority. If positive
   540  // balance is zero then negative balance translates to a negative priority.
   541  func (n *NodeBalance) balanceToPriority(b balance, capacity uint64) int64 {
   542  	if !b.pos.IsZero() {
   543  		return int64(b.pos.Value(n.bt.posExp.LogOffset(n.bt.clock.Now())) / capacity)
   544  	}
   545  	return -int64(b.neg.Value(n.bt.negExp.LogOffset(n.bt.clock.Now())))
   546  }
   547  
   548  // reducedBalance estimates the reduced balance at a given time in the fututre based
   549  // on the current balance, the time factor and an estimated average request cost per time ratio
   550  func (n *NodeBalance) reducedBalance(at mclock.AbsTime, capacity uint64, avgReqCost float64) balance {
   551  	dt := float64(at - n.lastUpdate)
   552  	b := n.balance
   553  	if !b.pos.IsZero() {
   554  		factor := n.posFactor.timePrice(capacity) + n.posFactor.RequestFactor*avgReqCost
   555  		diff := -int64(dt * factor)
   556  		dd := b.pos.Add(diff, n.bt.posExp.LogOffset(at))
   557  		if dd == diff {
   558  			dt = 0
   559  		} else {
   560  			dt += float64(dd) / factor
   561  		}
   562  	}
   563  	if dt > 0 {
   564  		factor := n.negFactor.timePrice(capacity) + n.negFactor.RequestFactor*avgReqCost
   565  		b.neg.Add(int64(dt*factor), n.bt.negExp.LogOffset(at))
   566  	}
   567  	return b
   568  }
   569  
   570  // timeUntil calculates the remaining time needed to reach a given priority level
   571  // assuming that no requests are processed until then. If the given level is never
   572  // reached then (0, false) is returned.
   573  // Note: the function assumes that the balance has been recently updated and
   574  // calculates the time starting from the last update.
   575  func (n *NodeBalance) timeUntil(priority int64) (time.Duration, bool) {
   576  	now := n.bt.clock.Now()
   577  	var dt float64
   578  	if !n.balance.pos.IsZero() {
   579  		posBalance := n.balance.pos.Value(n.bt.posExp.LogOffset(now))
   580  		timePrice := n.posFactor.timePrice(n.capacity)
   581  		if timePrice < 1e-100 {
   582  			return 0, false
   583  		}
   584  		if priority > 0 {
   585  			newBalance := uint64(priority) * n.capacity
   586  			if newBalance > posBalance {
   587  				return 0, false
   588  			}
   589  			dt = float64(posBalance-newBalance) / timePrice
   590  			return time.Duration(dt), true
   591  		}
   592  		dt = float64(posBalance) / timePrice
   593  	} else {
   594  		if priority > 0 {
   595  			return 0, false
   596  		}
   597  	}
   598  	// if we have a positive balance then dt equals the time needed to get it to zero
   599  	negBalance := n.balance.neg.Value(n.bt.negExp.LogOffset(now))
   600  	timePrice := n.negFactor.timePrice(n.capacity)
   601  	if uint64(-priority) > negBalance {
   602  		if timePrice < 1e-100 {
   603  			return 0, false
   604  		}
   605  		dt += float64(uint64(-priority)-negBalance) / timePrice
   606  	}
   607  	return time.Duration(dt), true
   608  }