@@ -199,6 +199,8 @@ type faucet struct {
199199
200200 keystore * keystore.KeyStore // Keystore containing the single signer
201201 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
202204 nonce uint64 // Current pending nonce of the faucet
203205 price * big.Int // Current gas price to issue funds with
204206
@@ -324,33 +326,30 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
324326 nonce uint64
325327 err error
326328 )
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 )
336334 }
337- cancel ()
335+ if f .balance != nil {
336+ balance = new (big.Int ).Set (f .balance )
337+ }
338+ nonce = f .nonce
339+ f .lock .RUnlock ()
338340
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 {
342344 log .Warn ("Failed to send faucet error to client" , "err" , err )
343345 return
344346 }
345347 time .Sleep (3 * time .Second )
346- continue
347348 }
348- // Initial stats reported successfully, proceed with user interaction
349- break
350349 }
351350 // Send over the initial stats and the latest header
352351 if err = send (conn , map [string ]interface {}{
353- "funds" : balance .Div (balance , ether ),
352+ "funds" : new (big. Int ) .Div (balance , ether ),
354353 "funded" : nonce ,
355354 "peers" : f .stack .Server ().PeerCount (),
356355 "requests" : f .reqs ,
@@ -520,6 +519,47 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
520519 }
521520}
522521
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+
523563// loop keeps waiting for interesting events and pushes them out to connected
524564// websockets.
525565func (f * faucet ) loop () {
@@ -537,45 +577,27 @@ func (f *faucet) loop() {
537577 go func () {
538578 for head := range update {
539579 // 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
553584 }
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 {
558586 log .Warn ("Failed to update faucet state" , "block" , head .Number , "hash" , head .Hash (), "err" , err )
559587 continue
560- } else {
561- log .Info ("Updated faucet state" , "block" , head .Number , "hash" , head .Hash (), "balance" , balance , "nonce" , nonce , "price" , price )
562588 }
563589 // 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 )
565592
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 ()
572595
573- f .lock .RLock ()
574596 for _ , conn := range f .conns {
575597 if err := send (conn , map [string ]interface {}{
576598 "funds" : balance ,
577599 "funded" : f .nonce ,
578- "peers" : f . stack . Server (). PeerCount () ,
600+ "peers" : peers ,
579601 "requests" : f .reqs ,
580602 }, time .Second ); err != nil {
581603 log .Warn ("Failed to send stats to client" , "err" , err )
0 commit comments