Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,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();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

namespace Nethermind.Evm.TransactionProcessing
{
public sealed class TransactionProcessor(
public class TransactionProcessor(
ITransactionProcessor.IBlobBaseFeeCalculator blobBaseFeeCalculator,
ISpecProvider? specProvider,
IWorldState? worldState,
Expand Down Expand Up @@ -963,6 +963,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);
Expand Down Expand Up @@ -993,6 +994,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
{
Expand All @@ -1008,6 +1010,7 @@ public enum ErrorType
SenderNotSpecified,
TransactionSizeOverMaxInitCodeSize,
WrongTransactionNonce,
ContainsBlacklistedAddress
}
}
}
73 changes: 72 additions & 1 deletion src/Nethermind/Nethermind.Xdc.Test/Helpers/XdcTestBlockchain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -292,7 +310,22 @@ protected override IBlockProducerRunner CreateBlockProducerRunner()
{
if (_useHotStuffModule)
{
return Container.Resolve<IBlockProducerRunner>();
return new XdcHotStuff(
BlockTree,
XdcContext,
SpecProvider,
Container.Resolve<IBlockProducer>(),
EpochSwitchManager,
SnapshotManager,
QuorumCertificateManager,
VotesManager,
Signer,
Container.Resolve<ITimeoutTimer>(),
Container.Resolve<IProcessExitSource>(),
new MemDb(),
Container.Resolve<ITxPool>(),
LogManager
);
}
return base.CreateBlockProducerRunner();
}
Expand All @@ -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
Expand Down
132 changes: 132 additions & 0 deletions src/Nethermind/Nethermind.Xdc.Test/SpecialTransactionsTests.cs
Original file line number Diff line number Diff line change
@@ -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);
}

}
Loading
Loading