@@ -199,6 +199,8 @@ type faucet struct {
199
199
200
200
keystore * keystore.KeyStore // Keystore containing the single signer
201
201
account accounts.Account // Account funding user faucet requests
202
+ head * types.Header // Current head header of the faucet
203
+ balance * big.Int // Current balance of the faucet
202
204
nonce uint64 // Current pending nonce of the faucet
203
205
price * big.Int // Current gas price to issue funds with
204
206
@@ -324,33 +326,30 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
324
326
nonce uint64
325
327
err error
326
328
)
327
- for {
328
- // Attempt to retrieve the stats, may error on no faucet connectivity
329
- ctx , cancel := context .WithTimeout (context .Background (), 3 * time .Second )
330
- head , err = f .client .HeaderByNumber (ctx , nil )
331
- if err == nil {
332
- balance , err = f .client .BalanceAt (ctx , f .account .Address , head .Number )
333
- if err == nil {
334
- nonce , err = f .client .NonceAt (ctx , f .account .Address , nil )
335
- }
329
+ for head == nil || balance == nil {
330
+ // Retrieve the current stats cached by the faucet
331
+ f .lock .RLock ()
332
+ if f .head != nil {
333
+ head = types .CopyHeader (f .head )
336
334
}
337
- cancel ()
335
+ if f .balance != nil {
336
+ balance = new (big.Int ).Set (f .balance )
337
+ }
338
+ nonce = f .nonce
339
+ f .lock .RUnlock ()
338
340
339
- // If stats retrieval failed, wait a bit and retry
340
- if err != nil {
341
- if err = sendError (conn , errors .New ("Faucet offline: " + err . Error () )); err != nil {
341
+ if head == nil || balance == nil {
342
+ // Report the faucet offline until initial stats are ready
343
+ if err = sendError (conn , errors .New ("Faucet offline" )); err != nil {
342
344
log .Warn ("Failed to send faucet error to client" , "err" , err )
343
345
return
344
346
}
345
347
time .Sleep (3 * time .Second )
346
- continue
347
348
}
348
- // Initial stats reported successfully, proceed with user interaction
349
- break
350
349
}
351
350
// Send over the initial stats and the latest header
352
351
if err = send (conn , map [string ]interface {}{
353
- "funds" : balance .Div (balance , ether ),
352
+ "funds" : new (big. Int ) .Div (balance , ether ),
354
353
"funded" : nonce ,
355
354
"peers" : f .stack .Server ().PeerCount (),
356
355
"requests" : f .reqs ,
@@ -520,6 +519,47 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
520
519
}
521
520
}
522
521
522
+ // refresh attempts to retrieve the latest header from the chain and extract the
523
+ // associated faucet balance and nonce for connectivity caching.
524
+ func (f * faucet ) refresh (head * types.Header ) error {
525
+ // Ensure a state update does not run for too long
526
+ ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
527
+ defer cancel ()
528
+
529
+ // If no header was specified, use the current chain head
530
+ var err error
531
+ if head == nil {
532
+ if head , err = f .client .HeaderByNumber (ctx , nil ); err != nil {
533
+ return err
534
+ }
535
+ }
536
+ // Retrieve the balance, nonce and gas price from the current head
537
+ var (
538
+ balance * big.Int
539
+ nonce uint64
540
+ price * big.Int
541
+ )
542
+ if balance , err = f .client .BalanceAt (ctx , f .account .Address , head .Number ); err != nil {
543
+ return err
544
+ }
545
+ if nonce , err = f .client .NonceAt (ctx , f .account .Address , head .Number ); err != nil {
546
+ return err
547
+ }
548
+ if price , err = f .client .SuggestGasPrice (ctx ); err != nil {
549
+ return err
550
+ }
551
+ // Everything succeeded, update the cached stats and eject old requests
552
+ f .lock .Lock ()
553
+ f .head , f .balance = head , balance
554
+ f .price , f .nonce = price , nonce
555
+ for len (f .reqs ) > 0 && f .reqs [0 ].Tx .Nonce () < f .nonce {
556
+ f .reqs = f .reqs [1 :]
557
+ }
558
+ f .lock .Unlock ()
559
+
560
+ return nil
561
+ }
562
+
523
563
// loop keeps waiting for interesting events and pushes them out to connected
524
564
// websockets.
525
565
func (f * faucet ) loop () {
@@ -537,45 +577,27 @@ func (f *faucet) loop() {
537
577
go func () {
538
578
for head := range update {
539
579
// New chain head arrived, query the current stats and stream to clients
540
- var (
541
- balance * big.Int
542
- nonce uint64
543
- price * big.Int
544
- err error
545
- )
546
- ctx , cancel := context .WithTimeout (context .Background (), 5 * time .Second )
547
- balance , err = f .client .BalanceAt (ctx , f .account .Address , head .Number )
548
- if err == nil {
549
- nonce , err = f .client .NonceAt (ctx , f .account .Address , nil )
550
- if err == nil {
551
- price , err = f .client .SuggestGasPrice (ctx )
552
- }
580
+ timestamp := time .Unix (head .Time .Int64 (), 0 )
581
+ if time .Since (timestamp ) > time .Hour {
582
+ log .Warn ("Skipping faucet refresh, head too old" , "number" , head .Number , "hash" , head .Hash (), "age" , common .PrettyAge (timestamp ))
583
+ continue
553
584
}
554
- cancel ()
555
-
556
- // If querying the data failed, try for the next block
557
- if err != nil {
585
+ if err := f .refresh (head ); err != nil {
558
586
log .Warn ("Failed to update faucet state" , "block" , head .Number , "hash" , head .Hash (), "err" , err )
559
587
continue
560
- } else {
561
- log .Info ("Updated faucet state" , "block" , head .Number , "hash" , head .Hash (), "balance" , balance , "nonce" , nonce , "price" , price )
562
588
}
563
589
// Faucet state retrieved, update locally and send to clients
564
- balance = new (big.Int ).Div (balance , ether )
590
+ f .lock .RLock ()
591
+ log .Info ("Updated faucet state" , "number" , head .Number , "hash" , head .Hash (), "age" , common .PrettyAge (timestamp ), "balance" , f .balance , "nonce" , f .nonce , "price" , f .price )
565
592
566
- f .lock .Lock ()
567
- f .price , f .nonce = price , nonce
568
- for len (f .reqs ) > 0 && f .reqs [0 ].Tx .Nonce () < f .nonce {
569
- f .reqs = f .reqs [1 :]
570
- }
571
- f .lock .Unlock ()
593
+ balance := new (big.Int ).Div (f .balance , ether )
594
+ peers := f .stack .Server ().PeerCount ()
572
595
573
- f .lock .RLock ()
574
596
for _ , conn := range f .conns {
575
597
if err := send (conn , map [string ]interface {}{
576
598
"funds" : balance ,
577
599
"funded" : f .nonce ,
578
- "peers" : f . stack . Server (). PeerCount () ,
600
+ "peers" : peers ,
579
601
"requests" : f .reqs ,
580
602
}, time .Second ); err != nil {
581
603
log .Warn ("Failed to send stats to client" , "err" , err )
0 commit comments