github.com/hardtosaygoodbye/go-ethereum@v1.10.16-0.20220122011429-97003b9e6c15/les/vflux/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/hardtosaygoodbye/go-ethereum/common/mclock" 26 "github.com/hardtosaygoodbye/go-ethereum/les/utils" 27 "github.com/hardtosaygoodbye/go-ethereum/p2p/enode" 28 "github.com/hardtosaygoodbye/go-ethereum/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 // connectionPrice returns the price of connection per nanosecond at the given capacity 51 // and the estimated average request cost. 52 func (p PriceFactors) connectionPrice(cap uint64, avgReqCost float64) float64 { 53 return p.TimeFactor + float64(cap)*p.CapacityFactor/1000000 + p.RequestFactor*avgReqCost 54 } 55 56 type ( 57 // nodePriority interface provides current and estimated future priorities on demand 58 nodePriority interface { 59 // priority should return the current priority of the node (higher is better) 60 priority(cap uint64) int64 61 // estimatePriority should return a lower estimate for the minimum of the node priority 62 // value starting from the current moment until the given time. If the priority goes 63 // under the returned estimate before the specified moment then it is the caller's 64 // responsibility to signal with updateFlag. 65 estimatePriority(cap uint64, addBalance int64, future, bias time.Duration, update bool) int64 66 } 67 68 // ReadOnlyBalance provides read-only operations on the node balance 69 ReadOnlyBalance interface { 70 nodePriority 71 GetBalance() (uint64, uint64) 72 GetRawBalance() (utils.ExpiredValue, utils.ExpiredValue) 73 GetPriceFactors() (posFactor, negFactor PriceFactors) 74 } 75 76 // ConnectedBalance provides operations permitted on connected nodes (non-read-only 77 // operations are not permitted inside a BalanceOperation) 78 ConnectedBalance interface { 79 ReadOnlyBalance 80 SetPriceFactors(posFactor, negFactor PriceFactors) 81 RequestServed(cost uint64) uint64 82 } 83 84 // AtomicBalanceOperator provides operations permitted in an atomic BalanceOperation 85 AtomicBalanceOperator interface { 86 ReadOnlyBalance 87 AddBalance(amount int64) (uint64, uint64, error) 88 SetBalance(pos, neg uint64) error 89 } 90 ) 91 92 // nodeBalance keeps track of the positive and negative balances of a connected 93 // client and calculates actual and projected future priority values. 94 // Implements nodePriority interface. 95 type nodeBalance struct { 96 bt *balanceTracker 97 lock sync.RWMutex 98 node *enode.Node 99 connAddress string 100 active, hasPriority, setFlags bool 101 capacity uint64 102 balance balance 103 posFactor, negFactor PriceFactors 104 sumReqCost uint64 105 lastUpdate, nextUpdate, initTime mclock.AbsTime 106 updateEvent mclock.Timer 107 // since only a limited and fixed number of callbacks are needed, they are 108 // stored in a fixed size array ordered by priority threshold. 109 callbacks [balanceCallbackCount]balanceCallback 110 // callbackIndex maps balanceCallback constants to callbacks array indexes (-1 if not active) 111 callbackIndex [balanceCallbackCount]int 112 callbackCount int // number of active callbacks 113 } 114 115 // balance represents a pair of positive and negative balances 116 type balance struct { 117 pos, neg utils.ExpiredValue 118 posExp, negExp utils.ValueExpirer 119 } 120 121 // posValue returns the value of positive balance at a given timestamp. 122 func (b balance) posValue(now mclock.AbsTime) uint64 { 123 return b.pos.Value(b.posExp.LogOffset(now)) 124 } 125 126 // negValue returns the value of negative balance at a given timestamp. 127 func (b balance) negValue(now mclock.AbsTime) uint64 { 128 return b.neg.Value(b.negExp.LogOffset(now)) 129 } 130 131 // addValue adds the value of a given amount to the balance. The original value and 132 // updated value will also be returned if the addition is successful. 133 // Returns the error if the given value is too large and the value overflows. 134 func (b *balance) addValue(now mclock.AbsTime, amount int64, pos bool, force bool) (uint64, uint64, int64, error) { 135 var ( 136 val utils.ExpiredValue 137 offset utils.Fixed64 138 ) 139 if pos { 140 offset, val = b.posExp.LogOffset(now), b.pos 141 } else { 142 offset, val = b.negExp.LogOffset(now), b.neg 143 } 144 old := val.Value(offset) 145 if amount > 0 && (amount > maxBalance || old > maxBalance-uint64(amount)) { 146 if !force { 147 return old, 0, 0, errBalanceOverflow 148 } 149 val = utils.ExpiredValue{} 150 amount = maxBalance 151 } 152 net := val.Add(amount, offset) 153 if pos { 154 b.pos = val 155 } else { 156 b.neg = val 157 } 158 return old, val.Value(offset), net, nil 159 } 160 161 // setValue sets the internal balance amount to the given values. Returns the 162 // error if the given value is too large. 163 func (b *balance) setValue(now mclock.AbsTime, pos uint64, neg uint64) error { 164 if pos > maxBalance || neg > maxBalance { 165 return errBalanceOverflow 166 } 167 var pb, nb utils.ExpiredValue 168 pb.Add(int64(pos), b.posExp.LogOffset(now)) 169 nb.Add(int64(neg), b.negExp.LogOffset(now)) 170 b.pos = pb 171 b.neg = nb 172 return nil 173 } 174 175 // balanceCallback represents a single callback that is activated when client priority 176 // reaches the given threshold 177 type balanceCallback struct { 178 id int 179 threshold int64 180 callback func() 181 } 182 183 // GetBalance returns the current positive and negative balance. 184 func (n *nodeBalance) GetBalance() (uint64, uint64) { 185 n.lock.Lock() 186 defer n.lock.Unlock() 187 188 now := n.bt.clock.Now() 189 n.updateBalance(now) 190 return n.balance.posValue(now), n.balance.negValue(now) 191 } 192 193 // GetRawBalance returns the current positive and negative balance 194 // but in the raw(expired value) format. 195 func (n *nodeBalance) GetRawBalance() (utils.ExpiredValue, utils.ExpiredValue) { 196 n.lock.Lock() 197 defer n.lock.Unlock() 198 199 now := n.bt.clock.Now() 200 n.updateBalance(now) 201 return n.balance.pos, n.balance.neg 202 } 203 204 // AddBalance adds the given amount to the positive balance and returns the balance 205 // before and after the operation. Exceeding maxBalance results in an error (balance is 206 // unchanged) while adding a negative amount higher than the current balance results in 207 // zero balance. 208 // Note: this function should run inside a NodeStateMachine operation 209 func (n *nodeBalance) AddBalance(amount int64) (uint64, uint64, error) { 210 var ( 211 err error 212 old, new uint64 213 now = n.bt.clock.Now() 214 callbacks []func() 215 setPriority bool 216 ) 217 // Operation with holding the lock 218 n.bt.updateTotalBalance(n, func() bool { 219 n.updateBalance(now) 220 if old, new, _, err = n.balance.addValue(now, amount, true, false); err != nil { 221 return false 222 } 223 callbacks, setPriority = n.checkCallbacks(now), n.checkPriorityStatus() 224 n.storeBalance(true, false) 225 return true 226 }) 227 if err != nil { 228 return old, old, err 229 } 230 // Operation without holding the lock 231 for _, cb := range callbacks { 232 cb() 233 } 234 if n.setFlags { 235 if setPriority { 236 n.bt.ns.SetStateSub(n.node, n.bt.setup.priorityFlag, nodestate.Flags{}, 0) 237 } 238 // Note: priority flag is automatically removed by the zero priority callback if necessary 239 n.signalPriorityUpdate() 240 } 241 return old, new, nil 242 } 243 244 // SetBalance sets the positive and negative balance to the given values 245 // Note: this function should run inside a NodeStateMachine operation 246 func (n *nodeBalance) SetBalance(pos, neg uint64) error { 247 var ( 248 now = n.bt.clock.Now() 249 callbacks []func() 250 setPriority bool 251 ) 252 // Operation with holding the lock 253 n.bt.updateTotalBalance(n, func() bool { 254 n.updateBalance(now) 255 if err := n.balance.setValue(now, pos, neg); err != nil { 256 return false 257 } 258 callbacks, setPriority = n.checkCallbacks(now), n.checkPriorityStatus() 259 n.storeBalance(true, true) 260 return true 261 }) 262 // Operation without holding the lock 263 for _, cb := range callbacks { 264 cb() 265 } 266 if n.setFlags { 267 if setPriority { 268 n.bt.ns.SetStateSub(n.node, n.bt.setup.priorityFlag, nodestate.Flags{}, 0) 269 } 270 // Note: priority flag is automatically removed by the zero priority callback if necessary 271 n.signalPriorityUpdate() 272 } 273 return nil 274 } 275 276 // RequestServed should be called after serving a request for the given peer 277 func (n *nodeBalance) RequestServed(cost uint64) (newBalance uint64) { 278 n.lock.Lock() 279 280 var ( 281 check bool 282 fcost = float64(cost) 283 now = n.bt.clock.Now() 284 ) 285 n.updateBalance(now) 286 if !n.balance.pos.IsZero() { 287 posCost := -int64(fcost * n.posFactor.RequestFactor) 288 if posCost == 0 { 289 fcost = 0 290 newBalance = n.balance.posValue(now) 291 } else { 292 var net int64 293 _, newBalance, net, _ = n.balance.addValue(now, posCost, true, false) 294 if posCost == net { 295 fcost = 0 296 } else { 297 fcost *= 1 - float64(net)/float64(posCost) 298 } 299 check = true 300 } 301 } 302 if fcost > 0 && n.negFactor.RequestFactor != 0 { 303 n.balance.addValue(now, int64(fcost*n.negFactor.RequestFactor), false, false) 304 check = true 305 } 306 n.sumReqCost += cost 307 308 var callbacks []func() 309 if check { 310 callbacks = n.checkCallbacks(now) 311 } 312 n.lock.Unlock() 313 314 if callbacks != nil { 315 n.bt.ns.Operation(func() { 316 for _, cb := range callbacks { 317 cb() 318 } 319 }) 320 } 321 return 322 } 323 324 // priority returns the actual priority based on the current balance 325 func (n *nodeBalance) priority(capacity uint64) int64 { 326 n.lock.Lock() 327 defer n.lock.Unlock() 328 329 now := n.bt.clock.Now() 330 n.updateBalance(now) 331 return n.balanceToPriority(now, n.balance, capacity) 332 } 333 334 // EstMinPriority gives a lower estimate for the priority at a given time in the future. 335 // An average request cost per time is assumed that is twice the average cost per time 336 // in the current session. 337 // If update is true then a priority callback is added that turns updateFlag on and off 338 // in case the priority goes below the estimated minimum. 339 func (n *nodeBalance) estimatePriority(capacity uint64, addBalance int64, future, bias time.Duration, update bool) int64 { 340 n.lock.Lock() 341 defer n.lock.Unlock() 342 343 now := n.bt.clock.Now() 344 n.updateBalance(now) 345 346 b := n.balance // copy the balance 347 if addBalance != 0 { 348 b.addValue(now, addBalance, true, true) 349 } 350 if future > 0 { 351 var avgReqCost float64 352 dt := time.Duration(n.lastUpdate - n.initTime) 353 if dt > time.Second { 354 avgReqCost = float64(n.sumReqCost) * 2 / float64(dt) 355 } 356 b = n.reducedBalance(b, now, future, capacity, avgReqCost) 357 } 358 if bias > 0 { 359 b = n.reducedBalance(b, now+mclock.AbsTime(future), bias, capacity, 0) 360 } 361 pri := n.balanceToPriority(now, b, capacity) 362 // Ensure that biased estimates are always lower than actual priorities, even if 363 // the bias is very small. 364 // This ensures that two nodes will not ping-pong update signals forever if both of 365 // them have zero estimated priority drop in the projected future. 366 current := n.balanceToPriority(now, n.balance, capacity) 367 if pri >= current { 368 pri = current - 1 369 } 370 if update { 371 n.addCallback(balanceCallbackUpdate, pri, n.signalPriorityUpdate) 372 } 373 return pri 374 } 375 376 // SetPriceFactors sets the price factors. TimeFactor is the price of a nanosecond of 377 // connection while RequestFactor is the price of a request cost unit. 378 func (n *nodeBalance) SetPriceFactors(posFactor, negFactor PriceFactors) { 379 n.lock.Lock() 380 now := n.bt.clock.Now() 381 n.updateBalance(now) 382 n.posFactor, n.negFactor = posFactor, negFactor 383 callbacks := n.checkCallbacks(now) 384 n.lock.Unlock() 385 if callbacks != nil { 386 n.bt.ns.Operation(func() { 387 for _, cb := range callbacks { 388 cb() 389 } 390 }) 391 } 392 } 393 394 // GetPriceFactors returns the price factors 395 func (n *nodeBalance) GetPriceFactors() (posFactor, negFactor PriceFactors) { 396 n.lock.Lock() 397 defer n.lock.Unlock() 398 399 return n.posFactor, n.negFactor 400 } 401 402 // activate starts time/capacity cost deduction. 403 func (n *nodeBalance) activate() { 404 n.bt.updateTotalBalance(n, func() bool { 405 if n.active { 406 return false 407 } 408 n.active = true 409 n.lastUpdate = n.bt.clock.Now() 410 return true 411 }) 412 } 413 414 // deactivate stops time/capacity cost deduction and saves the balances in the database 415 func (n *nodeBalance) deactivate() { 416 n.bt.updateTotalBalance(n, func() bool { 417 if !n.active { 418 return false 419 } 420 n.updateBalance(n.bt.clock.Now()) 421 if n.updateEvent != nil { 422 n.updateEvent.Stop() 423 n.updateEvent = nil 424 } 425 n.storeBalance(true, true) 426 n.active = false 427 return true 428 }) 429 } 430 431 // updateBalance updates balance based on the time factor 432 func (n *nodeBalance) updateBalance(now mclock.AbsTime) { 433 if n.active && now > n.lastUpdate { 434 n.balance = n.reducedBalance(n.balance, n.lastUpdate, time.Duration(now-n.lastUpdate), n.capacity, 0) 435 n.lastUpdate = now 436 } 437 } 438 439 // storeBalance stores the positive and/or negative balance of the node in the database 440 func (n *nodeBalance) storeBalance(pos, neg bool) { 441 if pos { 442 n.bt.storeBalance(n.node.ID().Bytes(), false, n.balance.pos) 443 } 444 if neg { 445 n.bt.storeBalance([]byte(n.connAddress), true, n.balance.neg) 446 } 447 } 448 449 // addCallback sets up a one-time callback to be called when priority reaches 450 // the threshold. If it has already reached the threshold the callback is called 451 // immediately. 452 // Note: should be called while n.lock is held 453 // Note 2: the callback function runs inside a NodeStateMachine operation 454 func (n *nodeBalance) addCallback(id int, threshold int64, callback func()) { 455 n.removeCallback(id) 456 idx := 0 457 for idx < n.callbackCount && threshold > n.callbacks[idx].threshold { 458 idx++ 459 } 460 for i := n.callbackCount - 1; i >= idx; i-- { 461 n.callbackIndex[n.callbacks[i].id]++ 462 n.callbacks[i+1] = n.callbacks[i] 463 } 464 n.callbackCount++ 465 n.callbackIndex[id] = idx 466 n.callbacks[idx] = balanceCallback{id, threshold, callback} 467 now := n.bt.clock.Now() 468 n.updateBalance(now) 469 n.scheduleCheck(now) 470 } 471 472 // removeCallback removes the given callback and returns true if it was active 473 // Note: should be called while n.lock is held 474 func (n *nodeBalance) removeCallback(id int) bool { 475 idx := n.callbackIndex[id] 476 if idx == -1 { 477 return false 478 } 479 n.callbackIndex[id] = -1 480 for i := idx; i < n.callbackCount-1; i++ { 481 n.callbackIndex[n.callbacks[i+1].id]-- 482 n.callbacks[i] = n.callbacks[i+1] 483 } 484 n.callbackCount-- 485 return true 486 } 487 488 // checkCallbacks checks whether the threshold of any of the active callbacks 489 // have been reached and returns triggered callbacks. 490 // Note: checkCallbacks assumes that the balance has been recently updated. 491 func (n *nodeBalance) checkCallbacks(now mclock.AbsTime) (callbacks []func()) { 492 if n.callbackCount == 0 || n.capacity == 0 { 493 return 494 } 495 pri := n.balanceToPriority(now, n.balance, n.capacity) 496 for n.callbackCount != 0 && n.callbacks[n.callbackCount-1].threshold >= pri { 497 n.callbackCount-- 498 n.callbackIndex[n.callbacks[n.callbackCount].id] = -1 499 callbacks = append(callbacks, n.callbacks[n.callbackCount].callback) 500 } 501 n.scheduleCheck(now) 502 return 503 } 504 505 // scheduleCheck sets up or updates a scheduled event to ensure that it will be called 506 // again just after the next threshold has been reached. 507 func (n *nodeBalance) scheduleCheck(now mclock.AbsTime) { 508 if n.callbackCount != 0 { 509 d, ok := n.timeUntil(n.callbacks[n.callbackCount-1].threshold) 510 if !ok { 511 n.nextUpdate = 0 512 n.updateAfter(0) 513 return 514 } 515 if n.nextUpdate == 0 || n.nextUpdate > now+mclock.AbsTime(d) { 516 if d > time.Second { 517 // Note: if the scheduled update is not in the very near future then we 518 // schedule the update a bit earlier. This way we do need to update a few 519 // extra times but don't need to reschedule every time a processed request 520 // brings the expected firing time a little bit closer. 521 d = ((d - time.Second) * 7 / 8) + time.Second 522 } 523 n.nextUpdate = now + mclock.AbsTime(d) 524 n.updateAfter(d) 525 } 526 } else { 527 n.nextUpdate = 0 528 n.updateAfter(0) 529 } 530 } 531 532 // updateAfter schedules a balance update and callback check in the future 533 func (n *nodeBalance) updateAfter(dt time.Duration) { 534 if n.updateEvent == nil || n.updateEvent.Stop() { 535 if dt == 0 { 536 n.updateEvent = nil 537 } else { 538 n.updateEvent = n.bt.clock.AfterFunc(dt, func() { 539 var callbacks []func() 540 n.lock.Lock() 541 if n.callbackCount != 0 { 542 now := n.bt.clock.Now() 543 n.updateBalance(now) 544 callbacks = n.checkCallbacks(now) 545 } 546 n.lock.Unlock() 547 if callbacks != nil { 548 n.bt.ns.Operation(func() { 549 for _, cb := range callbacks { 550 cb() 551 } 552 }) 553 } 554 }) 555 } 556 } 557 } 558 559 // balanceExhausted should be called when the positive balance is exhausted (priority goes to zero/negative) 560 // Note: this function should run inside a NodeStateMachine operation 561 func (n *nodeBalance) balanceExhausted() { 562 n.lock.Lock() 563 n.storeBalance(true, false) 564 n.hasPriority = false 565 n.lock.Unlock() 566 if n.setFlags { 567 n.bt.ns.SetStateSub(n.node, nodestate.Flags{}, n.bt.setup.priorityFlag, 0) 568 } 569 } 570 571 // checkPriorityStatus checks whether the node has gained priority status and sets the priority 572 // callback and flag if necessary. It assumes that the balance has been recently updated. 573 // Note that the priority flag has to be set by the caller after the mutex has been released. 574 func (n *nodeBalance) checkPriorityStatus() bool { 575 if !n.hasPriority && !n.balance.pos.IsZero() { 576 n.hasPriority = true 577 n.addCallback(balanceCallbackZero, 0, func() { n.balanceExhausted() }) 578 return true 579 } 580 return false 581 } 582 583 // signalPriorityUpdate signals that the priority fell below the previous minimum estimate 584 // Note: this function should run inside a NodeStateMachine operation 585 func (n *nodeBalance) signalPriorityUpdate() { 586 n.bt.ns.SetStateSub(n.node, n.bt.setup.updateFlag, nodestate.Flags{}, 0) 587 n.bt.ns.SetStateSub(n.node, nodestate.Flags{}, n.bt.setup.updateFlag, 0) 588 } 589 590 // setCapacity updates the capacity value used for priority calculation 591 // Note: capacity should never be zero 592 // Note 2: this function should run inside a NodeStateMachine operation 593 func (n *nodeBalance) setCapacity(capacity uint64) { 594 n.lock.Lock() 595 now := n.bt.clock.Now() 596 n.updateBalance(now) 597 n.capacity = capacity 598 callbacks := n.checkCallbacks(now) 599 n.lock.Unlock() 600 for _, cb := range callbacks { 601 cb() 602 } 603 } 604 605 // balanceToPriority converts a balance to a priority value. Lower priority means 606 // first to disconnect. Positive balance translates to positive priority. If positive 607 // balance is zero then negative balance translates to a negative priority. 608 func (n *nodeBalance) balanceToPriority(now mclock.AbsTime, b balance, capacity uint64) int64 { 609 pos := b.posValue(now) 610 if pos > 0 { 611 return int64(pos / capacity) 612 } 613 return -int64(b.negValue(now)) 614 } 615 616 // priorityToBalance converts a target priority to a requested balance value. 617 // If the priority is negative, then minimal negative balance is returned; 618 // otherwise the minimal positive balance is returned. 619 func (n *nodeBalance) priorityToBalance(priority int64, capacity uint64) (uint64, uint64) { 620 if priority > 0 { 621 return uint64(priority) * n.capacity, 0 622 } 623 return 0, uint64(-priority) 624 } 625 626 // reducedBalance estimates the reduced balance at a given time in the fututre based 627 // on the given balance, the time factor and an estimated average request cost per time ratio 628 func (n *nodeBalance) reducedBalance(b balance, start mclock.AbsTime, dt time.Duration, capacity uint64, avgReqCost float64) balance { 629 // since the costs are applied continuously during the dt time period we calculate 630 // the expiration offset at the middle of the period 631 var ( 632 at = start + mclock.AbsTime(dt/2) 633 dtf = float64(dt) 634 ) 635 if !b.pos.IsZero() { 636 factor := n.posFactor.connectionPrice(capacity, avgReqCost) 637 diff := -int64(dtf * factor) 638 _, _, net, _ := b.addValue(at, diff, true, false) 639 if net == diff { 640 dtf = 0 641 } else { 642 dtf += float64(net) / factor 643 } 644 } 645 if dtf > 0 { 646 factor := n.negFactor.connectionPrice(capacity, avgReqCost) 647 b.addValue(at, int64(dtf*factor), false, false) 648 } 649 return b 650 } 651 652 // timeUntil calculates the remaining time needed to reach a given priority level 653 // assuming that no requests are processed until then. If the given level is never 654 // reached then (0, false) is returned. If it has already been reached then (0, true) 655 // is returned. 656 // Note: the function assumes that the balance has been recently updated and 657 // calculates the time starting from the last update. 658 func (n *nodeBalance) timeUntil(priority int64) (time.Duration, bool) { 659 var ( 660 now = n.bt.clock.Now() 661 pos = n.balance.posValue(now) 662 targetPos, targetNeg = n.priorityToBalance(priority, n.capacity) 663 diffTime float64 664 ) 665 if pos > 0 { 666 timePrice := n.posFactor.connectionPrice(n.capacity, 0) 667 if timePrice < 1e-100 { 668 return 0, false 669 } 670 if targetPos > 0 { 671 if targetPos > pos { 672 return 0, true 673 } 674 diffTime = float64(pos-targetPos) / timePrice 675 return time.Duration(diffTime), true 676 } else { 677 diffTime = float64(pos) / timePrice 678 } 679 } else { 680 if targetPos > 0 { 681 return 0, true 682 } 683 } 684 neg := n.balance.negValue(now) 685 if targetNeg > neg { 686 timePrice := n.negFactor.connectionPrice(n.capacity, 0) 687 if timePrice < 1e-100 { 688 return 0, false 689 } 690 diffTime += float64(targetNeg-neg) / timePrice 691 } 692 return time.Duration(diffTime), true 693 }