From 250a8d3fcd568ea8b3d5080d16e1dfab9cbd15b5 Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Thu, 20 Nov 2025 11:34:53 +0100 Subject: [PATCH 01/15] Adds Sign tx handling : draft --- ...sor.BlockValidationTransactionsExecutor.cs | 2 +- .../Nethermind.Xdc/ContractsUtils.cs | 191 ++++++++++++++++++ .../Nethermind.Xdc/Spec/XdcReleaseSpec.cs | 5 + .../XdcSpecialTransactionExecutor.cs | 104 ++++++++++ 4 files changed, 301 insertions(+), 1 deletion(-) create mode 100644 src/Nethermind/Nethermind.Xdc/ContractsUtils.cs create mode 100644 src/Nethermind/Nethermind.Xdc/XdcSpecialTransactionExecutor.cs diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs index d40b4067e56..c6e7a5991c1 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockValidationTransactionsExecutor.cs @@ -29,7 +29,7 @@ public void SetBlockExecutionContext(in BlockExecutionContext blockExecutionCont transactionProcessor.SetBlockExecutionContext(in blockExecutionContext); } - public TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processingOptions, BlockReceiptsTracer receiptsTracer, CancellationToken token) + public virtual TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processingOptions, BlockReceiptsTracer receiptsTracer, CancellationToken token) { Metrics.ResetBlockStats(); diff --git a/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs b/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs new file mode 100644 index 00000000000..cfa333fc116 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs @@ -0,0 +1,191 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using DotNetty.Common.Utilities; +using Microsoft.AspNetCore.Components.Forms; +using Microsoft.AspNetCore.DataProtection.KeyManagement; +using Nethermind.Consensus; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Crypto; +using Nethermind.Db; +using Nethermind.Evm.State; +using Nethermind.Int256; +using Nethermind.TxPool; +using Nethermind.Wallet; +using Nethermind.Xdc.Spec; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading.Tasks; +using static Microsoft.FSharp.Core.ByRefKinds; +using static System.Net.Mime.MediaTypeNames; + +namespace Nethermind.Xdc; + +internal static class ContractsUtils +{ + const string HexSignMethod = "e341eaa4"; + const string HexSetSecret = "34d38600"; + const string HexSetOpening = "e11f5ba2"; + public static async Task CreateTransactionSign(XdcBlockHeader header, ISigner signer, IDb stateDb, ITxPool txPool, IXdcReleaseSpec spec) + { + UInt256 nonce = txPool.GetLatestPendingNonce(signer.Address); + Transaction transaction = CreateTxSign((UInt256)header.Number, header.Hash, nonce, spec.BlockSignersAddress); + + await signer.Sign(transaction); + + // add local somehow to tx pool + bool added = txPool.SubmitTx(transaction, TxHandlingOptions.None); + if(!added) + { + throw new Exception("Failed to add signed transaction to the pool."); + } + + long blockNumber = header.Number; + long checkNumber = blockNumber % spec.EpochLength; + + PrivateKeyGenerator privateKeyGenerator = new PrivateKeyGenerator(); + PrivateKey randomPrivate = privateKeyGenerator.Generate(); + + byte[] randomKey = Encoding.UTF8.GetBytes("randomizeKey"); + + var exists = stateDb.KeyExists(randomKey); + + if (exists) + { + if(checkNumber > 0 && spec.EpochBlockOpening <= checkNumber && spec.EpochBlockRandomize >= checkNumber) + { + var randomizeKeyValue = stateDb.Get(randomKey); + Transaction tx = CreateTxOpeningRandomize(nonce + 1, spec.RandomizeSMCBinary, randomizeKeyValue); + await signer.Sign(tx); + + // add local somehow to tx pool + bool addedOpening = txPool.SubmitTx(tx, TxHandlingOptions.None); + + stateDb.Remove(randomKey); + } + } + else + { + var randomizeKeyValue = RandStringByte(32); + if (checkNumber > 0 && spec.EpochBlockOpening <= checkNumber && spec.EpochBlockRandomize > checkNumber) + { + Transaction tx = BuildTxSecretRandomize(nonce + 1, spec.RandomizeSMCBinary, (ulong)spec.EpochLength, randomizeKeyValue); + await signer.Sign(tx); + // add local somehow to tx pool + bool addedOpening = txPool.SubmitTx(tx, TxHandlingOptions.None); + stateDb.PutSpan(randomKey, randomizeKeyValue); + + } + } + } + + private static Transaction CreateTxSign(UInt256 number, Hash256 hash, UInt256 nonce, Address blockSignersAddress) + { + var functionSelector = Bytes.FromHexString(HexSignMethod); // hexSetSecret like "0x..." (method id +) + byte[] inputData = [.. functionSelector, .. number.PaddedBytes(32), .. hash.Bytes.PadLeft(32)]; + + var transaction = new Transaction(); + transaction.Nonce = nonce; + transaction.To = blockSignersAddress; + transaction.Value = 0; + transaction.GasLimit = 200_000; + transaction.GasPrice = 0; + transaction.Data = inputData; + + return transaction; + } + + private static Transaction CreateTxOpeningRandomize(UInt256 nonce, Address randomizeSMCBinary, byte[] randomizeKey) + { + var functionSelector = Bytes.FromHexString(HexSetOpening); // hexSetSecret like "0x..." (method id +) + byte[] inputData = [.. functionSelector, .. randomizeKey]; + + var transaction = new Transaction(); + transaction.Nonce = nonce; + transaction.To = randomizeSMCBinary; + transaction.Value = 0; + transaction.GasLimit = 200_000; + transaction.GasPrice = 0; + transaction.Data = inputData; + + return transaction; + } + + public static Transaction BuildTxSecretRandomize(UInt256 nonce, Address randomizeSMCBinary, ulong epochNumber, byte[] randomizeKey) + { + var functionSelector = Bytes.FromHexString(HexSetSecret); // hexSetSecret like "0x..." (method id +) + var secretNumb = RandomNumberGenerator.GetInt32((int)epochNumber); + + var secrets = new long[] { secretNumb }; + const int sizeOfArray = 32; + + var arrSizeOfSecrets = (UInt256)sizeOfArray; + var arrLengthOfSecrets = (UInt256)secrets.Length; + + List input = [.. functionSelector, .. arrSizeOfSecrets.PaddedBytes(32), .. arrLengthOfSecrets.PaddedBytes(32)]; + + foreach (var secret in secrets) + { + var enc = Encrypt(randomizeKey, secret.ToString()); // base64-url string + var encBytes = Encoding.UTF8.GetBytes(enc); + var padded = encBytes.PadLeft(sizeOfArray); + input.AddRange(padded); + } + + var inputData = input.ToArray(); + // Build TransactionInput (no from set here). Caller may sign/create raw tx with nonce/gas/value. + var transaction = new Transaction(); + transaction.Nonce = nonce; + transaction.To = randomizeSMCBinary; + transaction.Value = 0; + transaction.GasLimit = 200_000; + transaction.GasPrice = 0; + transaction.Data = inputData; + + return transaction; + } + + private static string Encrypt(byte[] randomizeKey, string text) + { + using var aes = Aes.Create(); + aes.Key = randomizeKey; + aes.Mode = CipherMode.CFB; + aes.Padding = PaddingMode.None; + aes.BlockSize = 128; + + var iv = new byte[aes.BlockSize / 8]; + RandomNumberGenerator.Fill(iv); + + var plaintext = Encoding.UTF8.GetBytes(text); + using var ms = new MemoryStream(); + // prepend IV + ms.Write(iv, 0, iv.Length); + using (var encryptor = aes.CreateEncryptor(aes.Key, iv)) + using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) + { + cs.Write(plaintext, 0, plaintext.Length); + cs.FlushFinalBlock(); + } + + var cipherBytes = ms.ToArray(); + return Convert.ToBase64String(cipherBytes); + } + + public static byte[] RandStringByte(int n) + { + const string letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"; + var result = new byte[n]; + for (int i = 0; i < n; i++) + { + int idx = RandomNumberGenerator.GetInt32(letterBytes.Length); + result[i] = (byte)letterBytes[idx]; + } + return result; + } +} diff --git a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs index c793d675bc2..7d099509d23 100644 --- a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs @@ -96,6 +96,11 @@ public interface IXdcReleaseSpec : IReleaseSpec public int MinimumSigningTx { get; set; } // Signing txs that a node needs to produce to get out of penalty, after `LimitPenaltyEpoch` public List V2Configs { get; set; } Address[] GenesisMasterNodes { get; set; } + Address BlockSignersAddress { get; set; } + Address RandomizeSMCBinary { get; set; } + long BlackListHFNumber { get; set; } + long EpochBlockOpening { get; set; } + long EpochBlockRandomize { get; set; } public void ApplyV2Config(ulong round); } diff --git a/src/Nethermind/Nethermind.Xdc/XdcSpecialTransactionExecutor.cs b/src/Nethermind/Nethermind.Xdc/XdcSpecialTransactionExecutor.cs new file mode 100644 index 00000000000..ccab47a34a8 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/XdcSpecialTransactionExecutor.cs @@ -0,0 +1,104 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Blockchain.Tracing; +using Nethermind.Consensus.Processing; +using Nethermind.Core; +using Nethermind.Core.Specs; +using Nethermind.Evm; +using Nethermind.Evm.State; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.Int256; +using Nethermind.Xdc.Spec; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Nethermind.Xdc; + +internal class XdcTransactionExecutor(ITransactionProcessorAdapter txProcessorAdapter, IWorldState stateProvider, ISpecProvider specProvider) + : BlockProcessor.BlockValidationTransactionsExecutor(txProcessorAdapter, stateProvider) +{ + public override TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processingOptions, BlockReceiptsTracer receiptsTracer, CancellationToken token) + { + Metrics.ResetBlockStats(); + + var spec = specProvider.GetXdcSpec(block.Header as XdcBlockHeader); + + for (int i = 0; i < block.Transactions.Length; i++) + { + Transaction currentTx = block.Transactions[i]; + if (!IsSpecial(spec, currentTx)) + continue; + ProcessSpecialTransaction(block, currentTx, i, receiptsTracer, processingOptions, spec); + } + + for (int i = 0; i < block.Transactions.Length; i++) + { + Transaction currentTx = block.Transactions[i]; + if (IsSpecial(spec, currentTx)) + continue; + + ProcessNormalTransaction(block, currentTx, i, receiptsTracer, processingOptions, spec); + } + + return receiptsTracer.TxReceipts.ToArray(); + } + + private void ProcessNormalTransaction(Block block, Transaction currentTx, int i, BlockReceiptsTracer receiptsTracer, ProcessingOptions processingOptions, IXdcReleaseSpec spec) + { + Address sender = currentTx.SenderAddress; + Address target = currentTx.To; + if (IsBlackListed(spec, sender) || IsBlackListed(spec, target)) + { + // Skip processing special transactions if either sender or recipient is blacklisted + return; + } + + ProcessTransaction(block, currentTx, i, receiptsTracer, processingOptions); + } + + private void ProcessSpecialTransaction(Block block, Transaction currentTx, int i, BlockReceiptsTracer receiptsTracer, ProcessingOptions processingOptions, IXdcReleaseSpec spec) + { + Address sender = currentTx.SenderAddress; + Address target = currentTx.To; + if (spec.BlackListHFNumber == block.Number) + { + if (IsBlackListed(spec, sender)) + { + // Skip processing special transactions if either sender or recipient is blacklisted + return; + } + } + + if(target == spec.BlockSignersAddress) + { + if(currentTx.Data.Length < 68) { + return; + } + + UInt256 blkNumber = new UInt256(currentTx.Data.Span[8..40], true); + if (blkNumber >= (UInt256)block.Number || blkNumber <= (UInt256)(block.Number - (spec.EpochLength * 2))) + { + // Invalid block number in special transaction data + return; + } + + } + + ProcessTransaction(block, currentTx, i, receiptsTracer, processingOptions); + } + + private bool IsBlackListed(IXdcReleaseSpec spec, Address sender) + { + throw new NotImplementedException(); + } + + private bool IsSpecial(IXdcReleaseSpec spec, Transaction currentTx) + { + return currentTx.To is not null && ((currentTx.To == spec.BlockSignersAddress) || (currentTx.To == spec.RandomizeSMCBinary)); + } +} From bdaa436008978baaa31bca862a76a65cfe27904b Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Fri, 21 Nov 2025 11:34:00 +0100 Subject: [PATCH 02/15] Added createSignTx --- src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs | 1 + src/tests | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs index 9bc78b3c7b7..2769ac1a6cf 100644 --- a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs @@ -102,6 +102,7 @@ public interface IXdcReleaseSpec : IReleaseSpec long BlackListHFNumber { get; set; } long EpochBlockOpening { get; set; } long EpochBlockRandomize { get; set; } + long MergeSignRange { get; set; } public void ApplyV2Config(ulong round); } diff --git a/src/tests b/src/tests index 08b5fe38c23..ae4791077e8 160000 --- a/src/tests +++ b/src/tests @@ -1 +1 @@ -Subproject commit 08b5fe38c23c3ea4f88af93e10fbf03b715414f7 +Subproject commit ae4791077e8fcf716136e70fe8392f1a1f1495fb From 69de096af6872825c7cc7597c06c011dacd1d8ef Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Tue, 25 Nov 2025 11:15:09 +0100 Subject: [PATCH 03/15] Add some spec changes --- .../Nethermind.Xdc/Spec/XdcReleaseSpec.cs | 6 ++++++ src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs index 2769ac1a6cf..88295cd50b4 100644 --- a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs @@ -31,6 +31,12 @@ public class XdcReleaseSpec : ReleaseSpec, IXdcReleaseSpec public List V2Configs { get; set; } = new List(); public Address[] GenesisMasterNodes { get; set; } + public Address BlockSignersAddress { get ; set ; } + public Address RandomizeSMCBinary { get ; set ; } + public long BlackListHFNumber { get ; set ; } + public long EpochBlockOpening { get ; set ; } + public long EpochBlockRandomize { get ; set ; } + public long MergeSignRange { get ; set ; } public void ApplyV2Config(ulong round) { diff --git a/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs b/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs index c17b6d1dc3a..d5e91fe03dd 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs @@ -7,7 +7,9 @@ using Nethermind.Consensus.Producers; using Nethermind.Core; using Nethermind.Core.Specs; +using Nethermind.Db; using Nethermind.Logging; +using Nethermind.TxPool; using Nethermind.Xdc.Spec; using Nethermind.Xdc.Types; using System; @@ -33,6 +35,8 @@ internal class XdcHotStuff : IBlockProducerRunner private readonly ISigner _signer; private readonly ITimeoutTimer _timeoutTimer; private readonly IProcessExitSource _processExit; + private readonly IDb _stateDb; + private readonly ITxPool _txPool; private readonly ILogger _logger; private CancellationTokenSource? _cancellationTokenSource; @@ -46,6 +50,7 @@ internal class XdcHotStuff : IBlockProducerRunner private ulong _highestSelfMinedRound; private ulong _highestVotedRound; + public XdcHotStuff( IBlockTree blockTree, IXdcConsensusContext xdcContext, @@ -58,6 +63,8 @@ public XdcHotStuff( ISigner signer, ITimeoutTimer timeoutTimer, IProcessExitSource processExit, + IDb stateDb, + ITxPool txPool, ILogManager logManager) { _blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree)); @@ -71,6 +78,8 @@ public XdcHotStuff( _signer = signer ?? throw new ArgumentNullException(nameof(signer)); _timeoutTimer = timeoutTimer; _processExit = processExit; + _stateDb = stateDb; + _txPool = txPool; _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); _lastActivityTime = DateTime.UtcNow; @@ -247,6 +256,11 @@ internal async Task RunRoundChecks(CancellationToken ct) { await CommitCertificateAndVote(roundParent, epochInfo); } + + if((spec.MergeSignRange % roundParent.Number == 0)|| spec.IsTIP2019(roundParent.Number)) + { + await ContractsUtils.CreateTransactionSign(roundParent, _signer, _stateDb, _txPool, spec); + } } private XdcBlockHeader GetParentForRound() From f659b09564314e06539c60e300e07218189a6363 Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Tue, 25 Nov 2025 11:28:02 +0100 Subject: [PATCH 04/15] fix the TIP check --- src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs | 2 ++ src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs index 88295cd50b4..f8048e314f5 100644 --- a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs @@ -37,6 +37,7 @@ public class XdcReleaseSpec : ReleaseSpec, IXdcReleaseSpec public long EpochBlockOpening { get ; set ; } public long EpochBlockRandomize { get ; set ; } public long MergeSignRange { get ; set ; } + public long TIP2019Block { get ; set ; } public void ApplyV2Config(ulong round) { @@ -109,6 +110,7 @@ public interface IXdcReleaseSpec : IReleaseSpec long EpochBlockOpening { get; set; } long EpochBlockRandomize { get; set; } long MergeSignRange { get; set; } + long TIP2019Block { get; set; } public void ApplyV2Config(ulong round); } diff --git a/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs b/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs index d5e91fe03dd..f56c9a48b7b 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs @@ -257,7 +257,7 @@ internal async Task RunRoundChecks(CancellationToken ct) await CommitCertificateAndVote(roundParent, epochInfo); } - if((spec.MergeSignRange % roundParent.Number == 0)|| spec.IsTIP2019(roundParent.Number)) + if((spec.MergeSignRange % roundParent.Number == 0)|| spec.TIP2019Block == roundParent.Number) { await ContractsUtils.CreateTransactionSign(roundParent, _signer, _stateDb, _txPool, spec); } From 6ee164e12cc3eb93d3531c175d86df40ca57bac8 Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Tue, 25 Nov 2025 15:52:27 +0100 Subject: [PATCH 05/15] refactor transaction validation --- .../TransactionProcessor.cs | 5 +- .../Nethermind.Xdc/Spec/XdcReleaseSpec.cs | 2 + .../Nethermind.Xdc/XdcExtensions.cs | 5 +- .../XdcSpecialTransactionExecutor.cs | 63 ++--------------- .../Nethermind.Xdc/XdcTransactionProcessor.cs | 68 +++++++++++++++++++ 5 files changed, 83 insertions(+), 60 deletions(-) create mode 100644 src/Nethermind/Nethermind.Xdc/XdcTransactionProcessor.cs diff --git a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs index 21580b779de..79e7cf15444 100644 --- a/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Evm/TransactionProcessing/TransactionProcessor.cs @@ -22,7 +22,7 @@ namespace Nethermind.Evm.TransactionProcessing { - public sealed class TransactionProcessor( + public class TransactionProcessor( ITransactionProcessor.IBlobBaseFeeCalculator blobBaseFeeCalculator, ISpecProvider? specProvider, IWorldState? worldState, @@ -951,6 +951,7 @@ private TransactionResult(ErrorType error = ErrorType.None, EvmExceptionType evm ErrorType.SenderNotSpecified => "sender not specified", ErrorType.TransactionSizeOverMaxInitCodeSize => "EIP-3860 - transaction size over max init code size", ErrorType.WrongTransactionNonce => "wrong transaction nonce", + ErrorType.ContainsBlacklistedAddress => "sender or receiver are blacklisted addresses", _ => "" }; public static implicit operator TransactionResult(ErrorType error) => new(error); @@ -981,6 +982,7 @@ public static TransactionResult EvmException(EvmExceptionType evmExceptionType, public static readonly TransactionResult SenderNotSpecified = ErrorType.SenderNotSpecified; public static readonly TransactionResult TransactionSizeOverMaxInitCodeSize = ErrorType.TransactionSizeOverMaxInitCodeSize; public static readonly TransactionResult WrongTransactionNonce = ErrorType.WrongTransactionNonce; + public static readonly TransactionResult ContainsBlacklistedAddress = ErrorType.ContainsBlacklistedAddress; public enum ErrorType { @@ -996,6 +998,7 @@ public enum ErrorType SenderNotSpecified, TransactionSizeOverMaxInitCodeSize, WrongTransactionNonce, + ContainsBlacklistedAddress } } } diff --git a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs index f8048e314f5..31d1223c2a7 100644 --- a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs @@ -112,5 +112,7 @@ public interface IXdcReleaseSpec : IReleaseSpec long MergeSignRange { get; set; } long TIP2019Block { get; set; } + Address[] BlackListedAddresses { get; set; } + public void ApplyV2Config(ulong round); } diff --git a/src/Nethermind/Nethermind.Xdc/XdcExtensions.cs b/src/Nethermind/Nethermind.Xdc/XdcExtensions.cs index 509ddcc3920..cfd594cedf6 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcExtensions.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcExtensions.cs @@ -23,7 +23,10 @@ public static Signature Sign(this IEthereumEcdsa ecdsa, PrivateKey privateKey, X ValueHash256 hash = ValueKeccak.Compute(_headerDecoder.Encode(header, RlpBehaviors.ForSealing).Bytes); return ecdsa.Sign(privateKey, in hash); } - + public static bool IsSpecialTransaction(this Transaction currentTx, IXdcReleaseSpec spec) + { + return currentTx.To is not null && ((currentTx.To == spec.BlockSignersAddress) || (currentTx.To == spec.RandomizeSMCBinary)); + } public static Address RecoverVoteSigner(this IEthereumEcdsa ecdsa, Vote vote) { KeccakRlpStream stream = new(); diff --git a/src/Nethermind/Nethermind.Xdc/XdcSpecialTransactionExecutor.cs b/src/Nethermind/Nethermind.Xdc/XdcSpecialTransactionExecutor.cs index ccab47a34a8..3545627d88e 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcSpecialTransactionExecutor.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcSpecialTransactionExecutor.cs @@ -31,74 +31,21 @@ public override TxReceipt[] ProcessTransactions(Block block, ProcessingOptions p for (int i = 0; i < block.Transactions.Length; i++) { Transaction currentTx = block.Transactions[i]; - if (!IsSpecial(spec, currentTx)) + if (!currentTx.IsSpecialTransaction(spec)) continue; - ProcessSpecialTransaction(block, currentTx, i, receiptsTracer, processingOptions, spec); + + ProcessTransaction(block, currentTx, i, receiptsTracer, processingOptions); } for (int i = 0; i < block.Transactions.Length; i++) { Transaction currentTx = block.Transactions[i]; - if (IsSpecial(spec, currentTx)) + if (currentTx.IsSpecialTransaction(spec)) continue; - ProcessNormalTransaction(block, currentTx, i, receiptsTracer, processingOptions, spec); + ProcessTransaction(block, currentTx, i, receiptsTracer, processingOptions); } return receiptsTracer.TxReceipts.ToArray(); } - - private void ProcessNormalTransaction(Block block, Transaction currentTx, int i, BlockReceiptsTracer receiptsTracer, ProcessingOptions processingOptions, IXdcReleaseSpec spec) - { - Address sender = currentTx.SenderAddress; - Address target = currentTx.To; - if (IsBlackListed(spec, sender) || IsBlackListed(spec, target)) - { - // Skip processing special transactions if either sender or recipient is blacklisted - return; - } - - ProcessTransaction(block, currentTx, i, receiptsTracer, processingOptions); - } - - private void ProcessSpecialTransaction(Block block, Transaction currentTx, int i, BlockReceiptsTracer receiptsTracer, ProcessingOptions processingOptions, IXdcReleaseSpec spec) - { - Address sender = currentTx.SenderAddress; - Address target = currentTx.To; - if (spec.BlackListHFNumber == block.Number) - { - if (IsBlackListed(spec, sender)) - { - // Skip processing special transactions if either sender or recipient is blacklisted - return; - } - } - - if(target == spec.BlockSignersAddress) - { - if(currentTx.Data.Length < 68) { - return; - } - - UInt256 blkNumber = new UInt256(currentTx.Data.Span[8..40], true); - if (blkNumber >= (UInt256)block.Number || blkNumber <= (UInt256)(block.Number - (spec.EpochLength * 2))) - { - // Invalid block number in special transaction data - return; - } - - } - - ProcessTransaction(block, currentTx, i, receiptsTracer, processingOptions); - } - - private bool IsBlackListed(IXdcReleaseSpec spec, Address sender) - { - throw new NotImplementedException(); - } - - private bool IsSpecial(IXdcReleaseSpec spec, Transaction currentTx) - { - return currentTx.To is not null && ((currentTx.To == spec.BlockSignersAddress) || (currentTx.To == spec.RandomizeSMCBinary)); - } } diff --git a/src/Nethermind/Nethermind.Xdc/XdcTransactionProcessor.cs b/src/Nethermind/Nethermind.Xdc/XdcTransactionProcessor.cs new file mode 100644 index 00000000000..e641e75a70b --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/XdcTransactionProcessor.cs @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Core; +using Nethermind.Core.Specs; +using Nethermind.Evm; +using Nethermind.Evm.State; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.Int256; +using Nethermind.Logging; +using Nethermind.Xdc.Spec; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nethermind.Xdc; + +internal class XdcTransactionProcessor( + ITransactionProcessor.IBlobBaseFeeCalculator blobBaseFeeCalculator, + ISpecProvider? specProvider, + IWorldState? worldState, + IVirtualMachine? virtualMachine, + ICodeInfoRepository? codeInfoRepository, + ILogManager? logManager) : TransactionProcessor(blobBaseFeeCalculator, specProvider, worldState, virtualMachine, codeInfoRepository, logManager) +{ + protected override TransactionResult ValidateStatic(Transaction tx, BlockHeader header, IReleaseSpec spec, ExecutionOptions opts, in IntrinsicGas intrinsicGas) + { + var xdcSpec = spec as XdcReleaseSpec; + Address target = tx.To; + Address sender = tx.SenderAddress; + + if (xdcSpec.BlackListHFNumber == header.Number) + { + if (IsBlackListed(xdcSpec, sender) || IsBlackListed(xdcSpec, target)) + { + // Skip processing special transactions if either sender or recipient is blacklisted + return TransactionResult.ContainsBlacklistedAddress; + } + } + + if (tx.IsSpecialTransaction(xdcSpec)) + { + if (target == xdcSpec.BlockSignersAddress) + { + if (tx.Data.Length < 68) + { + return TransactionResult.MalformedTransaction; + } + + UInt256 blkNumber = new UInt256(tx.Data.Span[8..40], true); + if (blkNumber >= (UInt256)header.Number || blkNumber <= (UInt256)(header.Number - (xdcSpec.EpochLength * 2))) + { + // Invalid block number in special transaction data + return TransactionResult.MalformedTransaction; + } + + } + } + + return base.ValidateStatic(tx, header, spec, opts, intrinsicGas); + } + + private bool IsBlackListed(IXdcReleaseSpec spec, Address sender) + { + return spec.BlackListedAddresses.Contains(sender); + } + +} From 6447bf33cb546fbd8e5d3c2a5c8f2e77677076fb Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Wed, 26 Nov 2025 10:44:39 +0100 Subject: [PATCH 06/15] fix incorrect check --- src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs | 1 + src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs index 31d1223c2a7..4fd7396c813 100644 --- a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs @@ -38,6 +38,7 @@ public class XdcReleaseSpec : ReleaseSpec, IXdcReleaseSpec public long EpochBlockRandomize { get ; set ; } public long MergeSignRange { get ; set ; } public long TIP2019Block { get ; set ; } + public Address[] BlackListedAddresses { get ; set ; } public void ApplyV2Config(ulong round) { diff --git a/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs b/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs index f56c9a48b7b..0e45d53e70e 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs @@ -257,7 +257,7 @@ internal async Task RunRoundChecks(CancellationToken ct) await CommitCertificateAndVote(roundParent, epochInfo); } - if((spec.MergeSignRange % roundParent.Number == 0)|| spec.TIP2019Block == roundParent.Number) + if((roundParent.Number % spec.MergeSignRange == 0)|| spec.TIP2019Block != roundParent.Number) { await ContractsUtils.CreateTransactionSign(roundParent, _signer, _stateDb, _txPool, spec); } From 3f3052e8140e19d72bb8770054b92d472122ea24 Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Thu, 27 Nov 2025 11:39:42 +0100 Subject: [PATCH 07/15] inject txProcessor and txExecutor --- src/Nethermind/Nethermind.Xdc/XdcModule.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Nethermind/Nethermind.Xdc/XdcModule.cs b/src/Nethermind/Nethermind.Xdc/XdcModule.cs index c533dcf82b4..726d7dbe16d 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcModule.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcModule.cs @@ -12,6 +12,7 @@ using Nethermind.Core; using Nethermind.Core.Specs; using Nethermind.Db; +using Nethermind.Evm.TransactionProcessing; using Nethermind.Init.Modules; using Nethermind.Specs.ChainSpecStyle; using Nethermind.Xdc.Spec; @@ -65,6 +66,10 @@ protected override void Load(ContainerBuilder builder) .AddSingleton() .AddSingleton() .AddSingleton() + + // block processing + .AddScoped() + .AddScoped() ; } From 2134d57a406b754255562a574a41cf6d16393f09 Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Mon, 1 Dec 2025 16:01:23 +0100 Subject: [PATCH 08/15] minor fixes and refactors --- .../Helpers/XdcTestBlockchain.cs | 73 +++++++++- .../SpecialTransactionsTests.cs | 132 ++++++++++++++++++ .../Nethermind.Xdc/ContractsUtils.cs | 18 ++- .../XdcBlockProductionEnvFactory.cs | 21 +++ src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs | 2 +- src/Nethermind/Nethermind.Xdc/XdcModule.cs | 3 +- ...nExecutor.cs => XdcTransactionExecutor.cs} | 1 + 7 files changed, 244 insertions(+), 6 deletions(-) create mode 100644 src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs create mode 100644 src/Nethermind/Nethermind.Xdc/XdcBlockProductionEnvFactory.cs rename src/Nethermind/Nethermind.Xdc/{XdcSpecialTransactionExecutor.cs => XdcTransactionExecutor.cs} (99%) diff --git a/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs b/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs index 33f5e224e9d..54ab34eabdd 100644 --- a/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs +++ b/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs @@ -11,11 +11,13 @@ using Nethermind.Consensus.Processing; using Nethermind.Consensus.Producers; using Nethermind.Core; +using Nethermind.Core.Crypto; using Nethermind.Core.Specs; using Nethermind.Core.Test.Blockchain; using Nethermind.Core.Test.Builders; using Nethermind.Crypto; using Nethermind.Db; +using Nethermind.Evm; using Nethermind.Evm.State; using Nethermind.Facade.Find; using Nethermind.Int256; @@ -219,6 +221,22 @@ private IXdcReleaseSpec WrapReleaseSpec(IReleaseSpec spec) xdcSpec.MinimumSigningTx = 1; xdcSpec.GasLimitBoundDivisor = 1024; + xdcSpec.BlackListedAddresses = + [ + new Address("0x00000000000000000000000000000000b1Ac701"), + new Address("0x00000000000000000000000000000000b1Ac702"), + new Address("0x00000000000000000000000000000000b1Ac703"), + new Address("0x00000000000000000000000000000000b1Ac704"), + new Address("0x00000000000000000000000000000000b1Ac705"), + new Address("0x00000000000000000000000000000000b1Ac706"), + new Address("0x00000000000000000000000000000000b1Ac707"), + ]; + xdcSpec.MergeSignRange = 15; + + xdcSpec.RandomizeSMCBinary = new Address("0x7c2E0c7B4F06C3cF4dA27AE5A6b89C4A0F1C9c41"); + xdcSpec.BlockSignersAddress = new Address("0xA45eEFb7d2a6800F9f39E35C0F0F8D5E0d7C3B22"); + xdcSpec.TIP2019Block = 10; + V2ConfigParams[] v2ConfigParams = [ new V2ConfigParams { SwitchRound = 0, @@ -292,7 +310,22 @@ protected override IBlockProducerRunner CreateBlockProducerRunner() { if (_useHotStuffModule) { - return Container.Resolve(); + return new XdcHotStuff( + BlockTree, + XdcContext, + SpecProvider, + Container.Resolve(), + EpochSwitchManager, + SnapshotManager, + QuorumCertificateManager, + VotesManager, + Signer, + Container.Resolve(), + Container.Resolve(), + new MemDb(), + Container.Resolve(), + LogManager + ); } return base.CreateBlockProducerRunner(); } @@ -310,9 +343,47 @@ public Block Build() state.CreateAccount(TestItem.AddressA, testConfiguration.AccountInitialValue); state.CreateAccount(TestItem.AddressB, testConfiguration.AccountInitialValue); state.CreateAccount(TestItem.AddressC, testConfiguration.AccountInitialValue); + state.CreateAccount(TestItem.AddressD, testConfiguration.AccountInitialValue); + state.CreateAccount(TestItem.AddressF, testConfiguration.AccountInitialValue); + + + var genesisSpec = specProvider.GenesisSpec as IXdcReleaseSpec; + + state.CreateAccount(genesisSpec!.BlockSignersAddress, 100_000); + state.CreateAccount(genesisSpec!.RandomizeSMCBinary, 100_000); + + var gasEaterCode = Prepare.EvmCode + .JUMPDEST() + .ADD(1, 1) + .POP() + .JUMP(0) + .Done; + var gasEaterHashcode = Keccak.Compute(gasEaterCode); + var gasEaterAddress = Address.FromNumber(new UInt256(gasEaterHashcode.Bytes.Slice(0, 32))); + state.CreateAccount(gasEaterAddress, 100_000); + + var dummyCode = Prepare.EvmCode + .PushData(0) + .PushData(0) + .PushData(0) + .PushData(0) + .PushData(1) + .PushData(gasEaterAddress) + .GAS() + .CALL() + .Done; + var dummyCodeHashcode = Keccak.Compute(dummyCode); + + state.InsertCode(genesisSpec.BlockSignersAddress, dummyCodeHashcode, dummyCode, genesisSpec, true); + state.InsertCode(genesisSpec.RandomizeSMCBinary, dummyCodeHashcode, dummyCode, genesisSpec, true); IXdcReleaseSpec? finalSpec = (IXdcReleaseSpec)specProvider.GetFinalSpec(); + foreach (var nodeAddress in finalSpec.GenesisMasterNodes) + { + state.CreateAccount(nodeAddress, testConfiguration.AccountInitialValue); + } + XdcBlockHeaderBuilder xdcBlockHeaderBuilder = new(); var genesisBlock = new Block(xdcBlockHeaderBuilder diff --git a/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs b/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs new file mode 100644 index 00000000000..2f018fbb0c1 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs @@ -0,0 +1,132 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Autofac; +using FluentAssertions; +using FluentAssertions.Extensions; +using Nethermind.Consensus; +using Nethermind.Consensus.Processing; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using Nethermind.Core.Test.Builders; +using Nethermind.Db; +using Nethermind.Evm; +using Nethermind.TxPool; +using Nethermind.Xdc.Spec; +using Nethermind.Xdc.Test.Helpers; +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Nethermind.Xdc.Test; + +internal class SpecialTransactionsTests +{ + private bool IsTimeForOnchainSignature(IXdcReleaseSpec spec, long blockNumber) + { + long checkNumber = blockNumber % spec.EpochLength; + return checkNumber == spec.MergeSignRange; + } + + [Test] + public async Task Special_Tx_Is_Dispatched_On_MergeSignRange_Block() + { + var blockChain = await XdcTestBlockchain.Create(1, true); + await Task.Delay(2.Seconds()); // to avoid tight loop + + blockChain.ChangeReleaseSpec((spec) => + { + spec.MergeSignRange = 5; + spec.IsEip1559Enabled = false; + }); + + blockChain.StartHotStuffModule(); + + XdcBlockHeader? head = blockChain.BlockTree.Head!.Header as XdcBlockHeader; + do + { + await blockChain.TriggerAndSimulateBlockProposalAndVoting(); + await Task.Delay(blockChain.SpecProvider.GetXdcSpec(head!).MinePeriod.Seconds()); // to avoid tight loop + head = (XdcBlockHeader)blockChain.BlockTree.Head!.Header; + } + while (!IsTimeForOnchainSignature(blockChain.SpecProvider.GetXdcSpec(head), head.Number)); + + // await blockChain.StopHotStuffModule(); + + await Task.Delay(((XdcReleaseSpec)blockChain.SpecProvider.GetFinalSpec()).MinePeriod.Seconds()); // to avoid tight loop + + var receipts = blockChain.TxPool.GetPendingTransactions(); + + receipts.Any(r => r.To == blockChain.SpecProvider.GetXdcSpec(head).BlockSignersAddress + || r.To == blockChain.SpecProvider.GetXdcSpec(head).RandomizeSMCBinary ).Should().BeTrue(); + } + + [Test] + public async Task Special_Tx_Is_Not_Dispatched_Outside_MergeSignRange_Block() + { + var blockChain = await XdcTestBlockchain.Create(1, true); + await Task.Delay(2.Seconds()); // to avoid tight loop + + blockChain.ChangeReleaseSpec((spec) => + { + spec.MergeSignRange = 5; + spec.IsEip1559Enabled = false; + }); + + blockChain.StartHotStuffModule(); + + XdcBlockHeader? head = blockChain.BlockTree.Head!.Header as XdcBlockHeader; + do + { + await blockChain.TriggerAndSimulateBlockProposalAndVoting(); + await Task.Delay(blockChain.SpecProvider.GetXdcSpec(head!).MinePeriod.Seconds()); // to avoid tight loop + head = (XdcBlockHeader)blockChain.BlockTree.Head!.Header; + } + while (!IsTimeForOnchainSignature(blockChain.SpecProvider.GetXdcSpec(head), head.Number + 1)); + + // await blockChain.StopHotStuffModule(); + + await Task.Delay(((XdcReleaseSpec)blockChain.SpecProvider.GetFinalSpec()).MinePeriod.Seconds()); // to avoid tight loop + + var receipts = blockChain.TxPool.GetPendingTransactions(); + + receipts.Any(r => r.To == blockChain.SpecProvider.GetXdcSpec(head).BlockSignersAddress + || r.To == blockChain.SpecProvider.GetXdcSpec(head).RandomizeSMCBinary).Should().BeFalse(); + } + + [Test] + public async Task Special_Tx_Is_Executed_Before_Normal_Txs() + { + var blockChain = await XdcTestBlockchain.Create(1, true); + await Task.Delay(2.Seconds()); // to avoid tight loop + + blockChain.ChangeReleaseSpec((spec) => + { + spec.MergeSignRange = 5; + spec.IsEip1559Enabled = false; + }); + + blockChain.StartHotStuffModule(); + + XdcBlockHeader? head = blockChain.BlockTree.Head!.Header as XdcBlockHeader; + do + { + await blockChain.TriggerAndSimulateBlockProposalAndVoting(); + await Task.Delay(blockChain.SpecProvider.GetXdcSpec(head!).MinePeriod.Seconds()); // to avoid tight loop + head = (XdcBlockHeader)blockChain.BlockTree.Head!.Header; + } + while (!IsTimeForOnchainSignature(blockChain.SpecProvider.GetXdcSpec(head), head.Number - 1)); + + var block = blockChain.BlockTree.Head!; + + var receipts = blockChain.ReceiptStorage.Get(block.ParentHash!); + + Assert.That(block, Is.Not.Null); + } + +} diff --git a/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs b/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs index cfa333fc116..a9d8858633d 100644 --- a/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs +++ b/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs @@ -40,7 +40,7 @@ public static async Task CreateTransactionSign(XdcBlockHeader header, ISigner si await signer.Sign(transaction); // add local somehow to tx pool - bool added = txPool.SubmitTx(transaction, TxHandlingOptions.None); + bool added = txPool.SubmitTx(transaction, TxHandlingOptions.PersistentBroadcast); if(!added) { throw new Exception("Failed to add signed transaction to the pool."); @@ -65,7 +65,7 @@ public static async Task CreateTransactionSign(XdcBlockHeader header, ISigner si await signer.Sign(tx); // add local somehow to tx pool - bool addedOpening = txPool.SubmitTx(tx, TxHandlingOptions.None); + bool addedOpening = txPool.SubmitTx(tx, TxHandlingOptions.PersistentBroadcast); stateDb.Remove(randomKey); } @@ -78,7 +78,7 @@ public static async Task CreateTransactionSign(XdcBlockHeader header, ISigner si Transaction tx = BuildTxSecretRandomize(nonce + 1, spec.RandomizeSMCBinary, (ulong)spec.EpochLength, randomizeKeyValue); await signer.Sign(tx); // add local somehow to tx pool - bool addedOpening = txPool.SubmitTx(tx, TxHandlingOptions.None); + bool addedOpening = txPool.SubmitTx(tx, TxHandlingOptions.PersistentBroadcast); stateDb.PutSpan(randomKey, randomizeKeyValue); } @@ -98,6 +98,10 @@ private static Transaction CreateTxSign(UInt256 number, Hash256 hash, UInt256 no transaction.GasPrice = 0; transaction.Data = inputData; + transaction.Type = TxType.Legacy; + + transaction.Hash = transaction.CalculateHash(); + return transaction; } @@ -114,6 +118,10 @@ private static Transaction CreateTxOpeningRandomize(UInt256 nonce, Address rando transaction.GasPrice = 0; transaction.Data = inputData; + transaction.Type = TxType.Legacy; + + transaction.Hash = transaction.CalculateHash(); + return transaction; } @@ -148,6 +156,10 @@ public static Transaction BuildTxSecretRandomize(UInt256 nonce, Address randomiz transaction.GasPrice = 0; transaction.Data = inputData; + transaction.Type = TxType.Legacy; + + transaction.Hash = transaction.CalculateHash(); + return transaction; } diff --git a/src/Nethermind/Nethermind.Xdc/XdcBlockProductionEnvFactory.cs b/src/Nethermind/Nethermind.Xdc/XdcBlockProductionEnvFactory.cs new file mode 100644 index 00000000000..327349bd421 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/XdcBlockProductionEnvFactory.cs @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Autofac; +using Nethermind.Consensus.Processing; +using Nethermind.Consensus.Producers; +using Nethermind.Core; +using Nethermind.State; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nethermind.Xdc; + +public class XdcBlockProductionEnvFactory(ILifetimeScope rootLifetime, IWorldStateManager worldStateManager, IBlockProducerTxSourceFactory txSourceFactory) : BlockProducerEnvFactory(rootLifetime, worldStateManager, txSourceFactory) +{ + protected override ContainerBuilder ConfigureBuilder(ContainerBuilder builder) => + // Taiko does not seems to use `BlockProductionTransactionsExecutor` + base.ConfigureBuilder(builder) + .AddScoped(); +} diff --git a/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs b/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs index 0e45d53e70e..b941cd8920f 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs @@ -257,7 +257,7 @@ internal async Task RunRoundChecks(CancellationToken ct) await CommitCertificateAndVote(roundParent, epochInfo); } - if((roundParent.Number % spec.MergeSignRange == 0)|| spec.TIP2019Block != roundParent.Number) + if((roundParent.Number % spec.MergeSignRange == 0) || !(spec.TIP2019Block <= roundParent.Number)) { await ContractsUtils.CreateTransactionSign(roundParent, _signer, _stateDb, _txPool, spec); } diff --git a/src/Nethermind/Nethermind.Xdc/XdcModule.cs b/src/Nethermind/Nethermind.Xdc/XdcModule.cs index 726d7dbe16d..7a4e409a45a 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcModule.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcModule.cs @@ -8,6 +8,7 @@ using Nethermind.Blockchain.Headers; using Nethermind.Consensus; using Nethermind.Consensus.Processing; +using Nethermind.Consensus.Producers; using Nethermind.Consensus.Validators; using Nethermind.Core; using Nethermind.Core.Specs; @@ -69,7 +70,7 @@ protected override void Load(ContainerBuilder builder) // block processing .AddScoped() - .AddScoped() + .AddSingleton() ; } diff --git a/src/Nethermind/Nethermind.Xdc/XdcSpecialTransactionExecutor.cs b/src/Nethermind/Nethermind.Xdc/XdcTransactionExecutor.cs similarity index 99% rename from src/Nethermind/Nethermind.Xdc/XdcSpecialTransactionExecutor.cs rename to src/Nethermind/Nethermind.Xdc/XdcTransactionExecutor.cs index 3545627d88e..4e95b4afd56 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcSpecialTransactionExecutor.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcTransactionExecutor.cs @@ -28,6 +28,7 @@ public override TxReceipt[] ProcessTransactions(Block block, ProcessingOptions p var spec = specProvider.GetXdcSpec(block.Header as XdcBlockHeader); + for (int i = 0; i < block.Transactions.Length; i++) { Transaction currentTx = block.Transactions[i]; From 908c3eda8b5427d738d11aa2df57a8a2156a61e0 Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Mon, 8 Dec 2025 11:16:09 +0100 Subject: [PATCH 09/15] more tests and more fixes --- ...sor.BlockProductionTransactionsExecutor.cs | 2 +- .../Producers/BlockToProduce.cs | 1 + .../Helpers/XdcTestBlockchain.cs | 2 +- .../SpecialTransactionsTests.cs | 301 +++++++++++++++++- .../Nethermind.Xdc/ContractsUtils.cs | 21 +- .../XdcBlockBuildingTransactionExecutor.cs | 109 +++++++ .../XdcBlockProductionEnvFactory.cs | 6 +- .../XdcBlockValidationModule.cs | 20 ++ ... XdcBlockValidationTransactionExecutor.cs} | 4 +- src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs | 9 +- src/Nethermind/Nethermind.Xdc/XdcModule.cs | 4 +- .../Nethermind.Xdc/XdcTransactionProcessor.cs | 6 +- 12 files changed, 451 insertions(+), 34 deletions(-) create mode 100644 src/Nethermind/Nethermind.Xdc/XdcBlockBuildingTransactionExecutor.cs create mode 100644 src/Nethermind/Nethermind.Xdc/XdcBlockValidationModule.cs rename src/Nethermind/Nethermind.Xdc/{XdcTransactionExecutor.cs => XdcBlockValidationTransactionExecutor.cs} (88%) diff --git a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs index 1418fe28401..a2f4830cb7a 100644 --- a/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs +++ b/src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.BlockProductionTransactionsExecutor.cs @@ -87,7 +87,7 @@ public virtual TxReceipt[] ProcessTransactions(Block block, ProcessingOptions pr return receiptsTracer.TxReceipts.ToArray(); } - private TxAction ProcessTransaction( + protected TxAction ProcessTransaction( Block block, Transaction currentTx, int index, diff --git a/src/Nethermind/Nethermind.Consensus/Producers/BlockToProduce.cs b/src/Nethermind/Nethermind.Consensus/Producers/BlockToProduce.cs index ce6d7be6f11..016bb940a69 100644 --- a/src/Nethermind/Nethermind.Consensus/Producers/BlockToProduce.cs +++ b/src/Nethermind/Nethermind.Consensus/Producers/BlockToProduce.cs @@ -9,6 +9,7 @@ //TODO: Redo clique block producer [assembly: InternalsVisibleTo("Nethermind.Consensus.Clique")] +[assembly: InternalsVisibleTo("Nethermind.Xdc")] [assembly: InternalsVisibleTo("Nethermind.Blockchain.Test")] namespace Nethermind.Consensus.Producers diff --git a/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs b/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs index 54ab34eabdd..0c6d63e9a2f 100644 --- a/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs +++ b/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs @@ -235,7 +235,7 @@ private IXdcReleaseSpec WrapReleaseSpec(IReleaseSpec spec) xdcSpec.RandomizeSMCBinary = new Address("0x7c2E0c7B4F06C3cF4dA27AE5A6b89C4A0F1C9c41"); xdcSpec.BlockSignersAddress = new Address("0xA45eEFb7d2a6800F9f39E35C0F0F8D5E0d7C3B22"); - xdcSpec.TIP2019Block = 10; + xdcSpec.TIP2019Block = 0; V2ConfigParams[] v2ConfigParams = [ new V2ConfigParams { diff --git a/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs b/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs index 2f018fbb0c1..9c2b1623a93 100644 --- a/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs +++ b/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs @@ -4,6 +4,8 @@ using Autofac; using FluentAssertions; using FluentAssertions.Extensions; +using Nethermind.Blockchain; +using Nethermind.Blockchain.Headers; using Nethermind.Consensus; using Nethermind.Consensus.Processing; using Nethermind.Core; @@ -13,9 +15,15 @@ using Nethermind.Core.Test.Builders; using Nethermind.Db; using Nethermind.Evm; +using Nethermind.Evm.Tracing; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.Int256; +using Nethermind.Logging; using Nethermind.TxPool; using Nethermind.Xdc.Spec; using Nethermind.Xdc.Test.Helpers; +using NSubstitute; +using NSubstitute.ExceptionExtensions; using NUnit.Framework; using System; using System.Collections.Generic; @@ -29,19 +37,19 @@ internal class SpecialTransactionsTests { private bool IsTimeForOnchainSignature(IXdcReleaseSpec spec, long blockNumber) { - long checkNumber = blockNumber % spec.EpochLength; - return checkNumber == spec.MergeSignRange; + return blockNumber % spec.MergeSignRange == 0; } [Test] public async Task Special_Tx_Is_Dispatched_On_MergeSignRange_Block() { var blockChain = await XdcTestBlockchain.Create(1, true); - await Task.Delay(2.Seconds()); // to avoid tight loop + + var mergeSignBlockRange = 5; blockChain.ChangeReleaseSpec((spec) => { - spec.MergeSignRange = 5; + spec.MergeSignRange = mergeSignBlockRange; spec.IsEip1559Enabled = false; }); @@ -54,23 +62,30 @@ public async Task Special_Tx_Is_Dispatched_On_MergeSignRange_Block() await Task.Delay(blockChain.SpecProvider.GetXdcSpec(head!).MinePeriod.Seconds()); // to avoid tight loop head = (XdcBlockHeader)blockChain.BlockTree.Head!.Header; } - while (!IsTimeForOnchainSignature(blockChain.SpecProvider.GetXdcSpec(head), head.Number)); + while (!IsTimeForOnchainSignature(blockChain.SpecProvider.GetXdcSpec(head), head.Number - 1)); - // await blockChain.StopHotStuffModule(); + Assert.That(blockChain.BlockTree.Head.Number, Is.EqualTo(mergeSignBlockRange + 1)); await Task.Delay(((XdcReleaseSpec)blockChain.SpecProvider.GetFinalSpec()).MinePeriod.Seconds()); // to avoid tight loop - var receipts = blockChain.TxPool.GetPendingTransactions(); + Transaction[] pendingTxs = blockChain.TxPool.GetPendingTransactions(); - receipts.Any(r => r.To == blockChain.SpecProvider.GetXdcSpec(head).BlockSignersAddress - || r.To == blockChain.SpecProvider.GetXdcSpec(head).RandomizeSMCBinary ).Should().BeTrue(); + var specialTxs = pendingTxs.Where(r => r.To == blockChain.SpecProvider.GetXdcSpec(head).BlockSignersAddress + || r.To == blockChain.SpecProvider.GetXdcSpec(head).RandomizeSMCBinary); + + Assert.That(specialTxs, Is.Not.Empty); + + var specialTx = specialTxs.First(); + + var blockTarget = (long)(new UInt256(specialTx.Data.Span.Slice(4, 32), true)); + + Assert.That(blockTarget, Is.EqualTo(mergeSignBlockRange)); } [Test] public async Task Special_Tx_Is_Not_Dispatched_Outside_MergeSignRange_Block() { var blockChain = await XdcTestBlockchain.Create(1, true); - await Task.Delay(2.Seconds()); // to avoid tight loop blockChain.ChangeReleaseSpec((spec) => { @@ -103,7 +118,6 @@ public async Task Special_Tx_Is_Not_Dispatched_Outside_MergeSignRange_Block() public async Task Special_Tx_Is_Executed_Before_Normal_Txs() { var blockChain = await XdcTestBlockchain.Create(1, true); - await Task.Delay(2.Seconds()); // to avoid tight loop blockChain.ChangeReleaseSpec((spec) => { @@ -123,10 +137,271 @@ public async Task Special_Tx_Is_Executed_Before_Normal_Txs() while (!IsTimeForOnchainSignature(blockChain.SpecProvider.GetXdcSpec(head), head.Number - 1)); var block = blockChain.BlockTree.Head!; + var spec = blockChain.SpecProvider.GetXdcSpec(head); + + var receipts = blockChain.ReceiptStorage.Get(block.Hash!); + + Assert.That(receipts, Is.Not.Empty); + + bool onlyEncounteredSpecialTx = true; + foreach (var transaction in receipts) + { + if(transaction.Recipient == spec.BlockSignersAddress || transaction.Recipient == spec.RandomizeSMCBinary) + { + if(!onlyEncounteredSpecialTx) + { + // we encountered a normal transaction before so special txs are not lumped at the start + Assert.Fail(); + } + } else + { + onlyEncounteredSpecialTx = false; + } + } + + Assert.Pass(); + } + + [TestCase(false)] + [TestCase(true)] + public async Task Tx_With_With_BlackListed_Sender_Fails_Validation(bool blackListingActivated) + { + var blockChain = await XdcTestBlockchain.Create(5, false); + blockChain.ChangeReleaseSpec((spec) => + { + spec.BlackListedAddresses = [blockChain.Signer.Address]; + spec.IsEip1559Enabled = false; + spec.BlackListHFNumber = blackListingActivated ? 0 : long.MaxValue; + }); + + var moqVm = new VirtualMachine(new BlockhashProvider(new BlockhashCache(blockChain.Container.Resolve(), NullLogManager.Instance), blockChain.WorldStateManager.GlobalWorldState, NullLogManager.Instance), blockChain.SpecProvider, NullLogManager.Instance); + + var transactionProcessor = new XdcTransactionProcessor(BlobBaseFeeCalculator.Instance, blockChain.SpecProvider, blockChain.WorldStateManager.GlobalWorldState, moqVm, NSubstitute.Substitute.For(), NullLogManager.Instance); + + + XdcBlockHeader head = (XdcBlockHeader)blockChain.BlockTree.Head!.Header!; + XdcReleaseSpec spec = (XdcReleaseSpec)blockChain.SpecProvider.GetXdcSpec(head); + + + moqVm.SetBlockExecutionContext(new BlockExecutionContext(head, spec)); + + var txSign = ContractsUtils.CreateTxSign((UInt256)head.Number, head.Hash!, blockChain.TxPool.GetLatestPendingNonce(TestItem.AddressA), spec.BlockSignersAddress, blockChain.Signer.Address); + await blockChain.Signer.Sign(txSign); + + TransactionResult? result = null; + + try + { + result = transactionProcessor.Execute(txSign, NullTxTracer.Instance); + } + catch + { + result = TransactionResult.Ok; + } + + if (blackListingActivated) + { + result.Value.Error.Should().Be(TransactionResult.ErrorType.ContainsBlacklistedAddress); + } + else + { + result.Value.Error.Should().NotBe(TransactionResult.ErrorType.ContainsBlacklistedAddress); + } + } + + + [TestCase(false)] + [TestCase(true)] + public async Task Tx_With_With_BlackListed_Receiver_Fails_Validation(bool blackListingActivated) + { + + var blockChain = await XdcTestBlockchain.Create(5, false); + blockChain.ChangeReleaseSpec((spec) => + { + spec.BlackListedAddresses = [TestItem.AddressA]; + spec.IsEip1559Enabled = false; + spec.BlackListHFNumber = blackListingActivated ? 0 : long.MaxValue; + }); + var moqVm = new VirtualMachine(new BlockhashProvider(new BlockhashCache(blockChain.Container.Resolve(), NullLogManager.Instance), blockChain.WorldStateManager.GlobalWorldState, NullLogManager.Instance), blockChain.SpecProvider, NullLogManager.Instance); + + var transactionProcessor = new XdcTransactionProcessor(BlobBaseFeeCalculator.Instance, blockChain.SpecProvider, blockChain.WorldStateManager.GlobalWorldState, moqVm, NSubstitute.Substitute.For(), NullLogManager.Instance); + + + XdcBlockHeader head = (XdcBlockHeader)blockChain.BlockTree.Head!.Header!; + XdcReleaseSpec spec = (XdcReleaseSpec)blockChain.SpecProvider.GetXdcSpec(head); + + + moqVm.SetBlockExecutionContext(new BlockExecutionContext(head, spec)); + + Transaction tx = Build.A.Transaction.WithSenderAddress(blockChain.Signer.Address).WithTo(TestItem.AddressA).TestObject; + await blockChain.Signer.Sign(tx); + + TransactionResult? result = null; + + try + { + result = transactionProcessor.Execute(tx, NullTxTracer.Instance); + } + catch + { + result = TransactionResult.Ok; + } + + if (blackListingActivated) + { + result.Value.Error.Should().Be(TransactionResult.ErrorType.ContainsBlacklistedAddress); + } + else + { + result.Value.Error.Should().NotBe(TransactionResult.ErrorType.ContainsBlacklistedAddress); + } + } + + [TestCase(true)] + [TestCase(false)] + public async Task Malformed_WrongLenght_SpecialTx_Fails_Validation(bool isSpecialTx) + { + var blockChain = await XdcTestBlockchain.Create(5, false); + blockChain.ChangeReleaseSpec((spec) => + { + spec.IsEip1559Enabled = false; + }); + + var moqVm = new VirtualMachine(new BlockhashProvider(new BlockhashCache(blockChain.Container.Resolve(), NullLogManager.Instance), blockChain.WorldStateManager.GlobalWorldState, NullLogManager.Instance), blockChain.SpecProvider, NullLogManager.Instance); - var receipts = blockChain.ReceiptStorage.Get(block.ParentHash!); + var transactionProcessor = new XdcTransactionProcessor(BlobBaseFeeCalculator.Instance, blockChain.SpecProvider, blockChain.WorldStateManager.GlobalWorldState, moqVm, NSubstitute.Substitute.For(), NullLogManager.Instance); - Assert.That(block, Is.Not.Null); + + XdcBlockHeader head = (XdcBlockHeader)blockChain.BlockTree.Head!.Header!; + XdcReleaseSpec spec = (XdcReleaseSpec)blockChain.SpecProvider.GetXdcSpec(head); + + + moqVm.SetBlockExecutionContext(new BlockExecutionContext(head, spec)); + + Transaction? tx = null; + + if (isSpecialTx) + { + tx = ContractsUtils.CreateTxSign((UInt256)head.Number, head.Hash!, blockChain.TxPool.GetLatestPendingNonce(TestItem.AddressA), spec.BlockSignersAddress, blockChain.Signer.Address); + } + else + { + tx = Build.A.Transaction.WithSenderAddress(blockChain.Signer.Address).WithTo(TestItem.AddressA).TestObject; + } + + // damage the data field in the tx + tx.Data = Enumerable.Range(0, 48).Select(i => (byte)i).ToArray(); + + await blockChain.Signer.Sign(tx); + + TransactionResult? result = null; + + try + { + result = transactionProcessor.Execute(tx, NullTxTracer.Instance); + } + catch + { + result = TransactionResult.Ok; + } + + if (isSpecialTx) + { + result.Value.Error.Should().Be(TransactionResult.ErrorType.MalformedTransaction); + } + else + { + result.Value.Error.Should().NotBe(TransactionResult.ErrorType.MalformedTransaction); + } } + public async Task Malformed_WrongBlockNumber_BlockLessThanCurrent_SpecialTx_Fails_Validation() + { + var blockChain = await XdcTestBlockchain.Create(5, false); + blockChain.ChangeReleaseSpec((spec) => + { + spec.IsEip1559Enabled = false; + }); + + var moqVm = new VirtualMachine(new BlockhashProvider(new BlockhashCache(blockChain.Container.Resolve(), NullLogManager.Instance), blockChain.WorldStateManager.GlobalWorldState, NullLogManager.Instance), blockChain.SpecProvider, NullLogManager.Instance); + + var transactionProcessor = new XdcTransactionProcessor(BlobBaseFeeCalculator.Instance, blockChain.SpecProvider, blockChain.WorldStateManager.GlobalWorldState, moqVm, NSubstitute.Substitute.For(), NullLogManager.Instance); + + + XdcBlockHeader head = (XdcBlockHeader)blockChain.BlockTree.Head!.Header!; + XdcReleaseSpec spec = (XdcReleaseSpec)blockChain.SpecProvider.GetXdcSpec(head); + + + moqVm.SetBlockExecutionContext(new BlockExecutionContext(head, spec)); + + + var blockNumber = head.Number - 1; + Transaction? tx = ContractsUtils.CreateTxSign((UInt256)blockNumber, head.Hash!, blockChain.TxPool.GetLatestPendingNonce(TestItem.AddressA), spec.BlockSignersAddress, blockChain.Signer.Address); + + await blockChain.Signer.Sign(tx); + + TransactionResult? result = transactionProcessor.Execute(tx, NullTxTracer.Instance); + + result.Value.Error.Should().NotBe(TransactionResult.ErrorType.MalformedTransaction); + } + + public async Task Malformed_WrongBlockNumber_BlockEqualToCurrent_SpecialTx_Fails_Validation() + { + var blockChain = await XdcTestBlockchain.Create(5, false); + blockChain.ChangeReleaseSpec((spec) => + { + spec.IsEip1559Enabled = false; + }); + + var moqVm = new VirtualMachine(new BlockhashProvider(new BlockhashCache(blockChain.Container.Resolve(), NullLogManager.Instance), blockChain.WorldStateManager.GlobalWorldState, NullLogManager.Instance), blockChain.SpecProvider, NullLogManager.Instance); + + var transactionProcessor = new XdcTransactionProcessor(BlobBaseFeeCalculator.Instance, blockChain.SpecProvider, blockChain.WorldStateManager.GlobalWorldState, moqVm, NSubstitute.Substitute.For(), NullLogManager.Instance); + + + XdcBlockHeader head = (XdcBlockHeader)blockChain.BlockTree.Head!.Header!; + XdcReleaseSpec spec = (XdcReleaseSpec)blockChain.SpecProvider.GetXdcSpec(head); + + + moqVm.SetBlockExecutionContext(new BlockExecutionContext(head, spec)); + + + var blockNumber = head.Number; + Transaction? tx = ContractsUtils.CreateTxSign((UInt256)blockNumber, head.Hash!, blockChain.TxPool.GetLatestPendingNonce(TestItem.AddressA), spec.BlockSignersAddress, blockChain.Signer.Address); + + await blockChain.Signer.Sign(tx); + + TransactionResult? result = transactionProcessor.Execute(tx, NullTxTracer.Instance); + + result.Value.Error.Should().Be(TransactionResult.ErrorType.MalformedTransaction); + } + + public async Task Malformed_WrongBlockNumber_BlockBiggerThanCurrent_SpecialTx_Fails_Validation() + { + var blockChain = await XdcTestBlockchain.Create(5, false); + blockChain.ChangeReleaseSpec((spec) => + { + spec.IsEip1559Enabled = false; + }); + + var moqVm = new VirtualMachine(new BlockhashProvider(new BlockhashCache(blockChain.Container.Resolve(), NullLogManager.Instance), blockChain.WorldStateManager.GlobalWorldState, NullLogManager.Instance), blockChain.SpecProvider, NullLogManager.Instance); + + var transactionProcessor = new XdcTransactionProcessor(BlobBaseFeeCalculator.Instance, blockChain.SpecProvider, blockChain.WorldStateManager.GlobalWorldState, moqVm, NSubstitute.Substitute.For(), NullLogManager.Instance); + + + XdcBlockHeader head = (XdcBlockHeader)blockChain.BlockTree.Head!.Header!; + XdcReleaseSpec spec = (XdcReleaseSpec)blockChain.SpecProvider.GetXdcSpec(head); + + + moqVm.SetBlockExecutionContext(new BlockExecutionContext(head, spec)); + + + var blockNumber = head.Number + 1; + Transaction? tx = ContractsUtils.CreateTxSign((UInt256)blockNumber, head.Hash!, blockChain.TxPool.GetLatestPendingNonce(TestItem.AddressA), spec.BlockSignersAddress, blockChain.Signer.Address); + + await blockChain.Signer.Sign(tx); + + TransactionResult? result = transactionProcessor.Execute(tx, NullTxTracer.Instance); + + result.Value.Error.Should().Be(TransactionResult.ErrorType.MalformedTransaction); + } } diff --git a/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs b/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs index a9d8858633d..6c38477de9a 100644 --- a/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs +++ b/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs @@ -17,6 +17,7 @@ using Nethermind.Xdc.Spec; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Security.Cryptography; @@ -35,7 +36,7 @@ internal static class ContractsUtils public static async Task CreateTransactionSign(XdcBlockHeader header, ISigner signer, IDb stateDb, ITxPool txPool, IXdcReleaseSpec spec) { UInt256 nonce = txPool.GetLatestPendingNonce(signer.Address); - Transaction transaction = CreateTxSign((UInt256)header.Number, header.Hash, nonce, spec.BlockSignersAddress); + Transaction transaction = CreateTxSign((UInt256)header.Number, header.Hash, nonce, spec.BlockSignersAddress, signer.Address); await signer.Sign(transaction); @@ -44,6 +45,9 @@ public static async Task CreateTransactionSign(XdcBlockHeader header, ISigner si if(!added) { throw new Exception("Failed to add signed transaction to the pool."); + } else + { + Debug.WriteLine($"Added sign tx for block {header.Number}"); } long blockNumber = header.Number; @@ -61,7 +65,7 @@ public static async Task CreateTransactionSign(XdcBlockHeader header, ISigner si if(checkNumber > 0 && spec.EpochBlockOpening <= checkNumber && spec.EpochBlockRandomize >= checkNumber) { var randomizeKeyValue = stateDb.Get(randomKey); - Transaction tx = CreateTxOpeningRandomize(nonce + 1, spec.RandomizeSMCBinary, randomizeKeyValue); + Transaction tx = CreateTxOpeningRandomize(nonce + 1, spec.RandomizeSMCBinary, randomizeKeyValue, signer.Address); await signer.Sign(tx); // add local somehow to tx pool @@ -75,17 +79,17 @@ public static async Task CreateTransactionSign(XdcBlockHeader header, ISigner si var randomizeKeyValue = RandStringByte(32); if (checkNumber > 0 && spec.EpochBlockOpening <= checkNumber && spec.EpochBlockRandomize > checkNumber) { - Transaction tx = BuildTxSecretRandomize(nonce + 1, spec.RandomizeSMCBinary, (ulong)spec.EpochLength, randomizeKeyValue); + Transaction tx = BuildTxSecretRandomize(nonce + 1, spec.RandomizeSMCBinary, (ulong)spec.EpochLength, randomizeKeyValue, signer.Address); await signer.Sign(tx); // add local somehow to tx pool bool addedOpening = txPool.SubmitTx(tx, TxHandlingOptions.PersistentBroadcast); - stateDb.PutSpan(randomKey, randomizeKeyValue); + stateDb.PutSpan(randomKey, randomizeKeyValue); } } } - private static Transaction CreateTxSign(UInt256 number, Hash256 hash, UInt256 nonce, Address blockSignersAddress) + internal static Transaction CreateTxSign(UInt256 number, Hash256 hash, UInt256 nonce, Address blockSignersAddress, Address sender) { var functionSelector = Bytes.FromHexString(HexSignMethod); // hexSetSecret like "0x..." (method id +) byte[] inputData = [.. functionSelector, .. number.PaddedBytes(32), .. hash.Bytes.PadLeft(32)]; @@ -97,6 +101,7 @@ private static Transaction CreateTxSign(UInt256 number, Hash256 hash, UInt256 no transaction.GasLimit = 200_000; transaction.GasPrice = 0; transaction.Data = inputData; + transaction.SenderAddress = sender; transaction.Type = TxType.Legacy; @@ -105,7 +110,7 @@ private static Transaction CreateTxSign(UInt256 number, Hash256 hash, UInt256 no return transaction; } - private static Transaction CreateTxOpeningRandomize(UInt256 nonce, Address randomizeSMCBinary, byte[] randomizeKey) + internal static Transaction CreateTxOpeningRandomize(UInt256 nonce, Address randomizeSMCBinary, byte[] randomizeKey, Address sender) { var functionSelector = Bytes.FromHexString(HexSetOpening); // hexSetSecret like "0x..." (method id +) byte[] inputData = [.. functionSelector, .. randomizeKey]; @@ -117,6 +122,7 @@ private static Transaction CreateTxOpeningRandomize(UInt256 nonce, Address rando transaction.GasLimit = 200_000; transaction.GasPrice = 0; transaction.Data = inputData; + transaction.SenderAddress = sender; transaction.Type = TxType.Legacy; @@ -125,7 +131,7 @@ private static Transaction CreateTxOpeningRandomize(UInt256 nonce, Address rando return transaction; } - public static Transaction BuildTxSecretRandomize(UInt256 nonce, Address randomizeSMCBinary, ulong epochNumber, byte[] randomizeKey) + internal static Transaction BuildTxSecretRandomize(UInt256 nonce, Address randomizeSMCBinary, ulong epochNumber, byte[] randomizeKey, Address sender) { var functionSelector = Bytes.FromHexString(HexSetSecret); // hexSetSecret like "0x..." (method id +) var secretNumb = RandomNumberGenerator.GetInt32((int)epochNumber); @@ -155,6 +161,7 @@ public static Transaction BuildTxSecretRandomize(UInt256 nonce, Address randomiz transaction.GasLimit = 200_000; transaction.GasPrice = 0; transaction.Data = inputData; + transaction.SenderAddress = sender; transaction.Type = TxType.Legacy; diff --git a/src/Nethermind/Nethermind.Xdc/XdcBlockBuildingTransactionExecutor.cs b/src/Nethermind/Nethermind.Xdc/XdcBlockBuildingTransactionExecutor.cs new file mode 100644 index 00000000000..f0bba95e2cf --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/XdcBlockBuildingTransactionExecutor.cs @@ -0,0 +1,109 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Blockchain.Tracing; +using Nethermind.Consensus.Processing; +using Nethermind.Consensus.Producers; +using Nethermind.Core; +using Nethermind.Core.Collections; +using Nethermind.Core.Specs; +using Nethermind.Evm.State; +using Nethermind.Evm.TransactionProcessing; +using Nethermind.Logging; +using Nethermind.State.Proofs; +using Nethermind.TxPool.Comparison; +using System.Collections.Generic; +using System.Threading; +using Nethermind.TxPool; +using static Nethermind.Consensus.Processing.BlockProcessor; + +namespace Nethermind.Xdc; + +internal class XdcBlockBuildingTransactionExecutor( + ITransactionProcessorAdapter transactionProcessor, + IWorldState stateProvider, + IBlockProductionTransactionPicker txPicker, + ISpecProvider specProvider, + ILogManager logManager) : BlockProcessor.BlockProductionTransactionsExecutor(transactionProcessor, stateProvider, txPicker, logManager) +{ + public override TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processingOptions, BlockReceiptsTracer receiptsTracer, CancellationToken token) + { + // We start with high number as don't want to resize too much + const int defaultTxCount = 512; + + var spec = specProvider.GetXdcSpec(block.Header as XdcBlockHeader); + + BlockToProduce? blockToProduce = block as BlockToProduce; + + // Don't use blockToProduce.Transactions.Count() as that would fully enumerate which is expensive + int txCount = blockToProduce is not null ? defaultTxCount : block.Transactions.Length; + IEnumerable transactions = blockToProduce?.Transactions ?? block.Transactions; + + ArrayPoolListRef includedTx = new(txCount); + + HashSet consideredTx = new(ByHashTxComparer.Instance); + int i = 0; + + HashSet delayedTx = new(ByHashTxComparer.Instance); + try + { + + foreach (Transaction currentTx in transactions) + { + // Check if we have gone over time or the payload has been requested + if (token.IsCancellationRequested) break; + + if (!currentTx.IsSpecialTransaction(spec)) + { + delayedTx.Add(currentTx); + continue; + } + + if (!ProcessSingleTransaction(block, processingOptions, receiptsTracer, blockToProduce, ref includedTx, consideredTx, i, currentTx)) + { + break; + } + } + + + foreach (Transaction currentTx in delayedTx) + { + // Check if we have gone over time or the payload has been requested + if (token.IsCancellationRequested) break; + + if(!ProcessSingleTransaction(block, processingOptions, receiptsTracer, blockToProduce, ref includedTx, consideredTx, i, currentTx)) + { + break; + } + } + + block.Header.TxRoot = TxTrie.CalculateRoot(includedTx.AsSpan()); + if (blockToProduce is not null) + { + blockToProduce.Transactions = includedTx.ToArray(); + } + return receiptsTracer.TxReceipts.ToArray(); + } finally + { + includedTx.Dispose(); + } + } + + private bool ProcessSingleTransaction(Block block, ProcessingOptions processingOptions, BlockReceiptsTracer receiptsTracer, BlockToProduce blockToProduce, ref ArrayPoolListRef includedTx, HashSet consideredTx, int i, Transaction currentTx) + { + TxAction action = ProcessTransaction(block, currentTx, i++, receiptsTracer, processingOptions, consideredTx); + if (action == TxAction.Stop) return false; + + consideredTx.Add(currentTx); + if (action == TxAction.Add) + { + includedTx.Add(currentTx); + if (blockToProduce is not null) + { + blockToProduce.TxByteLength += currentTx.GetLength(false); + } + } + + return true; + } +} diff --git a/src/Nethermind/Nethermind.Xdc/XdcBlockProductionEnvFactory.cs b/src/Nethermind/Nethermind.Xdc/XdcBlockProductionEnvFactory.cs index 327349bd421..e43ea0e9c59 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcBlockProductionEnvFactory.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcBlockProductionEnvFactory.cs @@ -9,13 +9,15 @@ using System; using System.Collections.Generic; using System.Text; +using static Nethermind.Consensus.Processing.BlockProcessor; namespace Nethermind.Xdc; -public class XdcBlockProductionEnvFactory(ILifetimeScope rootLifetime, IWorldStateManager worldStateManager, IBlockProducerTxSourceFactory txSourceFactory) : BlockProducerEnvFactory(rootLifetime, worldStateManager, txSourceFactory) +public class XdcBlockProductionEnvFactory(ILifetimeScope rootLifetime, IWorldStateManager worldStateManager, IBlockProducerTxSourceFactory txSourceFactory) + : BlockProducerEnvFactory(rootLifetime, worldStateManager, txSourceFactory) { protected override ContainerBuilder ConfigureBuilder(ContainerBuilder builder) => // Taiko does not seems to use `BlockProductionTransactionsExecutor` base.ConfigureBuilder(builder) - .AddScoped(); + .AddScoped(); } diff --git a/src/Nethermind/Nethermind.Xdc/XdcBlockValidationModule.cs b/src/Nethermind/Nethermind.Xdc/XdcBlockValidationModule.cs new file mode 100644 index 00000000000..fd1c82e23f2 --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/XdcBlockValidationModule.cs @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Autofac; +using Nethermind.Consensus.Processing; +using Nethermind.Core; +using Nethermind.Core.Container; +using System; +using System.Collections.Generic; +using System.Text; + +namespace Nethermind.Xdc; + +internal class XdcBlockValidationModule : Module, IBlockValidationModule +{ + protected override void Load(ContainerBuilder builder) + { + builder.AddScoped(); + } +} diff --git a/src/Nethermind/Nethermind.Xdc/XdcTransactionExecutor.cs b/src/Nethermind/Nethermind.Xdc/XdcBlockValidationTransactionExecutor.cs similarity index 88% rename from src/Nethermind/Nethermind.Xdc/XdcTransactionExecutor.cs rename to src/Nethermind/Nethermind.Xdc/XdcBlockValidationTransactionExecutor.cs index 4e95b4afd56..653ddd7c1a9 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcTransactionExecutor.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcBlockValidationTransactionExecutor.cs @@ -11,15 +11,15 @@ using Nethermind.Int256; using Nethermind.Xdc.Spec; using System; -using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using Metrics = Nethermind.Evm.Metrics; namespace Nethermind.Xdc; -internal class XdcTransactionExecutor(ITransactionProcessorAdapter txProcessorAdapter, IWorldState stateProvider, ISpecProvider specProvider) +internal class XdcBlockValidationTransactionExecutor(ITransactionProcessorAdapter txProcessorAdapter, IWorldState stateProvider, ISpecProvider specProvider) : BlockProcessor.BlockValidationTransactionsExecutor(txProcessorAdapter, stateProvider) { public override TxReceipt[] ProcessTransactions(Block block, ProcessingOptions processingOptions, BlockReceiptsTracer receiptsTracer, CancellationToken token) diff --git a/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs b/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs index b941cd8920f..2940066e1e1 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs @@ -250,6 +250,11 @@ internal async Task RunRoundChecks(CancellationToken ct) { _highestSelfMinedRound = currentRound; Task blockBuilder = BuildAndProposeBlock(roundParent, currentRound, spec, ct); + + if ((roundParent.Number % spec.MergeSignRange == 0) || !(spec.TIP2019Block <= roundParent.Number)) + { + await ContractsUtils.CreateTransactionSign(roundParent, _signer, _stateDb, _txPool, spec); + } } if (spec.SwitchBlock < roundParent.Number) @@ -257,10 +262,6 @@ internal async Task RunRoundChecks(CancellationToken ct) await CommitCertificateAndVote(roundParent, epochInfo); } - if((roundParent.Number % spec.MergeSignRange == 0) || !(spec.TIP2019Block <= roundParent.Number)) - { - await ContractsUtils.CreateTransactionSign(roundParent, _signer, _stateDb, _txPool, spec); - } } private XdcBlockHeader GetParentForRound() diff --git a/src/Nethermind/Nethermind.Xdc/XdcModule.cs b/src/Nethermind/Nethermind.Xdc/XdcModule.cs index b4f1b7177e0..4338879197c 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcModule.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcModule.cs @@ -11,6 +11,7 @@ using Nethermind.Consensus.Producers; using Nethermind.Consensus.Validators; using Nethermind.Core; +using Nethermind.Core.Container; using Nethermind.Core.Specs; using Nethermind.Db; using Nethermind.Evm.TransactionProcessing; @@ -70,8 +71,9 @@ protected override void Load(ContainerBuilder builder) .AddSingleton() // block processing + .AddScoped() .AddScoped() - .AddSingleton() + .AddScoped() ; } diff --git a/src/Nethermind/Nethermind.Xdc/XdcTransactionProcessor.cs b/src/Nethermind/Nethermind.Xdc/XdcTransactionProcessor.cs index e641e75a70b..388f1745b3b 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcTransactionProcessor.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcTransactionProcessor.cs @@ -29,7 +29,7 @@ protected override TransactionResult ValidateStatic(Transaction tx, BlockHeader Address target = tx.To; Address sender = tx.SenderAddress; - if (xdcSpec.BlackListHFNumber == header.Number) + if (xdcSpec.BlackListHFNumber <= header.Number) { if (IsBlackListed(xdcSpec, sender) || IsBlackListed(xdcSpec, target)) { @@ -47,8 +47,8 @@ protected override TransactionResult ValidateStatic(Transaction tx, BlockHeader return TransactionResult.MalformedTransaction; } - UInt256 blkNumber = new UInt256(tx.Data.Span[8..40], true); - if (blkNumber >= (UInt256)header.Number || blkNumber <= (UInt256)(header.Number - (xdcSpec.EpochLength * 2))) + UInt256 blkNumber = new UInt256(tx.Data.Span.Slice(4, 32), true); + if (blkNumber >= header.Number || blkNumber <= (header.Number - (xdcSpec.EpochLength * 2))) { // Invalid block number in special transaction data return TransactionResult.MalformedTransaction; From 0d4248f804ce488ff1e6ad3e4d09b06cdcabe269 Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Tue, 9 Dec 2025 12:15:43 +0100 Subject: [PATCH 10/15] add more tests --- .../Helpers/XdcTestBlockchain.cs | 24 +----- .../SpecialTransactionsTests.cs | 85 +++++++++++++++++-- .../XdcBlockProductionEnvFactory.cs | 2 +- src/Nethermind/Nethermind.Xdc/XdcModule.cs | 2 +- 4 files changed, 82 insertions(+), 31 deletions(-) diff --git a/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs b/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs index 0c6d63e9a2f..3f0b5dabf2f 100644 --- a/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs +++ b/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs @@ -233,8 +233,8 @@ private IXdcReleaseSpec WrapReleaseSpec(IReleaseSpec spec) ]; xdcSpec.MergeSignRange = 15; - xdcSpec.RandomizeSMCBinary = new Address("0x7c2E0c7B4F06C3cF4dA27AE5A6b89C4A0F1C9c41"); - xdcSpec.BlockSignersAddress = new Address("0xA45eEFb7d2a6800F9f39E35C0F0F8D5E0d7C3B22"); + xdcSpec.RandomizeSMCBinary = new Address("0x0000000000000000000000000000000000000089"); + xdcSpec.BlockSignersAddress = new Address("0x0000000000000000000000000000000000000090"); xdcSpec.TIP2019Block = 0; V2ConfigParams[] v2ConfigParams = [ @@ -352,25 +352,8 @@ public Block Build() state.CreateAccount(genesisSpec!.BlockSignersAddress, 100_000); state.CreateAccount(genesisSpec!.RandomizeSMCBinary, 100_000); - var gasEaterCode = Prepare.EvmCode - .JUMPDEST() - .ADD(1, 1) - .POP() - .JUMP(0) - .Done; - var gasEaterHashcode = Keccak.Compute(gasEaterCode); - var gasEaterAddress = Address.FromNumber(new UInt256(gasEaterHashcode.Bytes.Slice(0, 32))); - state.CreateAccount(gasEaterAddress, 100_000); - var dummyCode = Prepare.EvmCode - .PushData(0) - .PushData(0) - .PushData(0) - .PushData(0) - .PushData(1) - .PushData(gasEaterAddress) - .GAS() - .CALL() + .STOP() .Done; var dummyCodeHashcode = Keccak.Compute(dummyCode); @@ -438,6 +421,7 @@ public async Task AddBlocks(int count, bool withTransaction = false) } } + public override async Task AddBlock(params Transaction[] transactions) { var b = await AddBlockWithoutCommitQc(transactions); diff --git a/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs b/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs index 9c2b1623a93..7e7b1622cbb 100644 --- a/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs +++ b/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs @@ -12,7 +12,9 @@ using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; +using Nethermind.Core.Test.Blockchain; using Nethermind.Core.Test.Builders; +using Nethermind.Crypto; using Nethermind.Db; using Nethermind.Evm; using Nethermind.Evm.Tracing; @@ -40,6 +42,51 @@ private bool IsTimeForOnchainSignature(IXdcReleaseSpec spec, long blockNumber) return blockNumber % spec.MergeSignRange == 0; } + private Task ProposeBatchTransferTxFrom(PrivateKey source, PrivateKey destination, UInt256 amount, int count, XdcTestBlockchain chain) + { + return Task.Run(() => + { + (PrivateKey, PrivateKey) swap(PrivateKey a, PrivateKey b) => (b, a); + + for (int i = 0; i < count; i++) + { + (source, destination) = swap(source, destination); + CreateTransferTxFrom(source, destination, amount, chain); + // propose the tx to the tx pool + + } + }); + } + + private Transaction CreateTransferTxFrom(PrivateKey source, PrivateKey destination, UInt256 amount, XdcTestBlockchain chain) + { + // create a tx that transfers amount of eth from source address to destination address + Transaction tx = Build.A.Transaction + .WithSenderAddress(source.Address) + .WithTo(destination.Address) + .WithValue(amount) + .WithType(TxType.Legacy) + .TestObject; + + // sign the tx + var signer = new Signer(chain.SpecProvider.ChainId, source, NullLogManager.Instance); + signer.Sign(tx); + + tx.Hash = tx.CalculateHash(); + + var result = chain.TxPool.SubmitTx(tx, TxHandlingOptions.None); + + return tx; + } + + private PrivateKey[] FilledAccounts(XdcTestBlockchain chain) + { + var genesisSpec = chain.SpecProvider.GenesisSpec as XdcReleaseSpec; + var pks = chain.MasterNodeCandidates + .Where(k => genesisSpec!.GenesisMasterNodes.Contains(k.Address)); + return pks.ToArray(); + } + [Test] public async Task Special_Tx_Is_Dispatched_On_MergeSignRange_Block() { @@ -64,6 +111,8 @@ public async Task Special_Tx_Is_Dispatched_On_MergeSignRange_Block() } while (!IsTimeForOnchainSignature(blockChain.SpecProvider.GetXdcSpec(head), head.Number - 1)); + + Assert.That(blockChain.BlockTree.Head.Number, Is.EqualTo(mergeSignBlockRange + 1)); await Task.Delay(((XdcReleaseSpec)blockChain.SpecProvider.GetFinalSpec()).MinePeriod.Seconds()); // to avoid tight loop @@ -87,9 +136,11 @@ public async Task Special_Tx_Is_Not_Dispatched_Outside_MergeSignRange_Block() { var blockChain = await XdcTestBlockchain.Create(1, true); + var mergeSignBlockRange = 5; + blockChain.ChangeReleaseSpec((spec) => { - spec.MergeSignRange = 5; + spec.MergeSignRange = mergeSignBlockRange; spec.IsEip1559Enabled = false; }); @@ -119,41 +170,57 @@ public async Task Special_Tx_Is_Executed_Before_Normal_Txs() { var blockChain = await XdcTestBlockchain.Create(1, true); + var mergeSignBlockRange = 5; + blockChain.ChangeReleaseSpec((spec) => { - spec.MergeSignRange = 5; + spec.MergeSignRange = mergeSignBlockRange; spec.IsEip1559Enabled = false; }); blockChain.StartHotStuffModule(); XdcBlockHeader? head = blockChain.BlockTree.Head!.Header as XdcBlockHeader; - do + var spec = blockChain.SpecProvider.GetXdcSpec(head!); + + var random = new Random(); + + var accounts = FilledAccounts(blockChain); + + for (int i = 1; i < spec.MergeSignRange + 2; i++) { + if (head!.Number == mergeSignBlockRange + 1) + { + var source = accounts.ElementAt(random.Next() % accounts.Length); + var dest = accounts.Except([source]).ElementAt(random.Next() % (accounts.Length - 1)); + await ProposeBatchTransferTxFrom(source, dest, 1, 2, blockChain); + } + await blockChain.TriggerAndSimulateBlockProposalAndVoting(); await Task.Delay(blockChain.SpecProvider.GetXdcSpec(head!).MinePeriod.Seconds()); // to avoid tight loop head = (XdcBlockHeader)blockChain.BlockTree.Head!.Header; } - while (!IsTimeForOnchainSignature(blockChain.SpecProvider.GetXdcSpec(head), head.Number - 1)); - var block = blockChain.BlockTree.Head!; - var spec = blockChain.SpecProvider.GetXdcSpec(head); + var block = (XdcBlockHeader)blockChain.BlockTree.Head.Header; + spec = blockChain.SpecProvider.GetXdcSpec(block!); var receipts = blockChain.ReceiptStorage.Get(block.Hash!); Assert.That(receipts, Is.Not.Empty); + Assert.That(receipts.Length, Is.GreaterThan(1)); bool onlyEncounteredSpecialTx = true; foreach (var transaction in receipts) { - if(transaction.Recipient == spec.BlockSignersAddress || transaction.Recipient == spec.RandomizeSMCBinary) + if (transaction.Recipient == spec.BlockSignersAddress || transaction.Recipient == spec.RandomizeSMCBinary) { - if(!onlyEncounteredSpecialTx) + if (!onlyEncounteredSpecialTx) { // we encountered a normal transaction before so special txs are not lumped at the start Assert.Fail(); } - } else + } + else { onlyEncounteredSpecialTx = false; } diff --git a/src/Nethermind/Nethermind.Xdc/XdcBlockProductionEnvFactory.cs b/src/Nethermind/Nethermind.Xdc/XdcBlockProductionEnvFactory.cs index e43ea0e9c59..93cc99b9121 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcBlockProductionEnvFactory.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcBlockProductionEnvFactory.cs @@ -19,5 +19,5 @@ public class XdcBlockProductionEnvFactory(ILifetimeScope rootLifetime, IWorldSta protected override ContainerBuilder ConfigureBuilder(ContainerBuilder builder) => // Taiko does not seems to use `BlockProductionTransactionsExecutor` base.ConfigureBuilder(builder) - .AddScoped(); + .AddScoped(); } diff --git a/src/Nethermind/Nethermind.Xdc/XdcModule.cs b/src/Nethermind/Nethermind.Xdc/XdcModule.cs index 4338879197c..78404a15e77 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcModule.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcModule.cs @@ -71,9 +71,9 @@ protected override void Load(ContainerBuilder builder) .AddSingleton() // block processing + .AddSingleton() .AddScoped() .AddScoped() - .AddScoped() ; } From 52b2eadf86518d2ddb8ceaee6b4ec6da38ed5625 Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Tue, 9 Dec 2025 12:17:28 +0100 Subject: [PATCH 11/15] clean up --- .../Nethermind.Xdc.Test/SpecialTransactionsTests.cs | 4 ---- src/Nethermind/Nethermind.Xdc/ContractsUtils.cs | 3 --- 2 files changed, 7 deletions(-) diff --git a/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs b/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs index 7e7b1622cbb..d3b2541d4f1 100644 --- a/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs +++ b/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs @@ -52,15 +52,12 @@ private Task ProposeBatchTransferTxFrom(PrivateKey source, PrivateKey destinatio { (source, destination) = swap(source, destination); CreateTransferTxFrom(source, destination, amount, chain); - // propose the tx to the tx pool - } }); } private Transaction CreateTransferTxFrom(PrivateKey source, PrivateKey destination, UInt256 amount, XdcTestBlockchain chain) { - // create a tx that transfers amount of eth from source address to destination address Transaction tx = Build.A.Transaction .WithSenderAddress(source.Address) .WithTo(destination.Address) @@ -68,7 +65,6 @@ private Transaction CreateTransferTxFrom(PrivateKey source, PrivateKey destinati .WithType(TxType.Legacy) .TestObject; - // sign the tx var signer = new Signer(chain.SpecProvider.ChainId, source, NullLogManager.Instance); signer.Sign(tx); diff --git a/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs b/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs index 6c38477de9a..f2b50fd9116 100644 --- a/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs +++ b/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs @@ -45,9 +45,6 @@ public static async Task CreateTransactionSign(XdcBlockHeader header, ISigner si if(!added) { throw new Exception("Failed to add signed transaction to the pool."); - } else - { - Debug.WriteLine($"Added sign tx for block {header.Number}"); } long blockNumber = header.Number; From f0c0a7fb96c771c65a8205b358c00855107f86b3 Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Tue, 9 Dec 2025 14:16:51 +0100 Subject: [PATCH 12/15] update test submodule --- src/tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests b/src/tests index ae4791077e8..3ee1b579dca 160000 --- a/src/tests +++ b/src/tests @@ -1 +1 @@ -Subproject commit ae4791077e8fcf716136e70fe8392f1a1f1495fb +Subproject commit 3ee1b579dca95ae0b2cebdc3fc286b88c1e87e14 From 30792a489ae832e7eff58a22baf73f01ddbabbb8 Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Tue, 9 Dec 2025 14:21:12 +0100 Subject: [PATCH 13/15] checkout tests commit from master --- src/tests | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tests b/src/tests index 3ee1b579dca..08b5fe38c23 160000 --- a/src/tests +++ b/src/tests @@ -1 +1 @@ -Subproject commit 3ee1b579dca95ae0b2cebdc3fc286b88c1e87e14 +Subproject commit 08b5fe38c23c3ea4f88af93e10fbf03b715414f7 From dcadfc4474503ab5537286e2bd4630b5f886a6d6 Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Tue, 9 Dec 2025 16:56:33 +0100 Subject: [PATCH 14/15] ws fixes --- .../Nethermind.Core/Collections/ArrayPoolList.cs | 2 +- .../SpecialTransactionsTests.cs | 4 ++-- src/Nethermind/Nethermind.Xdc/ContractsUtils.cs | 4 ++-- .../Nethermind.Xdc/Spec/XdcReleaseSpec.cs | 16 ++++++++-------- .../XdcBlockBuildingTransactionExecutor.cs | 5 +++-- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs index cc8e1349d68..178027c5d29 100644 --- a/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs +++ b/src/Nethermind/Nethermind.Core/Collections/ArrayPoolList.cs @@ -243,7 +243,7 @@ public void Dispose() ArrayPoolListCore.Dispose(_arrayPool, ref _array, ref _count, ref _capacity, ref _disposed); #if DEBUG - GC.SuppressFinalize(this); + GC.SuppressFinalize(this); #endif } diff --git a/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs b/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs index d3b2541d4f1..cc13ce3e1f2 100644 --- a/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs +++ b/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs @@ -50,7 +50,7 @@ private Task ProposeBatchTransferTxFrom(PrivateKey source, PrivateKey destinatio for (int i = 0; i < count; i++) { - (source, destination) = swap(source, destination); + (source, destination) = swap(source, destination); CreateTransferTxFrom(source, destination, amount, chain); } }); @@ -107,7 +107,7 @@ public async Task Special_Tx_Is_Dispatched_On_MergeSignRange_Block() } while (!IsTimeForOnchainSignature(blockChain.SpecProvider.GetXdcSpec(head), head.Number - 1)); - + Assert.That(blockChain.BlockTree.Head.Number, Is.EqualTo(mergeSignBlockRange + 1)); diff --git a/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs b/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs index f2b50fd9116..ab980ddbfa8 100644 --- a/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs +++ b/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs @@ -42,7 +42,7 @@ public static async Task CreateTransactionSign(XdcBlockHeader header, ISigner si // add local somehow to tx pool bool added = txPool.SubmitTx(transaction, TxHandlingOptions.PersistentBroadcast); - if(!added) + if (!added) { throw new Exception("Failed to add signed transaction to the pool."); } @@ -59,7 +59,7 @@ public static async Task CreateTransactionSign(XdcBlockHeader header, ISigner si if (exists) { - if(checkNumber > 0 && spec.EpochBlockOpening <= checkNumber && spec.EpochBlockRandomize >= checkNumber) + if (checkNumber > 0 && spec.EpochBlockOpening <= checkNumber && spec.EpochBlockRandomize >= checkNumber) { var randomizeKeyValue = stateDb.Get(randomKey); Transaction tx = CreateTxOpeningRandomize(nonce + 1, spec.RandomizeSMCBinary, randomizeKeyValue, signer.Address); diff --git a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs index 4fd7396c813..5873a574ff5 100644 --- a/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs +++ b/src/Nethermind/Nethermind.Xdc/Spec/XdcReleaseSpec.cs @@ -31,14 +31,14 @@ public class XdcReleaseSpec : ReleaseSpec, IXdcReleaseSpec public List V2Configs { get; set; } = new List(); public Address[] GenesisMasterNodes { get; set; } - public Address BlockSignersAddress { get ; set ; } - public Address RandomizeSMCBinary { get ; set ; } - public long BlackListHFNumber { get ; set ; } - public long EpochBlockOpening { get ; set ; } - public long EpochBlockRandomize { get ; set ; } - public long MergeSignRange { get ; set ; } - public long TIP2019Block { get ; set ; } - public Address[] BlackListedAddresses { get ; set ; } + public Address BlockSignersAddress { get; set; } + public Address RandomizeSMCBinary { get; set; } + public long BlackListHFNumber { get; set; } + public long EpochBlockOpening { get; set; } + public long EpochBlockRandomize { get; set; } + public long MergeSignRange { get; set; } + public long TIP2019Block { get; set; } + public Address[] BlackListedAddresses { get; set; } public void ApplyV2Config(ulong round) { diff --git a/src/Nethermind/Nethermind.Xdc/XdcBlockBuildingTransactionExecutor.cs b/src/Nethermind/Nethermind.Xdc/XdcBlockBuildingTransactionExecutor.cs index f0bba95e2cf..cee606dc4c4 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcBlockBuildingTransactionExecutor.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcBlockBuildingTransactionExecutor.cs @@ -71,7 +71,7 @@ public override TxReceipt[] ProcessTransactions(Block block, ProcessingOptions p // Check if we have gone over time or the payload has been requested if (token.IsCancellationRequested) break; - if(!ProcessSingleTransaction(block, processingOptions, receiptsTracer, blockToProduce, ref includedTx, consideredTx, i, currentTx)) + if (!ProcessSingleTransaction(block, processingOptions, receiptsTracer, blockToProduce, ref includedTx, consideredTx, i, currentTx)) { break; } @@ -83,7 +83,8 @@ public override TxReceipt[] ProcessTransactions(Block block, ProcessingOptions p blockToProduce.Transactions = includedTx.ToArray(); } return receiptsTracer.TxReceipts.ToArray(); - } finally + } + finally { includedTx.Dispose(); } From 9abf609ada7f829b1752e38355fa2ae6616e7c69 Mon Sep 17 00:00:00 2001 From: Ayman Bouchareb Date: Wed, 10 Dec 2025 11:30:07 +0100 Subject: [PATCH 15/15] refactor staticness away --- .../Helpers/XdcTestBlockchain.cs | 17 +---------------- .../SpecialTransactionsTests.cs | 10 +++++----- .../Nethermind.Xdc/ISignTransactionManager.cs | 12 ++++++++++++ ...tractsUtils.cs => SignTransactionManager.cs} | 7 ++++--- src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs | 13 ++++--------- src/Nethermind/Nethermind.Xdc/XdcModule.cs | 8 ++++++++ 6 files changed, 34 insertions(+), 33 deletions(-) create mode 100644 src/Nethermind/Nethermind.Xdc/ISignTransactionManager.cs rename src/Nethermind/Nethermind.Xdc/{ContractsUtils.cs => SignTransactionManager.cs} (96%) diff --git a/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs b/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs index 3f0b5dabf2f..31bff504585 100644 --- a/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs +++ b/src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs @@ -310,22 +310,7 @@ protected override IBlockProducerRunner CreateBlockProducerRunner() { if (_useHotStuffModule) { - return new XdcHotStuff( - BlockTree, - XdcContext, - SpecProvider, - Container.Resolve(), - EpochSwitchManager, - SnapshotManager, - QuorumCertificateManager, - VotesManager, - Signer, - Container.Resolve(), - Container.Resolve(), - new MemDb(), - Container.Resolve(), - LogManager - ); + return Container.Resolve(); } return base.CreateBlockProducerRunner(); } diff --git a/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs b/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs index cc13ce3e1f2..5a64b0380c3 100644 --- a/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs +++ b/src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs @@ -248,7 +248,7 @@ public async Task Tx_With_With_BlackListed_Sender_Fails_Validation(bool blackLis moqVm.SetBlockExecutionContext(new BlockExecutionContext(head, spec)); - var txSign = ContractsUtils.CreateTxSign((UInt256)head.Number, head.Hash!, blockChain.TxPool.GetLatestPendingNonce(TestItem.AddressA), spec.BlockSignersAddress, blockChain.Signer.Address); + var txSign = SignTransactionManager.CreateTxSign((UInt256)head.Number, head.Hash!, blockChain.TxPool.GetLatestPendingNonce(TestItem.AddressA), spec.BlockSignersAddress, blockChain.Signer.Address); await blockChain.Signer.Sign(txSign); TransactionResult? result = null; @@ -345,7 +345,7 @@ public async Task Malformed_WrongLenght_SpecialTx_Fails_Validation(bool isSpecia if (isSpecialTx) { - tx = ContractsUtils.CreateTxSign((UInt256)head.Number, head.Hash!, blockChain.TxPool.GetLatestPendingNonce(TestItem.AddressA), spec.BlockSignersAddress, blockChain.Signer.Address); + tx = SignTransactionManager.CreateTxSign((UInt256)head.Number, head.Hash!, blockChain.TxPool.GetLatestPendingNonce(TestItem.AddressA), spec.BlockSignersAddress, blockChain.Signer.Address); } else { @@ -399,7 +399,7 @@ public async Task Malformed_WrongBlockNumber_BlockLessThanCurrent_SpecialTx_Fail var blockNumber = head.Number - 1; - Transaction? tx = ContractsUtils.CreateTxSign((UInt256)blockNumber, head.Hash!, blockChain.TxPool.GetLatestPendingNonce(TestItem.AddressA), spec.BlockSignersAddress, blockChain.Signer.Address); + Transaction? tx = SignTransactionManager.CreateTxSign((UInt256)blockNumber, head.Hash!, blockChain.TxPool.GetLatestPendingNonce(TestItem.AddressA), spec.BlockSignersAddress, blockChain.Signer.Address); await blockChain.Signer.Sign(tx); @@ -429,7 +429,7 @@ public async Task Malformed_WrongBlockNumber_BlockEqualToCurrent_SpecialTx_Fails var blockNumber = head.Number; - Transaction? tx = ContractsUtils.CreateTxSign((UInt256)blockNumber, head.Hash!, blockChain.TxPool.GetLatestPendingNonce(TestItem.AddressA), spec.BlockSignersAddress, blockChain.Signer.Address); + Transaction? tx = SignTransactionManager.CreateTxSign((UInt256)blockNumber, head.Hash!, blockChain.TxPool.GetLatestPendingNonce(TestItem.AddressA), spec.BlockSignersAddress, blockChain.Signer.Address); await blockChain.Signer.Sign(tx); @@ -459,7 +459,7 @@ public async Task Malformed_WrongBlockNumber_BlockBiggerThanCurrent_SpecialTx_Fa var blockNumber = head.Number + 1; - Transaction? tx = ContractsUtils.CreateTxSign((UInt256)blockNumber, head.Hash!, blockChain.TxPool.GetLatestPendingNonce(TestItem.AddressA), spec.BlockSignersAddress, blockChain.Signer.Address); + Transaction? tx = SignTransactionManager.CreateTxSign((UInt256)blockNumber, head.Hash!, blockChain.TxPool.GetLatestPendingNonce(TestItem.AddressA), spec.BlockSignersAddress, blockChain.Signer.Address); await blockChain.Signer.Sign(tx); diff --git a/src/Nethermind/Nethermind.Xdc/ISignTransactionManager.cs b/src/Nethermind/Nethermind.Xdc/ISignTransactionManager.cs new file mode 100644 index 00000000000..ab1da9ce73b --- /dev/null +++ b/src/Nethermind/Nethermind.Xdc/ISignTransactionManager.cs @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2025 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using Nethermind.Xdc.Spec; +using System.Threading.Tasks; + +namespace Nethermind.Xdc; + +internal interface ISignTransactionManager +{ + Task CreateTransactionSign(XdcBlockHeader header, IXdcReleaseSpec spec); +} \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs b/src/Nethermind/Nethermind.Xdc/SignTransactionManager.cs similarity index 96% rename from src/Nethermind/Nethermind.Xdc/ContractsUtils.cs rename to src/Nethermind/Nethermind.Xdc/SignTransactionManager.cs index ab980ddbfa8..e80d87ca9ef 100644 --- a/src/Nethermind/Nethermind.Xdc/ContractsUtils.cs +++ b/src/Nethermind/Nethermind.Xdc/SignTransactionManager.cs @@ -28,12 +28,13 @@ namespace Nethermind.Xdc; -internal static class ContractsUtils +internal class SignTransactionManager(IDb stateDb, ISigner signer, ITxPool txPool) : ISignTransactionManager { const string HexSignMethod = "e341eaa4"; const string HexSetSecret = "34d38600"; const string HexSetOpening = "e11f5ba2"; - public static async Task CreateTransactionSign(XdcBlockHeader header, ISigner signer, IDb stateDb, ITxPool txPool, IXdcReleaseSpec spec) + + public async Task CreateTransactionSign(XdcBlockHeader header, IXdcReleaseSpec spec) { UInt256 nonce = txPool.GetLatestPendingNonce(signer.Address); Transaction transaction = CreateTxSign((UInt256)header.Number, header.Hash, nonce, spec.BlockSignersAddress, signer.Address); @@ -193,7 +194,7 @@ private static string Encrypt(byte[] randomizeKey, string text) return Convert.ToBase64String(cipherBytes); } - public static byte[] RandStringByte(int n) + internal static byte[] RandStringByte(int n) { const string letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789"; var result = new byte[n]; diff --git a/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs b/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs index 2940066e1e1..7657676c6de 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcHotStuff.cs @@ -7,9 +7,7 @@ using Nethermind.Consensus.Producers; using Nethermind.Core; using Nethermind.Core.Specs; -using Nethermind.Db; using Nethermind.Logging; -using Nethermind.TxPool; using Nethermind.Xdc.Spec; using Nethermind.Xdc.Types; using System; @@ -35,9 +33,8 @@ internal class XdcHotStuff : IBlockProducerRunner private readonly ISigner _signer; private readonly ITimeoutTimer _timeoutTimer; private readonly IProcessExitSource _processExit; - private readonly IDb _stateDb; - private readonly ITxPool _txPool; private readonly ILogger _logger; + private readonly ISignTransactionManager _signTransactionManager; private CancellationTokenSource? _cancellationTokenSource; private Task? _runTask; @@ -63,8 +60,7 @@ public XdcHotStuff( ISigner signer, ITimeoutTimer timeoutTimer, IProcessExitSource processExit, - IDb stateDb, - ITxPool txPool, + ISignTransactionManager signTransactionManager, ILogManager logManager) { _blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree)); @@ -76,10 +72,9 @@ public XdcHotStuff( _quorumCertificateManager = quorumCertificateManager ?? throw new ArgumentNullException(nameof(quorumCertificateManager)); _votesManager = votesManager ?? throw new ArgumentNullException(nameof(votesManager)); _signer = signer ?? throw new ArgumentNullException(nameof(signer)); + _signTransactionManager = signTransactionManager ?? throw new ArgumentNullException(nameof(signTransactionManager)); _timeoutTimer = timeoutTimer; _processExit = processExit; - _stateDb = stateDb; - _txPool = txPool; _logger = logManager?.GetClassLogger() ?? throw new ArgumentNullException(nameof(logManager)); _lastActivityTime = DateTime.UtcNow; @@ -253,7 +248,7 @@ internal async Task RunRoundChecks(CancellationToken ct) if ((roundParent.Number % spec.MergeSignRange == 0) || !(spec.TIP2019Block <= roundParent.Number)) { - await ContractsUtils.CreateTransactionSign(roundParent, _signer, _stateDb, _txPool, spec); + await _signTransactionManager.CreateTransactionSign(roundParent, spec); } } diff --git a/src/Nethermind/Nethermind.Xdc/XdcModule.cs b/src/Nethermind/Nethermind.Xdc/XdcModule.cs index 78404a15e77..77ace69ae63 100644 --- a/src/Nethermind/Nethermind.Xdc/XdcModule.cs +++ b/src/Nethermind/Nethermind.Xdc/XdcModule.cs @@ -17,6 +17,7 @@ using Nethermind.Evm.TransactionProcessing; using Nethermind.Init.Modules; using Nethermind.Specs.ChainSpecStyle; +using Nethermind.TxPool; using Nethermind.Xdc.Spec; namespace Nethermind.Xdc; @@ -24,6 +25,7 @@ namespace Nethermind.Xdc; public class XdcModule : Module { private const string SnapshotDbName = "Snapshots"; + private const string SignTxRandomizeDbName = "RandomValues"; protected override void Load(ContainerBuilder builder) { @@ -66,6 +68,8 @@ protected override void Load(ContainerBuilder builder) .AddSingleton() .AddDatabase(SnapshotDbName) .AddSingleton(CreateSnapshotManager) + .AddDatabase(SignTxRandomizeDbName) + .AddSingleton(CreateSignTransactionManager) .AddSingleton() .AddSingleton() .AddSingleton() @@ -81,5 +85,9 @@ private ISnapshotManager CreateSnapshotManager([KeyFilter(SnapshotDbName)] IDb d { return new SnapshotManager(db, blockTree, penaltyHandler); } + private ISignTransactionManager CreateSignTransactionManager([KeyFilter(SignTxRandomizeDbName)] IDb db, ISigner signer, ITxPool txPool) + { + return new SignTransactionManager(db, signer, txPool); + } }