Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve parallel signature recover #7353

Merged
merged 1 commit into from
Aug 23, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 45 additions & 29 deletions src/Nethermind/Nethermind.Consensus/Processing/RecoverSignature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Nethermind.Core;
using Nethermind.Core.Specs;
Expand Down Expand Up @@ -36,72 +36,88 @@ public RecoverSignatures(IEthereumEcdsa? ecdsa, ITxPool? txPool, ISpecProvider?

public void RecoverData(Block block)
{
if (block.Transactions.Length == 0)
Transaction[] txs = block.Transactions;
if (txs.Length == 0)
return;

Transaction firstTx = block.Transactions[0];
Transaction firstTx = txs[0];
if (firstTx.IsSigned && firstTx.SenderAddress is not null)
// already recovered a sender for a signed tx in this block,
// so we assume the rest of txs in the block are already recovered
return;

Parallel.ForEach(
block.Transactions.Where(tx => !tx.IsHashCalculated),
blockTransaction =>
Parallel.For(0, txs.Length, i =>
{
Transaction tx = txs[i];
if (!tx.IsHashCalculated)
{
blockTransaction.CalculateHashInternal();
});
tx.CalculateHashInternal();
}
});

var releaseSpec = _specProvider.GetSpec(block.Header);

int recoverFromEcdsa = 0;
// Don't access txPool in Parallel loop as increases contention
foreach (Transaction blockTransaction in block.Transactions.Where(tx => tx.IsSigned && tx.SenderAddress is null))
foreach (Transaction tx in txs)
{
Transaction? transaction = null;
if (!ShouldRecoverSender(tx))
continue;

Transaction? poolTx = null;
try
{
_txPool.TryGetPendingTransaction(blockTransaction.Hash, out transaction);
_txPool.TryGetPendingTransaction(tx.Hash, out poolTx);
}
catch (Exception e)
{
if (_logger.IsError) _logger.Error($"An error occurred while getting a pending transaction from TxPool, Transaction: {blockTransaction}", e);
if (_logger.IsError) _logger.Error($"An error occurred while getting a pending transaction from TxPool, Transaction: {tx}", e);
}

Address sender = transaction?.SenderAddress;
Address sender = poolTx?.SenderAddress;
if (sender is not null)
{
blockTransaction.SenderAddress = sender;
tx.SenderAddress = sender;

if (_logger.IsTrace) _logger.Trace($"Recovered {blockTransaction.SenderAddress} sender for {blockTransaction.Hash} (tx pool cached value: {sender})");
if (_logger.IsTrace) _logger.Trace($"Recovered {tx.SenderAddress} sender for {tx.Hash} (tx pool cached value: {sender})");
}
else
{
recoverFromEcdsa++;
}
}

if (recoverFromEcdsa >= 4)
if (recoverFromEcdsa == 0)
return;

bool useSignatureChainId = !_specProvider.GetSpec(block.Header).ValidateChainId;
if (recoverFromEcdsa > 3)
{
// Recover ecdsa in Parallel
Parallel.ForEach(
block.Transactions.Where(tx => tx.IsSigned && tx.SenderAddress is null),
blockTransaction =>
{
blockTransaction.SenderAddress = _ecdsa.RecoverAddress(blockTransaction, !releaseSpec.ValidateChainId);

if (_logger.IsTrace) _logger.Trace($"Recovered {blockTransaction.SenderAddress} sender for {blockTransaction.Hash}");
});
Parallel.For(0, txs.Length, i =>
{
Transaction tx = txs[i];
if (!ShouldRecoverSender(tx)) return;

tx.SenderAddress = _ecdsa.RecoverAddress(tx, useSignatureChainId);

if (_logger.IsTrace) _logger.Trace($"Recovered {tx.SenderAddress} sender for {tx.Hash}");
});
}
else if (recoverFromEcdsa > 0)
else
{
foreach (Transaction blockTransaction in block.Transactions.Where(tx => tx.IsSigned && tx.SenderAddress is null))
foreach (Transaction tx in txs)
{
blockTransaction.SenderAddress = _ecdsa.RecoverAddress(blockTransaction, !releaseSpec.ValidateChainId);
if (!ShouldRecoverSender(tx)) continue;

tx.SenderAddress = _ecdsa.RecoverAddress(tx, useSignatureChainId);

if (_logger.IsTrace) _logger.Trace($"Recovered {blockTransaction.SenderAddress} sender for {blockTransaction.Hash}");
if (_logger.IsTrace) _logger.Trace($"Recovered {tx.SenderAddress} sender for {tx.Hash}");
}
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static bool ShouldRecoverSender(Transaction tx)
=> tx.IsSigned && tx.SenderAddress is null;
}
}