Skip to content

Commit

Permalink
Add blob tx gas calculations
Browse files Browse the repository at this point in the history
  • Loading branch information
flcl42 committed Apr 20, 2023
1 parent 69f61e4 commit 5c907d8
Show file tree
Hide file tree
Showing 16 changed files with 274 additions and 58 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,13 @@ bool IBlockProducer.IsProducingBlocks(ulong? maxProducingInterval)
);
header.TxRoot = new TxTrie(block.Transactions).RootHash;
block.Header.Author = _sealer.Address;

if (spec.IsEip4844Enabled)
{
header.ExcessDataGas = Evm.IntrinsicGasCalculator.CalculateExcessDataGas(parentBlock.ExcessDataGas,
block.Transactions.Sum(x => x.BlobVersionedHashes?.Length ?? 0), spec);
header.ParentExcessDataGas = parentHeader.ExcessDataGas ?? 0;
}
return block;
}

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

using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Nethermind.Blockchain;
using Nethermind.Blockchain.Receipts;
Expand Down Expand Up @@ -240,6 +241,12 @@ protected virtual TxReceipt[] ProcessBlock(
block.Header.StateRoot = _stateProvider.StateRoot;
block.Header.Hash = block.Header.CalculateHash();

if (spec.IsEip4844Enabled)
{
block.Header.ExcessDataGas = Evm.IntrinsicGasCalculator.CalculateExcessDataGas(block.Header.ParentExcessDataGas,
block.GetTransactions().Sum(x => x.BlobVersionedHashes?.Length ?? 0), spec);
}

return receipts;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -559,11 +559,14 @@ private ProcessingBranch PrepareProcessingBranch(Block suggestedBlock, Processin
$"To be processed (of {suggestedBlock.ToString(Block.Format.Short)}) is {toBeProcessed?.ToString(Block.Format.Short)}");
if (toBeProcessed.IsGenesis)
{
toBeProcessed.Header.ParentExcessDataGas = 0;
break;
}

branchingPoint = _blockTree.FindParentHeader(toBeProcessed.Header,
BlockTreeLookupOptions.TotalDifficultyNotNeeded);
toBeProcessed.Header.ParentExcessDataGas = branchingPoint?.ExcessDataGas.GetValueOrDefault() ?? 0;

if (branchingPoint is null)
{
// genesis block
Expand Down Expand Up @@ -650,7 +653,7 @@ private ProcessingBranch PrepareProcessingBranch(Block suggestedBlock, Processin
Keccak stateRoot = branchingPoint?.StateRoot;
if (_logger.IsTrace) _logger.Trace($"State root lookup: {stateRoot}");
blocksToBeAddedToMain.Reverse();
return new ProcessingBranch(stateRoot, blocksToBeAddedToMain);
return new ProcessingBranch(stateRoot, blocksToBeAddedToMain, branchingPoint);
}

[Todo(Improve.Refactor, "This probably can be made conditional (in DEBUG only)")]
Expand Down Expand Up @@ -707,16 +710,18 @@ public void Dispose()
[DebuggerDisplay("Root: {Root}, Length: {BlocksToProcess.Count}")]
private readonly struct ProcessingBranch
{
public ProcessingBranch(Keccak root, List<Block> blocks)
public ProcessingBranch(Keccak root, List<Block> blocks, BlockHeader parentHeader)
{
Root = root;
Blocks = blocks;
BlocksToProcess = new List<Block>();
ParentHeader = parentHeader;
}

public Keccak Root { get; }
public List<Block> Blocks { get; }
public List<Block> BlocksToProcess { get; }
public BlockHeader ParentHeader { get; }
}

public class Options
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -14,6 +15,7 @@
using Nethermind.Core.Crypto;
using Nethermind.Core.Extensions;
using Nethermind.Core.Specs;
using Nethermind.Evm;
using Nethermind.Evm.Tracing;
using Nethermind.Int256;
using Nethermind.Logging;
Expand Down Expand Up @@ -44,7 +46,7 @@ public abstract class BlockProducerBase : IBlockProducer
private IStateProvider StateProvider { get; }
private readonly IGasLimitCalculator _gasLimitCalculator;
private readonly IDifficultyCalculator _difficultyCalculator;
private readonly ISpecProvider _specProvider;
protected readonly ISpecProvider _specProvider;
private readonly ITxSource _txSource;
private readonly IBlockProductionTrigger _trigger;
private bool _isRunning;
Expand Down Expand Up @@ -292,7 +294,8 @@ protected virtual BlockHeader PrepareBlockHeader(BlockHeader parent,
_blocksConfig.GetExtraDataBytes())
{
Author = blockAuthor,
MixHash = payloadAttributes?.PrevRandao
MixHash = payloadAttributes?.PrevRandao,
ParentExcessDataGas = parent.ExcessDataGas.GetValueOrDefault(),
};

UInt256 difficulty = _difficultyCalculator.Calculate(header, parent);
Expand All @@ -310,10 +313,10 @@ protected virtual Block PrepareBlock(BlockHeader parent, PayloadAttributes? payl

IEnumerable<Transaction> transactions = GetTransactions(parent);

if (_specProvider.GetSpec(header).IsEip4844Enabled)
IReleaseSpec releaseSpec = _specProvider.GetSpec(header.Number, header.Timestamp);
if (releaseSpec.IsEip4844Enabled)
{
// TODO: Calculate ExcessDataGas depending on parent ExcessDataGas and number of blobs in txs
header.ExcessDataGas = 0;
header.ParentExcessDataGas = parent.ExcessDataGas ?? 0;
}

return new BlockToProduce(header, transactions, Array.Empty<BlockHeader>(), payloadAttributes?.Withdrawals);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ public static void AddBranch(this BlockTree blockTree, int branchLength, int spl
block.Header.ParentHash = parent?.Hash;
block.Header.StateRoot = parent?.StateRoot;
block.Header.Hash = block.Header.CalculateHash();


if (spec.IsEip4844Enabled)
{
block.Header.ExcessDataGas = Evm.IntrinsicGasCalculator.CalculateExcessDataGas(block.Header.ParentExcessDataGas,
block.GetTransactions().Sum(x => x.BlobVersionedHashes?.Length ?? 0), spec);
}
parent = block;
bool shouldProcess = i == branchLength - 1;
BlockTreeSuggestOptions options = BlockTreeSuggestOptions.ForceDontSetAsMain;
Expand Down
3 changes: 3 additions & 0 deletions src/Nethermind/Nethermind.Core/BlockHeader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ public BlockHeader(
public Keccak? WithdrawalsRoot { get; set; }
public UInt256? ExcessDataGas { get; set; }

// TODO: Used as a check, remove in the future
public UInt256 ParentExcessDataGas { get; set; } = UInt256.MaxValue;

public bool HasBody => (TxRoot is not null && TxRoot != Keccak.EmptyTreeHash)
|| (UnclesHash is not null && UnclesHash != Keccak.OfAnEmptySequenceRlp)
|| (WithdrawalsRoot is not null && WithdrawalsRoot != Keccak.EmptyTreeHash);
Expand Down
76 changes: 74 additions & 2 deletions src/Nethermind/Nethermind.Evm.Test/IntrinsicGasCalculatorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// SPDX-License-Identifier: LGPL-3.0-only

using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using FluentAssertions;
using Nethermind.Core;
Expand Down Expand Up @@ -41,7 +40,6 @@ public class IntrinsicGasCalculatorTests
yield return (new byte[] { 1, 1, 0 }, 140, 36);
yield return (new byte[] { 0, 0, 1, 1 }, 144, 40);
}

[TestCaseSource(nameof(TestCaseSource))]
public void Intrinsic_cost_is_calculated_properly((Transaction Tx, long Cost, string Description) testCase)
{
Expand Down Expand Up @@ -113,6 +111,80 @@ void Test(IReleaseSpec spec, bool isAfterRepricing)
Test(Istanbul.Instance, true);
Test(MuirGlacier.Instance, true);
Test(Berlin.Instance, true);
Test(GrayGlacier.Instance, true);
Test(Shanghai.Instance, true);
Test(Cancun.Instance, true);
}

public static IEnumerable<(UInt256 parentExcessDataGas, int newBlobsCount, UInt256 expectedCost)> ExcessDataGasTestCaseSource()
{
yield return (0, 0, 0);
yield return (0, 1, 0);
yield return (0, 2, 0);
yield return (0, 3, (1 << 17));
yield return (100000, 3, (1 << 17) + 100000);
yield return ((1 << 18), 1, (1 << 17));
yield return ((1 << 18), 0, 0);
yield return ((1 << 18), 2, (1 << 18));
}

[TestCaseSource(nameof(ExcessDataGasTestCaseSource))]
public void Blobs_excess_data_gas_is_calculated_correctly((UInt256 parentExcessDataGas, int newBlobsCount, UInt256 expectedCost) testCase)
{
void Test(IReleaseSpec spec, bool areBlobsEnabled)
{
IntrinsicGasCalculator.CalculateExcessDataGas(testCase.parentExcessDataGas, testCase.newBlobsCount, spec).Should()
.Be(areBlobsEnabled ? testCase.expectedCost : 0);
}

Test(Homestead.Instance, false);
Test(Frontier.Instance, false);
Test(SpuriousDragon.Instance, false);
Test(TangerineWhistle.Instance, false);
Test(Byzantium.Instance, false);
Test(Constantinople.Instance, false);
Test(ConstantinopleFix.Instance, false);
Test(Istanbul.Instance, false);
Test(MuirGlacier.Instance, false);
Test(Berlin.Instance, false);
Test(GrayGlacier.Instance, false);
Test(Shanghai.Instance, false);
Test(Cancun.Instance, true);
}

public static IEnumerable<(Transaction tx, UInt256 parentExcessDataGas, UInt256 expectedCost)> BlobDataGostTestCaseSource()
{
yield return (Build.A.Transaction.TestObject, 0, 0);
yield return (Build.A.Transaction.TestObject, 1000, 0);
yield return (Build.A.Transaction.WithType(TxType.Blob).WithBlobVersionedHashes(0).TestObject, 1000, 0);
yield return (Build.A.Transaction.WithType(TxType.Blob).WithBlobVersionedHashes(1).TestObject, 0, 131072);
yield return (Build.A.Transaction.WithType(TxType.Blob).WithBlobVersionedHashes(1).TestObject, 10000000, 11665408);
yield return (Build.A.Transaction.WithType(TxType.Blob).WithBlobVersionedHashes(1000).TestObject, 0, 131072000);
yield return (Build.A.Transaction.WithType(TxType.Blob).WithBlobVersionedHashes(1000).TestObject, 10000000, 11665408000);
}

[TestCaseSource(nameof(BlobDataGostTestCaseSource))]
public void Blobs_intrinsic_cost_is_calculated_properly((Transaction tx, UInt256 parentExcessDataGas, UInt256 expectedCost) testCase)
{
void Test(IReleaseSpec spec, bool areBlobsEnabled)
{
IntrinsicGasCalculator.CalculateBlobsGasCost(testCase.tx, testCase.parentExcessDataGas, spec).Should()
.Be(areBlobsEnabled ? testCase.expectedCost : 0);
}

Test(Homestead.Instance, false);
Test(Frontier.Instance, false);
Test(SpuriousDragon.Instance, false);
Test(TangerineWhistle.Instance, false);
Test(Byzantium.Instance, false);
Test(Constantinople.Instance, false);
Test(ConstantinopleFix.Instance, false);
Test(Istanbul.Instance, false);
Test(MuirGlacier.Instance, false);
Test(Berlin.Instance, false);
Test(GrayGlacier.Instance, false);
Test(Shanghai.Instance, false);
Test(Cancun.Instance, true);
}
}
}
75 changes: 74 additions & 1 deletion src/Nethermind/Nethermind.Evm/IntrinsicGasCalculator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited
// SPDX-License-Identifier: LGPL-3.0-only

using System;
using System.IO;
using System.Linq;
using Nethermind.Core;
Expand All @@ -12,7 +13,7 @@ namespace Nethermind.Evm
{
public static class IntrinsicGasCalculator
{
public static long Calculate(Transaction transaction, IReleaseSpec releaseSpec)
public static long Calculate(Transaction transaction, IReleaseSpec releaseSpec/*, UInt256 parentExcessDataGas*/)
{
long result = GasCostOf.Transaction;
result += DataCost(transaction, releaseSpec);
Expand Down Expand Up @@ -91,5 +92,77 @@ private static long AccessListCost(Transaction transaction, IReleaseSpec release

return accessListCost;
}

/// <summary>
/// Blobs add additional cost to a transaction. The cost is calculated within separate gas market.
/// Parent block holds traces of high demand in <see cref="Block.ExcessDataGas"/> field, which value affects
/// the current block's blob data cost, raises it exponentially.
/// </summary>
/// <param name="transaction">Blob transaction</param>
/// <param name="parentExcessDataGas"><see cref="Block.ExcessDataGas"/> of the parent block</param>
/// <param name="releaseSpec">The current fork's specification</param>
/// <returns>Gas cost added by blobs</returns>
/// <exception cref="InvalidOperationException">Raised in case of inproperly set parentExcessDataGas</exception>
public static UInt256 CalculateBlobsGasCost(Transaction transaction, UInt256 parentExcessDataGas, IReleaseSpec releaseSpec)
{
if (!releaseSpec.IsEip4844Enabled || transaction.Type != TxType.Blob || transaction.BlobVersionedHashes?.Any() != true)
{
return 0;
}
if(parentExcessDataGas == UInt256.MaxValue)
{
throw new InvalidOperationException();
}

return (ulong)transaction.BlobVersionedHashes!.Length * CostPerBlob(parentExcessDataGas);
}

private static UInt256 CostPerBlob(UInt256 parentExcessDataGas)
{
UInt256 DataGasPriceUpdateFraction = 2225652;
UInt256 MinDataGasPrice = 1L;

UInt256 FakeExponential(UInt256 factor, UInt256 num, UInt256 denominator)
{
UInt256 output = UInt256.Zero;

UInt256 numAccum = factor * denominator;

for (UInt256 i = 1; numAccum > 0; i++)
{
output += numAccum;
numAccum *= num;
numAccum /= i * denominator;
}
return output / denominator;
}

UInt256 scaleDueToParentExcessDataGas = FakeExponential(MinDataGasPrice, parentExcessDataGas, DataGasPriceUpdateFraction);
return scaleDueToParentExcessDataGas * 1<<17;
}

/// <summary>
/// Calculates the current block's ExcessDataGas, that regulates blob gas market price.
/// </summary>
/// <param name="parentExcessDataGas">Parent block's ExcessDataGas</param>
/// <param name="newBlobsCount">Current block's blob count</param>
/// <param name="releaseSpec"></param>
/// <returns>ExcessDataGas</returns>
public static UInt256? CalculateExcessDataGas(UInt256? parentExcessDataGas, int newBlobsCount,
IReleaseSpec releaseSpec)
{
if (!releaseSpec.IsEip4844Enabled)
{
return null;
}

UInt256 DataGasPerBlob = 1 << 17;
UInt256 TargetDataGasPerBlock = 1 << 18;

UInt256 excessDataGas = parentExcessDataGas.GetValueOrDefault();
UInt256 consumedGas = DataGasPerBlob * (UInt256)newBlobsCount;
excessDataGas += consumedGas;
return excessDataGas < TargetDataGasPerBlock ? UInt256.Zero : (excessDataGas - TargetDataGasPerBlock);
}
}
}
Loading

0 comments on commit 5c907d8

Please sign in to comment.