Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,13 @@
using Nethermind.Consensus.Processing;
using Nethermind.Core;
using Nethermind.Core.Specs;
using Nethermind.Core.Test.Builders;
using Nethermind.Int256;
using Nethermind.Logging;
using Nethermind.Specs;
using Nethermind.Specs.ChainSpecStyle;
using Nethermind.Specs.Forks;
using NSubstitute;
using NUnit.Framework;

namespace Nethermind.AuRa.Test.Contract;
Expand Down Expand Up @@ -66,6 +71,78 @@ public async Task can_validate_gas_limit_incorrect()
expectedGasLimit.Should().Be(CorrectHeadGasLimit);
}

[Test]
public void Override_takes_precedence_over_contract_limit()
{
const long innerGasLimit = 99_000_000;
const long overrideTarget = 50_000_000;
(AuRaContractGasLimitOverride calculator, IGasLimitCalculator inner) = BuildOverride(contractGasLimit: 42_000_000, innerResult: innerGasLimit);
BlockHeader parent = Build.A.BlockHeader.WithNumber(10).TestObject;

long result = calculator.GetGasLimit(parent, targetGasLimit: overrideTarget);

result.Should().Be(innerGasLimit);
inner.Received(1).GetGasLimit(parent, overrideTarget);
}

[Test]
public void IsGasLimitValid_accepts_within_parent_delta_when_contract_differs()
{
const long parentGasLimit = 30_000_000;
const long contractGasLimit = 42_000_000;
AuRaContractGasLimitOverride calculator = new(
[StubContract(contractGasLimit)],
new AuRaContractGasLimitOverride.Cache(),
minimum2MlnGasPerBlockWhenUsingBlockGasLimitContract: false,
new TargetAdjustedGasLimitCalculator(new TestSpecProvider(Prague.Instance), new BlocksConfig()),
LimboLogs.Instance);
BlockHeader parent = Build.A.BlockHeader.WithNumber(10).WithGasLimit(parentGasLimit).TestObject;

bool inRange = calculator.IsGasLimitValid(parent, parentGasLimit + 29_000, out long? expected);
bool outOfRange = calculator.IsGasLimitValid(parent, parentGasLimit + 10_000_000, out _);

inRange.Should().BeTrue();
expected.Should().Be(contractGasLimit);
outOfRange.Should().BeFalse();
}

[Test]
public void Override_is_forwarded_to_inner_when_no_contract_limit()
{
const long innerGasLimit = 31_000_000;
const long overrideTarget = 33_000_000;
(AuRaContractGasLimitOverride calculator, IGasLimitCalculator inner) = BuildOverride(contractGasLimit: null, innerResult: innerGasLimit);
BlockHeader parent = Build.A.BlockHeader.WithNumber(10).TestObject;

long result = calculator.GetGasLimit(parent, targetGasLimit: overrideTarget);

result.Should().Be(innerGasLimit);
inner.Received(1).GetGasLimit(parent, overrideTarget);
}

private static (AuRaContractGasLimitOverride calculator, IGasLimitCalculator inner) BuildOverride(long? contractGasLimit, long innerResult)
{
IGasLimitCalculator inner = Substitute.For<IGasLimitCalculator>();
inner.GetGasLimit(Arg.Any<BlockHeader>(), Arg.Any<long?>()).Returns(innerResult);

IBlockGasLimitContract[] contracts = contractGasLimit is { } limit ? [StubContract(limit)] : [];
AuRaContractGasLimitOverride calculator = new(
contracts,
new AuRaContractGasLimitOverride.Cache(),
minimum2MlnGasPerBlockWhenUsingBlockGasLimitContract: false,
inner,
LimboLogs.Instance);
return (calculator, inner);
}

private static IBlockGasLimitContract StubContract(long gasLimit)
{
IBlockGasLimitContract contract = Substitute.For<IBlockGasLimitContract>();
contract.ActivationBlock.Returns(0L);
contract.BlockGasLimit(Arg.Any<BlockHeader>()).Returns((UInt256)gasLimit);
return contract;
}

[Test]
public async Task skip_validate_gas_limit_before_enabled()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ namespace Nethermind.Consensus.AuRa
{
public class AuRaContractGasLimitOverride(
IList<IBlockGasLimitContract> contracts,
AuRaContractGasLimitOverride.Cache cache,
AuRaContractGasLimitOverride.Cache cache,
bool minimum2MlnGasPerBlockWhenUsingBlockGasLimitContract,
IGasLimitCalculator innerCalculator,
ILogManager logManager) : IGasLimitCalculator
Expand All @@ -37,7 +37,10 @@ private static UInt256 MinimalContractGasLimit
private readonly IGasLimitCalculator _innerCalculator = innerCalculator ?? throw new ArgumentNullException(nameof(innerCalculator));
private readonly ILogger _logger = logManager?.GetClassLogger<AuRaContractGasLimitOverride>() ?? throw new ArgumentNullException(nameof(logManager));

public long GetGasLimit(BlockHeader parentHeader) => GetGasLimitFromContract(parentHeader) ?? _innerCalculator.GetGasLimit(parentHeader);
public long GetGasLimit(BlockHeader parentHeader, long? targetGasLimit = null) =>
targetGasLimit is not null
? _innerCalculator.GetGasLimit(parentHeader, targetGasLimit)
: GetGasLimitFromContract(parentHeader) ?? _innerCalculator.GetGasLimit(parentHeader);

private long? GetGasLimitFromContract(BlockHeader parentHeader)
{
Expand Down Expand Up @@ -95,7 +98,9 @@ public class Cache
public bool IsGasLimitValid(BlockHeader parentHeader, in long gasLimit, out long? expectedGasLimit)
{
expectedGasLimit = GetGasLimitFromContract(parentHeader);
return expectedGasLimit is null || expectedGasLimit == gasLimit;
return expectedGasLimit is null
|| expectedGasLimit == gasLimit
|| _innerCalculator.GetGasLimit(parentHeader, gasLimit) == gasLimit;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ namespace Nethermind.Consensus.Test
public class ManualGasLimitCalculator : IGasLimitCalculator
{
public long GasLimit { get; set; }
public long GetGasLimit(BlockHeader parentHeader) => GasLimit;
public long GetGasLimit(BlockHeader parentHeader, long? targetGasLimit = null) => GasLimit;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,48 +16,60 @@ namespace Nethermind.Consensus.Test
{
public class TargetAdjustedGasLimitCalculatorTests
{
private const long DefaultBlockNumber = 20_000_000;

private static long Calc(
IReleaseSpec spec,
long parentGasLimit,
long? configTarget = null,
long? overrideTarget = null,
long blockNumber = DefaultBlockNumber)
{
TestSpecProvider specProvider = new(spec);
TargetAdjustedGasLimitCalculator calculator = new(
specProvider,
new BlocksConfig { TargetBlockGasLimit = configTarget });
BlockHeader header = Build.A.BlockHeader.WithNumber(blockNumber - 1).WithGasLimit(parentGasLimit).TestObject;
return calculator.GetGasLimit(header, overrideTarget);
}

[Test]
public void Is_bump_on_1559_eip_block()
{
int londonBlock = 5;
long gasLimit = 1000000000000000000;
OverridableReleaseSpec spec = new(London.Instance)
{
Eip1559TransitionBlock = londonBlock
};
TestSpecProvider specProvider = new(spec);
TargetAdjustedGasLimitCalculator targetedAdjustedGasLimitCalculator = new(specProvider, new BlocksConfig());
BlockHeader header = Build.A.BlockHeader.WithNumber(londonBlock - 1).WithGasLimit(gasLimit).TestObject;
long actualValue = targetedAdjustedGasLimitCalculator.GetGasLimit(header);
Assert.That(actualValue, Is.EqualTo(gasLimit * Eip1559Constants.DefaultElasticityMultiplier));
const int londonBlock = 5;
const long parentGasLimit = 1000000000000000000;
OverridableReleaseSpec spec = new(London.Instance) { Eip1559TransitionBlock = londonBlock };

Calc(spec, parentGasLimit, blockNumber: londonBlock)
.Should().Be(parentGasLimit * Eip1559Constants.DefaultElasticityMultiplier);
}

[TestCase(30_000_000, 100_000_000, 30029295)]
public void Is_calculating_correct_gasLimit(long currentGasLimit, long targetGasLimit, long expectedGasLimit)
public void Is_calculating_correct_gasLimit(long parentGasLimit, long configTarget, long expected)
=> Calc(Prague.Instance, parentGasLimit, configTarget: configTarget).Should().Be(expected);

[TestCase(30_000_000, 20_000_000, 50_000_000, 30029295)]
[TestCase(30_000_000, 100_000_000, 20_000_000, 29970705)]
public void Override_takes_precedence_over_config_target(long parentGasLimit, long configTarget, long overrideTarget, long expected)
=> Calc(Prague.Instance, parentGasLimit, configTarget, overrideTarget).Should().Be(expected);

[Test]
public void Null_override_falls_back_to_config_target()
{
int blockNumber = 20_000_000;
long gasLimit = currentGasLimit;
TestSpecProvider specProvider = new(Prague.Instance);
TargetAdjustedGasLimitCalculator targetedAdjustedGasLimitCalculator = new(specProvider,
new BlocksConfig()
{
TargetBlockGasLimit = targetGasLimit
});
BlockHeader header = Build.A.BlockHeader.WithNumber(blockNumber - 1).WithGasLimit(gasLimit).TestObject;
long actualValue = targetedAdjustedGasLimitCalculator.GetGasLimit(header);
actualValue.Should().Be(expectedGasLimit);
const long parentGasLimit = 30_000_000;
const long configTarget = 100_000_000;

Calc(Prague.Instance, parentGasLimit, configTarget, overrideTarget: null)
.Should().Be(Calc(Prague.Instance, parentGasLimit, configTarget));
}

[Test]
public void DoesNot_go_below_minimum()
{
int londonBlock = 5;
long gasLimit = 5000;
TestSpecProvider specProvider = new(London.Instance);
TargetAdjustedGasLimitCalculator targetedAdjustedGasLimitCalculator = new(specProvider, new BlocksConfig() { TargetBlockGasLimit = 1 });
BlockHeader header = Build.A.BlockHeader.WithNumber(londonBlock - 1).WithGasLimit(gasLimit).TestObject;
long actualValue = targetedAdjustedGasLimitCalculator.GetGasLimit(header);
Assert.That(actualValue, Is.EqualTo(specProvider.GetSpec(new ForkActivation(5)).MinGasLimit));
const int londonBlock = 5;

Calc(London.Instance, parentGasLimit: 5000, configTarget: 1, blockNumber: londonBlock)
.Should().Be(London.Instance.MinGasLimit);
}
}
}
2 changes: 1 addition & 1 deletion src/Nethermind/Nethermind.Consensus/FollowOtherMiners.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ public class FollowOtherMiners(ISpecProvider specProvider) : IGasLimitCalculator
{
private readonly ISpecProvider _specProvider = specProvider;

public long GetGasLimit(BlockHeader parentHeader)
public long GetGasLimit(BlockHeader parentHeader, long? targetGasLimit = null)
{
long gasLimit = parentHeader.GasLimit;
long newBlockNumber = parentHeader.Number + 1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ namespace Nethermind.Consensus
{
public interface IGasLimitCalculator
{
long GetGasLimit(BlockHeader parentHeader);
long GetGasLimit(BlockHeader parentHeader, long? targetGasLimit = null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ protected virtual BlockHeader PrepareBlockHeader(BlockHeader parent,
blockAuthor,
UInt256.Zero,
parent.Number + 1,
payloadAttributes?.GetGasLimit() ?? GasLimitCalculator.GetGasLimit(parent),
payloadAttributes?.GetGasLimit(parent, GasLimitCalculator) ?? GasLimitCalculator.GetGasLimit(parent),
timestamp,
_blocksConfig.GetExtraDataBytes())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ public class PayloadAttributes

public ulong? SlotNumber { get; set; }

public virtual long? GetGasLimit() => null;
public long? TargetGasLimit { get; set; }

public virtual long GetGasLimit(BlockHeader parent, IGasLimitCalculator gasLimitCalculator)
=> gasLimitCalculator.GetGasLimit(parent, TargetGasLimit);

public override string ToString() => ToString(string.Empty);

Expand All @@ -54,6 +57,11 @@ public string ToString(string indentation)
sb.Append($", {nameof(SlotNumber)}: {SlotNumber}");
}

if (TargetGasLimit is not null)
{
sb.Append($", {nameof(TargetGasLimit)}: {TargetGasLimit}");
}

sb.Append('}');

return sb.ToString();
Expand Down Expand Up @@ -83,7 +91,8 @@ protected virtual int ComputePayloadIdMembersSize() =>
+ Address.Size // suggested fee recipient
+ (Withdrawals is null ? 0 : Keccak.Size) // withdrawals root hash
+ (ParentBeaconBlockRoot is null ? 0 : Keccak.Size) // parent beacon block root
+ (SlotNumber is null ? 0 : sizeof(ulong)); // slot number
+ (SlotNumber is null ? 0 : sizeof(ulong)) // slot number
+ (TargetGasLimit is null ? 0 : sizeof(ulong)); // target gas limit

protected static string ComputePayloadId(Span<byte> inputSpan)
{
Expand Down Expand Up @@ -128,6 +137,12 @@ protected virtual int WritePayloadIdMembers(BlockHeader parentHeader, Span<byte>
position += sizeof(ulong);
}

if (TargetGasLimit is not null)
{
BinaryPrimitives.WriteInt64BigEndian(inputSpan.Slice(position, sizeof(long)), TargetGasLimit.Value);
position += sizeof(long);
}

return position;
}

Expand Down Expand Up @@ -214,6 +229,7 @@ public virtual PayloadAttributesValidationResult Validate(
>= PayloadAttributesVersions.V2 when Withdrawals is null => $"{nameof(Withdrawals)} must be provided",
>= PayloadAttributesVersions.V3 when ParentBeaconBlockRoot is null => $"{nameof(ParentBeaconBlockRoot)} must be provided",
>= PayloadAttributesVersions.V4 when SlotNumber is null => $"{nameof(SlotNumber)} must be provided",
>= PayloadAttributesVersions.V4 when TargetGasLimit is null => $"{nameof(TargetGasLimit)} must be provided",
_ => null
};
}
Expand All @@ -226,7 +242,7 @@ public static class PayloadAttributesExtensions
public static int GetVersion(this PayloadAttributes executionPayload) =>
executionPayload switch
{
{ SlotNumber: not null } => PayloadAttributesVersions.V4,
{ SlotNumber: not null } or { TargetGasLimit: not null } => PayloadAttributesVersions.V4,
{ ParentBeaconBlockRoot: not null } => PayloadAttributesVersions.V3,
{ Withdrawals: not null } => PayloadAttributesVersions.V2,
_ => PayloadAttributesVersions.V1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ public class TargetAdjustedGasLimitCalculator(ISpecProvider? specProvider, IBloc
private readonly ISpecProvider _specProvider = specProvider ?? throw new ArgumentNullException(nameof(specProvider));
private readonly IBlocksConfig _blocksConfig = blocksConfig ?? throw new ArgumentNullException(nameof(blocksConfig));

public long GetGasLimit(BlockHeader parentHeader)
public long GetGasLimit(BlockHeader parentHeader, long? targetGasLimit = null)
{
long parentGasLimit = parentHeader.GasLimit;
long gasLimit = parentGasLimit;

long? targetGasLimit = _blocksConfig.TargetBlockGasLimit;
targetGasLimit ??= _blocksConfig.TargetBlockGasLimit;
long newBlockNumber = parentHeader.Number + 1;
IReleaseSpec spec = _specProvider.GetSpec(newBlockNumber, parentHeader.Timestamp); // taking the parent timestamp is a temporary solution
if (targetGasLimit is not null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public override Task processing_block_should_serialize_valid_responses(string bl
"0xec6f5611ce3652fefd669e8d7e6d63bd8cdefdcdfe9a0a44eb61355084831da4",
"0xf382f220de54b57ac9355d4eeb114f9e6bc4d25e307cdac0347b43d5534ac68e",
"0xb8a1a0780980ab4e20a46237a3c533af8cd0386cf4c74d05c8ec5e9bf5cbc482",
"0x2802e8a8c34cd1ea",
"0xb6ea0a60ac692530",
_auraWithdrawalContractAddress)]
public override async Task Should_process_block_as_expected_V6(string latestValidHash, string blockHash, string stateRoot, string payloadId, string? customWithdrawalContractAddress)
=> await base.Should_process_block_as_expected_V6(latestValidHash, blockHash, stateRoot, payloadId, customWithdrawalContractAddress);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1927,6 +1927,7 @@ static PayloadAttributes Attrs(
Withdrawal[]? withdrawals = null,
Hash256? parentBeaconBlockRoot = null,
ulong? slotNumber = null,
long? targetGasLimit = null,
Action<PayloadAttributes>? mutate = null)
{
PayloadAttributes attrs = new()
Expand All @@ -1937,6 +1938,7 @@ static PayloadAttributes Attrs(
Withdrawals = withdrawals,
ParentBeaconBlockRoot = parentBeaconBlockRoot,
SlotNumber = slotNumber,
TargetGasLimit = targetGasLimit,
};
mutate?.Invoke(attrs);
return attrs;
Expand All @@ -1954,8 +1956,10 @@ static TestCaseData InvalidFieldCase(IReleaseSpec spec, string method, PayloadAt
yield return InvalidFieldCase(Paris.Instance, nameof(IEngineRpcModule.engine_forkchoiceUpdatedV1), Attrs(mutate: a => a.SuggestedFeeRecipient = null), "FCUv1 SuggestedFeeRecipient null");

yield return InvalidFieldCase(Cancun.Instance, nameof(IEngineRpcModule.engine_forkchoiceUpdatedV3), Attrs(parentBeaconBlockRoot: Keccak.Zero), "FCUv3 Withdrawals null");
yield return InvalidFieldCase(Amsterdam.Instance, nameof(IEngineRpcModule.engine_forkchoiceUpdatedV4), Attrs(parentBeaconBlockRoot: Keccak.Zero, slotNumber: 1), "FCUv4 Withdrawals null");
yield return InvalidFieldCase(Amsterdam.Instance, nameof(IEngineRpcModule.engine_forkchoiceUpdatedV4), Attrs(withdrawals: [], slotNumber: 1), "FCUv4 ParentBeaconBlockRoot null");
yield return InvalidFieldCase(Amsterdam.Instance, nameof(IEngineRpcModule.engine_forkchoiceUpdatedV4), Attrs(parentBeaconBlockRoot: Keccak.Zero, slotNumber: 1, targetGasLimit: 30_000_000), "FCUv4 Withdrawals null");
yield return InvalidFieldCase(Amsterdam.Instance, nameof(IEngineRpcModule.engine_forkchoiceUpdatedV4), Attrs(withdrawals: [], slotNumber: 1, targetGasLimit: 30_000_000), "FCUv4 ParentBeaconBlockRoot null");
yield return InvalidFieldCase(Amsterdam.Instance, nameof(IEngineRpcModule.engine_forkchoiceUpdatedV4), Attrs(withdrawals: [], parentBeaconBlockRoot: Keccak.Zero, targetGasLimit: 30_000_000), "FCUv4 SlotNumber null");
yield return InvalidFieldCase(Amsterdam.Instance, nameof(IEngineRpcModule.engine_forkchoiceUpdatedV4), Attrs(withdrawals: [], parentBeaconBlockRoot: Keccak.Zero, slotNumber: 1), "FCUv4 TargetGasLimit null");
}
}

Expand Down
Loading
Loading