@@ -2,11 +2,17 @@ package ethutil
22
33import (
44 "context"
5+ "time"
56
67 "github.com/ethereum/go-ethereum/accounts/abi/bind"
78 "github.com/ethereum/go-ethereum/common"
89)
910
11+ // The time for which the nonce value cached locally is valid. The local copy
12+ // is invalidated after the certain duration to let the nonce recover in case
13+ // the mempool crashed before propagating the last transaction sent.
14+ const localNonceTrustDuration = 5 * time .Second
15+
1016// NonceManager tracks the nonce for the account and allows to update it after
1117// each successfully submitted transaction. Tracking the nonce locall is
1218// required when transactions are submitted from multiple goroutines or when
@@ -23,9 +29,10 @@ import (
2329// 4. Call IncrementNonce(),
2430// 5. Release transaction lock.
2531type NonceManager struct {
26- account common.Address
27- transactor bind.ContractTransactor
28- localNonce uint64
32+ account common.Address
33+ transactor bind.ContractTransactor
34+ localNonce uint64
35+ expirationDate time.Time
2936}
3037
3138// NewNonceManager creates NonceManager instance for the provided account using
@@ -45,7 +52,9 @@ func NewNonceManager(
4552
4653// CurrentNonce returns the nonce value that should be used for the next
4754// transaction. The nonce is evaluated as the higher value from the local
48- // nonce and pending nonce fetched from the Ethereum client.
55+ // nonce and pending nonce fetched from the Ethereum client. The local nonce
56+ // is cached for the specific duration. If the local nonce expired, the pending
57+ // nonce returned from the chain is used.
4958//
5059// CurrentNonce is NOT safe for concurrent use. It is up to the code using this
5160// function to provide the required synchronization, optionally including
@@ -59,17 +68,32 @@ func (nm *NonceManager) CurrentNonce() (uint64, error) {
5968 return 0 , err
6069 }
6170
71+ now := time .Now ()
72+
6273 if pendingNonce < nm .localNonce {
63- logger .Infof (
64- "local nonce [%v] is higher than pending [%v]; using the local one" ,
65- nm .localNonce ,
66- pendingNonce ,
67- )
74+ if now .Before (nm .expirationDate ) {
75+ logger .Infof (
76+ "local nonce [%v] is higher than pending [%v]; using the local one" ,
77+ nm .localNonce ,
78+ pendingNonce ,
79+ )
80+ } else {
81+ logger .Infof (
82+ "local nonce [%v] is higher than pending [%v] but local " +
83+ "nonce expired; updating local nonce" ,
84+ nm .localNonce ,
85+ pendingNonce ,
86+ )
87+
88+ nm .localNonce = pendingNonce
89+ }
6890 }
6991
92+ nm .expirationDate = now .Add (localNonceTrustDuration )
93+
7094 if pendingNonce > nm .localNonce {
7195 logger .Infof (
72- "local nonce [%v] is lower than pending [%v]; updating" ,
96+ "local nonce [%v] is lower than pending [%v]; updating local nonce " ,
7397 nm .localNonce ,
7498 pendingNonce ,
7599 )
0 commit comments