Skip to content

Commit

Permalink
make NewTxsEvent channel in simualted_beacon_api a buffered channel o…
Browse files Browse the repository at this point in the history
…f capacity 15, to prevent it from deadlocking on nitro-tests
  • Loading branch information
ganeshvanahalli committed Apr 26, 2024
1 parent 39f0d7b commit 498311d
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 1 deletion.
5 changes: 4 additions & 1 deletion eth/catalyst/simulated_beacon_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ type api struct {

func (a *api) loop() {
var (
newTxs = make(chan core.NewTxsEvent)
// Arbitrum: we need to make newTxs a buffered channel because by the current design of simulated beacon
// it would deadlock with this cycle a.sim.Commit() -> txpool.Sync() -> subpools reset -> update feeds (newTxs is one of the recievers)

Check failure on line 36 in eth/catalyst/simulated_beacon_api.go

View workflow job for this annotation

GitHub Actions / run-linter

`recievers` is a misspelling of `receivers` (misspell)
// Note: capacity of this channel should be the worst-case estimate of number of transactions all arriving simultaneously to the pool
newTxs = make(chan core.NewTxsEvent, 15)
sub = a.sim.eth.TxPool().SubscribeTransactions(newTxs, true)
)
defer sub.Unsubscribe()
Expand Down
61 changes: 61 additions & 0 deletions eth/catalyst/simulated_beacon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,64 @@ func TestSimulatedBeaconSendWithdrawals(t *testing.T) {
}
}
}

func TestSimulatedBeaconAPIDeadlocksInExtremeConditions(t *testing.T) {
txs := make(map[common.Hash]types.Transaction)

var (
// testKey is a private key to use for funding a tester account.
testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")

// testAddr is the Ethereum address of the tester account.
testAddr = crypto.PubkeyToAddress(testKey.PublicKey)
)

// short period (1 second) for testing purposes
var gasLimit uint64 = 10_000_000
genesis := core.DeveloperGenesisBlock(gasLimit, &testAddr)
node, ethService, mock := startSimulatedBeaconEthService(t, genesis)
_ = mock
defer node.Close()

// simulated beacon api
mockApi := &api{mock}
go mockApi.loop()

chainHeadCh := make(chan core.ChainHeadEvent, 10)
subscription := ethService.BlockChain().SubscribeChainHeadEvent(chainHeadCh)
defer subscription.Unsubscribe()

// generate a bunch of transactions to overload simulated beacon api
// current capacity of core.NewTxsEvent channel is 15, we send 30 txs
signer := types.NewEIP155Signer(ethService.BlockChain().Config().ChainID)
for i := 0; i < 30; i++ {
tx, err := types.SignTx(types.NewTransaction(uint64(i), common.Address{}, big.NewInt(1000), params.TxGas, big.NewInt(params.InitialBaseFee), nil), signer, testKey)
if err != nil {
t.Fatalf("error signing transaction, err=%v", err)
}
txs[tx.Hash()] = *tx

if err := ethService.APIBackend.SendTx(context.Background(), tx); err != nil {
t.Fatal("SendTx failed", err)
}
}

includedTxs := make(map[common.Hash]struct{})

timer := time.NewTimer(12 * time.Second)
for {
select {
case evt := <-chainHeadCh:
for _, includedTx := range evt.Block.Transactions() {
includedTxs[includedTx.Hash()] = struct{}{}
}

// ensure all withdrawals/txs included. this will take two blocks b/c number of withdrawals > 10
if len(includedTxs) == len(txs) {
t.Fatal("all txs were included, the simulated beacon api did not deadlock")
}
case <-timer.C:
return
}
}
}

0 comments on commit 498311d

Please sign in to comment.