Skip to content
Closed
Show file tree
Hide file tree
Changes from 8 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 @@ -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