Skip to content

Commit

Permalink
Nonce error fix (#55)
Browse files Browse the repository at this point in the history
* noncer fix

* fix

* fix

* run linter

* bet

* add node

* bet

* start

---------

Co-authored-by: itsdevbear <[email protected]>
  • Loading branch information
hunter-bera and itsdevbear authored Jan 24, 2024
1 parent f011c6b commit 40484dd
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 1 deletion.
1 change: 1 addition & 0 deletions client/eth/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ type Reader interface {
HeaderByNumber(ctx context.Context, number *big.Int) (*ethcoretypes.Header, error)
PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error)
PendingNonceAt(ctx context.Context, account common.Address) (uint64, error)
NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error)
SendTransaction(ctx context.Context, tx *ethcoretypes.Transaction) error
SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery,
ch chan<- ethcoretypes.Log) (ethereum.Subscription, error)
Expand Down
9 changes: 9 additions & 0 deletions client/eth/client_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,15 @@ func (c *ChainProviderImpl) PendingNonceAt(
return 0, ErrClientNotFound
}

// PendingNonceAt returns the nonce of the given account in the pending state.
func (c *ChainProviderImpl) NonceAt(
ctx context.Context, account common.Address, bn *big.Int) (uint64, error) {
if client, ok := c.GetHTTP(); ok {
return client.NonceAt(ctx, account, bn)
}
return 0, ErrClientNotFound
}

// SendTransaction sends the given transaction.
func (c *ChainProviderImpl) SendTransaction(
ctx context.Context, tx *types.Transaction) error {
Expand Down
41 changes: 40 additions & 1 deletion core/transactor/tracker/noncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package tracker
import (
"context"
"sync"
"time"

"github.com/berachain/offchain-sdk/client/eth"
"github.com/huandu/skiplist"
Expand All @@ -17,6 +18,8 @@ type Noncer struct {
acquired *skiplist.SkipList // The list of acquired nonces.
inFlight *skiplist.SkipList // The list of nonces currently in flight.
mu sync.Mutex // Mutex for thread-safe operations.

latestConfirmedNonce uint64
}

// NewNoncer creates a new Noncer instance.
Expand All @@ -29,6 +32,28 @@ func NewNoncer(sender common.Address) *Noncer {
}
}

func (n *Noncer) RefreshLoop(ctx context.Context) {
go func() {
timer := time.NewTimer(5 * time.Second) //nolint:gomnd // fix later.
for {
select {
case <-ctx.Done():
return
case <-timer.C:
n.refreshConfirmedNonce(ctx)
}
}
}()
}

func (n *Noncer) refreshConfirmedNonce(ctx context.Context) {
latestConfirmedNonce, err := n.ethClient.NonceAt(ctx, n.sender, nil)
if err != nil {
return
}
n.latestConfirmedNonce = latestConfirmedNonce
}

// Start initiates the nonce synchronization.
func (n *Noncer) SetClient(ethClient eth.Client) {
n.ethClient = ethClient
Expand All @@ -46,8 +71,22 @@ func (n *Noncer) Acquire(ctx context.Context) (uint64, error) {
val := n.inFlight.Back()

var nextNonce uint64
foundGap := false
if val != nil {
nextNonce = val.Value.(*InFlightTx).Nonce() + 1
// Iterate through the inFlight objects to ensure there are no gaps
// TODO: convert to use a binary tree to go from O(n) to O(log(n))
for i := n.latestConfirmedNonce; i <= val.Value.(*InFlightTx).Nonce(); i++ {
if n.inFlight.Get(i) == nil {
// If a gap is found, use that
nextNonce = i
foundGap = false
break
}
}
// If we didn't find a gap, use the next nonce.
if !foundGap {
nextNonce = val.Value.(*InFlightTx).Nonce() + 1
}
} else {
var err error
// TODO: doing a network call while holding the lock is a bit dangerous
Expand Down
1 change: 1 addition & 0 deletions core/transactor/transactor.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ func (t *TxrV2) SendTxRequest(txReq *types.TxRequest) (string, error) {
// Start starts the transactor.
func (t *TxrV2) Start(ctx context.Context) {
go t.mainLoop(ctx)
go t.noncer.RefreshLoop(ctx)
}

// mainLoop is the main transaction sending / batching loop.
Expand Down

0 comments on commit 40484dd

Please sign in to comment.