diff --git a/src/Nethermind/Nethermind.Blockchain.Test/Validators/TxValidatorTests.cs b/src/Nethermind/Nethermind.Blockchain.Test/Validators/TxValidatorTests.cs index a40243a73bb..8e91f13773b 100644 --- a/src/Nethermind/Nethermind.Blockchain.Test/Validators/TxValidatorTests.cs +++ b/src/Nethermind/Nethermind.Blockchain.Test/Validators/TxValidatorTests.cs @@ -171,7 +171,6 @@ public bool Before_eip_2930_has_to_be_legacy_tx(TxType txType, bool eip2930) [TestCase(TxType.AccessList, true, false, ExpectedResult = true)] [TestCase(TxType.EIP1559, true, false, ExpectedResult = false)] [TestCase(TxType.EIP1559, true, true, ExpectedResult = true)] - [TestCase((TxType)100, true, false, ExpectedResult = false)] public bool Before_eip_1559_has_to_be_legacy_or_access_list_tx(TxType txType, bool eip2930, bool eip1559) { byte[] sigData = new byte[65]; @@ -184,7 +183,7 @@ public bool Before_eip_1559_has_to_be_legacy_or_access_list_tx(TxType txType, bo .WithChainId(TestBlockchainIds.ChainId) .WithMaxPriorityFeePerGas(txType == TxType.EIP1559 ? 10.GWei() : 5.GWei()) .WithMaxFeePerGas(txType == TxType.EIP1559 ? 10.GWei() : 5.GWei()) - .WithAccessList(txType == TxType.AccessList || txType == TxType.EIP1559 + .WithAccessList(txType is TxType.AccessList or TxType.EIP1559 ? AccessList.Empty : null) .WithSignature(signature).TestObject; diff --git a/src/Nethermind/Nethermind.Core.Test/Encoding/ShardBlobTxDecoderTests.cs b/src/Nethermind/Nethermind.Core.Test/Encoding/ShardBlobTxDecoderTests.cs index 8cbd5d3ccb7..bf70a7b46f9 100644 --- a/src/Nethermind/Nethermind.Core.Test/Encoding/ShardBlobTxDecoderTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Encoding/ShardBlobTxDecoderTests.cs @@ -18,7 +18,7 @@ namespace Nethermind.Core.Test.Encoding; [TestFixture] public partial class ShardBlobTxDecoderTests { - private readonly TxDecoder _txDecoder = new(); + private readonly TxDecoder _txDecoder = TxDecoder.Instance; [SetUp] public static Task SetUp() => KzgPolynomialCommitments.InitializeAsync(); diff --git a/src/Nethermind/Nethermind.Core.Test/Encoding/TxDecoderTests.cs b/src/Nethermind/Nethermind.Core.Test/Encoding/TxDecoderTests.cs index 76dcc48a671..2bf4116bab7 100644 --- a/src/Nethermind/Nethermind.Core.Test/Encoding/TxDecoderTests.cs +++ b/src/Nethermind/Nethermind.Core.Test/Encoding/TxDecoderTests.cs @@ -22,7 +22,7 @@ namespace Nethermind.Core.Test.Encoding [TestFixture] public class TxDecoderTests { - private readonly TxDecoder _txDecoder = new(); + private readonly TxDecoder _txDecoder = TxDecoder.Instance; public static IEnumerable<(TransactionBuilder, string)> TestObjectsSource() { @@ -72,12 +72,11 @@ public void CanCorrectlyCalculateTxHash_when_called_concurrently((Transaction Tx { Transaction tx = testCase.Tx; - TxDecoder decoder = new TxDecoder(); - Rlp rlp = decoder.Encode(tx); + Rlp rlp = _txDecoder.Encode(tx); Hash256 expectedHash = Keccak.Compute(rlp.Bytes); - Transaction decodedTx = decoder.Decode(new RlpStream(rlp.Bytes))!; + Transaction decodedTx = _txDecoder.Decode(new RlpStream(rlp.Bytes))!; decodedTx.SetPreHash(rlp.Bytes); diff --git a/src/Nethermind/Nethermind.Core/Transaction.cs b/src/Nethermind/Nethermind.Core/Transaction.cs index 1371eba41de..ec162f85c88 100644 --- a/src/Nethermind/Nethermind.Core/Transaction.cs +++ b/src/Nethermind/Nethermind.Core/Transaction.cs @@ -19,6 +19,7 @@ namespace Nethermind.Core [DebuggerDisplay("{Hash}, Value: {Value}, To: {To}, Gas: {GasLimit}")] public class Transaction { + public const byte MaxTxType = 0x7F; public const int BaseTxGasCost = 21000; public ulong? ChainId { get; set; } diff --git a/src/Nethermind/Nethermind.Core/TxType.cs b/src/Nethermind/Nethermind.Core/TxType.cs index 9257754dab6..b642ca78121 100644 --- a/src/Nethermind/Nethermind.Core/TxType.cs +++ b/src/Nethermind/Nethermind.Core/TxType.cs @@ -9,7 +9,6 @@ public enum TxType : byte AccessList = 1, EIP1559 = 2, Blob = 3, - - DepositTx = 0x7E, + DepositTx = 0x7E } } diff --git a/src/Nethermind/Nethermind.Crypto/TransactionExtensions.cs b/src/Nethermind/Nethermind.Crypto/TransactionExtensions.cs index ac04b15ad99..9dd09eebc27 100644 --- a/src/Nethermind/Nethermind.Crypto/TransactionExtensions.cs +++ b/src/Nethermind/Nethermind.Crypto/TransactionExtensions.cs @@ -9,7 +9,7 @@ namespace Nethermind.Crypto { public static class TransactionExtensions { - private static readonly TxDecoder _txDecoder = new(); + private static readonly TxDecoder _txDecoder = TxDecoder.Instance; public static Hash256 CalculateHash(this Transaction transaction) { diff --git a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs index 3ee10c8bcb3..f7747d6436f 100644 --- a/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs +++ b/src/Nethermind/Nethermind.JsonRpc/Modules/Trace/TraceRpcModule.cs @@ -38,7 +38,7 @@ public class TraceRpcModule : ITraceRpcModule private readonly IReceiptFinder _receiptFinder; private readonly ITracer _tracer; private readonly IBlockFinder _blockFinder; - private readonly TxDecoder _txDecoder = new(); + private readonly TxDecoder _txDecoder = TxDecoder.Instance; private readonly IJsonRpcConfig _jsonRpcConfig; private readonly TimeSpan _cancellationTokenTimeout; private readonly IStateReader _stateReader; diff --git a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs index 7bd893b0865..bc04725f1d4 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin.Test/EngineModuleTests.V3.cs @@ -146,9 +146,8 @@ public virtual async Task NewPayloadV3_should_decline_mempool_encoding(bool inMe ExecutionPayloadV3 payload = (await rpcModule.engine_getPayloadV3(Bytes.FromHexString(payloadId!))).Data!.ExecutionPayload; - TxDecoder rlpEncoder = new(); RlpBehaviors rlpBehaviors = (inMempoolForm ? RlpBehaviors.InMempoolForm : RlpBehaviors.None) | RlpBehaviors.SkipTypedWrapping; - payload.Transactions = transactions.Select(tx => rlpEncoder.Encode(tx, rlpBehaviors).Bytes).ToArray(); + payload.Transactions = transactions.Select(tx => TxDecoder.Instance.Encode(tx, rlpBehaviors).Bytes).ToArray(); byte[]?[] blobVersionedHashes = transactions.SelectMany(tx => tx.BlobVersionedHashes ?? Array.Empty()).ToArray(); ResultWrapper result = await rpcModule.engine_newPayloadV3(payload, blobVersionedHashes, payload.ParentBeaconBlockRoot); @@ -217,6 +216,26 @@ public async Task NewPayloadV3_WrongStateRoot_CorrectErrorIsReturnedAfterProcess Assert.That(result.Data.ValidationError, Does.StartWith("InvalidStateRoot")); } + [Test] + public async Task NewPayloadV3_UnsupportedTxType_BlockIsRejectedWithCorrectErrorMessage() + { + (IEngineRpcModule prevRpcModule, string? payloadId, Transaction[] transactions, _) = await BuildAndGetPayloadV3Result(Cancun.Instance, 1); + ExecutionPayloadV3 payload = (await prevRpcModule.engine_getPayloadV3(Bytes.FromHexString(payloadId!))).Data!.ExecutionPayload; + + payload.TryGetBlock(out Block? b); + byte[] txRlp = TxDecoder.Instance.EncodeTx(payload.GetTransactions()[0], RlpBehaviors.SkipTypedWrapping).Bytes; + txRlp[0] = 100; // set TxType to 100 + payload.Transactions = [txRlp]; + payload.BlockHash = b!.CalculateHash(); + + byte[]?[] blobVersionedHashes = transactions.SelectMany(tx => tx.BlobVersionedHashes ?? []).ToArray(); + ResultWrapper result = await prevRpcModule.engine_newPayloadV3(payload, blobVersionedHashes, payload.ParentBeaconBlockRoot); + + Assert.That(result.ErrorCode, Is.EqualTo(ErrorCodes.None)); + result.Data.Status.Should().Be("INVALID"); + Assert.That(result.Data.ValidationError, Does.StartWith("Transaction 0 is not valid")); + } + [Test] public async Task NewPayloadV3_should_decline_null_blobversionedhashes() { diff --git a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs index 3b3e8c41c1a..1f11ef675de 100644 --- a/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs +++ b/src/Nethermind/Nethermind.Merge.Plugin/Data/ExecutionPayload.cs @@ -123,8 +123,8 @@ public virtual bool TryGetBlock(out Block? block, UInt256? totalDifficulty = nul { try { - var transactions = GetTransactions(); - var header = new BlockHeader( + Transaction[] transactions = GetTransactions(); + BlockHeader header = new( ParentHash, Keccak.OfAnEmptySequenceRlp, FeeRecipient, diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandlerTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandlerTests.cs index 2e5693449ba..17c47f4c016 100644 --- a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandlerTests.cs +++ b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Eth/V68/Eth68ProtocolHandlerTests.cs @@ -279,7 +279,7 @@ private void HandleZeroMessage(T msg, byte messageCode) where T : MessageBase private void GenerateLists(int txCount, out ArrayPoolList types, out ArrayPoolList sizes, out ArrayPoolList hashes) { - TxDecoder txDecoder = new(); + TxDecoder txDecoder = TxDecoder.Instance; types = new(txCount); sizes = new(txCount); hashes = new(txCount); diff --git a/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/SyncPeerProtocolHandlerBase.cs b/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/SyncPeerProtocolHandlerBase.cs index a0ec107e7bc..b47db24a5ac 100644 --- a/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/SyncPeerProtocolHandlerBase.cs +++ b/src/Nethermind/Nethermind.Network/P2P/ProtocolHandlers/SyncPeerProtocolHandlerBase.cs @@ -87,7 +87,7 @@ protected SyncPeerProtocolHandlerBase(ISession session, SyncServer = syncServer ?? throw new ArgumentNullException(nameof(syncServer)); BackgroundTaskScheduler = new BackgroundTaskSchedulerWrapper(this, backgroundTaskScheduler ?? throw new ArgumentNullException(nameof(BackgroundTaskScheduler))); _timestamper = Timestamper.Default; - _txDecoder = new TxDecoder(); + _txDecoder = TxDecoder.Instance; _headersRequests = new MessageQueue>(Send); _bodiesRequests = new MessageQueue(Send); diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/BlockBodiesMessageSerializer.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/BlockBodiesMessageSerializer.cs index d4bba370b4f..3a240a83090 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/BlockBodiesMessageSerializer.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/BlockBodiesMessageSerializer.cs @@ -55,7 +55,7 @@ public BlockBodiesMessage Deserialize(IByteBuffer byteBuffer) private class BlockBodyDecoder : IRlpValueDecoder { - private readonly TxDecoder _txDecoder = new(); + private readonly TxDecoder _txDecoder = TxDecoder.Instance; private readonly HeaderDecoder _headerDecoder = new(); private readonly WithdrawalDecoder _withdrawalDecoderDecoder = new(); diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/TransactionsMessageSerializer.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/TransactionsMessageSerializer.cs index 4887018e90a..94366d508f7 100644 --- a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/TransactionsMessageSerializer.cs +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Eth/V62/Messages/TransactionsMessageSerializer.cs @@ -10,7 +10,7 @@ namespace Nethermind.Network.P2P.Subprotocols.Eth.V62.Messages { public class TransactionsMessageSerializer : IZeroInnerMessageSerializer { - private readonly TxDecoder _decoder = new(); + private readonly TxDecoder _decoder = TxDecoder.Instance; public void Serialize(IByteBuffer byteBuffer, TransactionsMessage message) { diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs index 71a5256baf1..f2b8c4a492e 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/BlockDecoder.cs @@ -11,7 +11,7 @@ namespace Nethermind.Serialization.Rlp public class BlockDecoder : IRlpValueDecoder, IRlpStreamDecoder { private readonly HeaderDecoder _headerDecoder = new(); - private readonly TxDecoder _txDecoder = new(); + private readonly TxDecoder _txDecoder = TxDecoder.Instance; private readonly WithdrawalDecoder _withdrawalDecoder = new(); public Block? Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Eip2930/AccessListDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Eip2930/AccessListDecoder.cs index 142326c3431..59c8d9807b4 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Eip2930/AccessListDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Eip2930/AccessListDecoder.cs @@ -13,6 +13,8 @@ public class AccessListDecoder : IRlpStreamDecoder, IRlpValueDecode { private const int IndexLength = 32; + public static readonly AccessListDecoder Instance = new(); + /// /// We pay a high code quality tax for the performance optimization on RLP. /// Adding more RLP decoders is costly (time wise) but the path taken saves a lot of allocations and GC. diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/AccessListTxDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/AccessListTxDecoder.cs new file mode 100644 index 00000000000..258f24d4e47 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/AccessListTxDecoder.cs @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; +using Nethermind.Serialization.Rlp.Eip2930; + +namespace Nethermind.Serialization.Rlp.TxDecoders; + +public class BaseAccessListTxDecoder(TxType txType, Func? transactionFactory = null) + : BaseTxDecoder(txType, transactionFactory) where T : Transaction, new() +{ + public override void Encode(Transaction transaction, RlpStream stream, RlpBehaviors rlpBehaviors = RlpBehaviors.None, + bool forSigning = false, bool isEip155Enabled = false, ulong chainId = 0) + { + int contentLength = GetContentLength(transaction, rlpBehaviors, forSigning); + int sequenceLength = Rlp.LengthOfSequence(contentLength); + + if ((rlpBehaviors & RlpBehaviors.SkipTypedWrapping) == 0) + { + stream.StartByteArray(sequenceLength + 1, false); + } + + stream.WriteByte((byte)Type); + EncodeTypedWrapped(transaction, stream, rlpBehaviors, forSigning, contentLength); + } + + protected virtual void EncodeTypedWrapped(Transaction transaction, RlpStream stream, RlpBehaviors rlpBehaviors, bool forSigning, int contentLength) + { + stream.StartSequence(contentLength); + EncodePayload(transaction, stream, rlpBehaviors); + EncodeSignature(transaction.Signature, stream, forSigning); + } + + public override int GetLength(Transaction transaction, RlpBehaviors rlpBehaviors, bool forSigning = false, bool isEip155Enabled = false, + ulong chainId = 0) + { + int txPayloadLength = base.GetLength(transaction, rlpBehaviors, forSigning, isEip155Enabled, chainId); + return rlpBehaviors.HasFlag(RlpBehaviors.SkipTypedWrapping) + ? 1 + txPayloadLength + : Rlp.LengthOfSequence(1 + txPayloadLength); + } + + protected override void DecodePayload(Transaction transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + transaction.ChainId = rlpStream.DecodeULong(); + base.DecodePayload(transaction, rlpStream, rlpBehaviors); + transaction.AccessList = AccessListDecoder.Instance.Decode(rlpStream, rlpBehaviors); + } + + protected override void DecodePayload(Transaction transaction, ref Rlp.ValueDecoderContext decoderContext, + RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + transaction.ChainId = decoderContext.DecodeULong(); + base.DecodePayload(transaction, ref decoderContext, rlpBehaviors); + transaction.AccessList = AccessListDecoder.Instance.Decode(ref decoderContext, rlpBehaviors); + } + + protected override void EncodePayload(Transaction transaction, RlpStream stream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + stream.Encode(transaction.ChainId ?? 0); + base.EncodePayload(transaction, stream, rlpBehaviors); + AccessListDecoder.Instance.Encode(stream, transaction.AccessList, rlpBehaviors); + } + + protected override int GetPayloadLength(Transaction transaction) + { + return base.GetPayloadLength(transaction) + + Rlp.LengthOf(transaction.ChainId ?? 0) + + AccessListDecoder.Instance.GetLength(transaction.AccessList, RlpBehaviors.None); + } +} + +public sealed class AccessListTxDecoder(Func? transactionFactory = null) : BaseAccessListTxDecoder(TxType.AccessList, transactionFactory) where T : Transaction, new(); + diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/BaseTxDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/BaseTxDecoder.cs new file mode 100644 index 00000000000..ab72eef75e7 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/BaseTxDecoder.cs @@ -0,0 +1,246 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; + +namespace Nethermind.Serialization.Rlp.TxDecoders; + +public abstract class BaseTxDecoder(TxType txType, Func? transactionFactory = null) + : ITxDecoder where T : Transaction, new() +{ + private const int MaxDelayedHashTxnSize = 32768; + private readonly Func _createTransaction = transactionFactory ?? (() => new T()); + + public TxType Type => txType; + + public virtual Transaction? Decode(Span transactionSequence, RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + T transaction = _createTransaction(); + transaction.Type = txType; + + int transactionLength = rlpStream.ReadSequenceLength(); + int lastCheck = rlpStream.Position + transactionLength; + + DecodePayload(transaction, rlpStream, rlpBehaviors); + + if (rlpStream.Position < lastCheck) + { + transaction.Signature = DecodeSignature(transaction, rlpStream, rlpBehaviors); + } + + if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) == 0) + { + rlpStream.Check(lastCheck); + } + + if ((rlpBehaviors & RlpBehaviors.ExcludeHashes) == 0) + { + CalculateHash(transaction, transactionSequence); + } + + return transaction; + } + + protected void CalculateHash(Transaction transaction, ReadOnlySpan transactionSequence) + { + if (transactionSequence.Length <= MaxDelayedHashTxnSize) + { + // Delay hash generation, as may be filtered as having too low gas etc + transaction.SetPreHashNoLock(transactionSequence); + } + else + { + // Just calculate the Hash as txn too large + transaction.Hash = Keccak.Compute(transactionSequence); + } + } + + public virtual void Decode(ref Transaction? transaction, int txSequenceStart, ReadOnlySpan transactionSequence, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + transaction ??= _createTransaction(); + transaction.Type = txType; + + int transactionLength = decoderContext.ReadSequenceLength(); + int lastCheck = decoderContext.Position + transactionLength; + + DecodePayload(transaction, ref decoderContext, rlpBehaviors); + + if (decoderContext.Position < lastCheck) + { + transaction.Signature = DecodeSignature(transaction, ref decoderContext, rlpBehaviors); + } + + if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) == 0) + { + decoderContext.Check(lastCheck); + } + + if ((rlpBehaviors & RlpBehaviors.ExcludeHashes) == 0) + { + CalculateHash(transaction, txSequenceStart, transactionSequence, ref decoderContext); + } + } + + protected void CalculateHash(Transaction transaction, int txSequenceStart, ReadOnlySpan transactionSequence, ref Rlp.ValueDecoderContext decoderContext) + { + if (transactionSequence.Length <= MaxDelayedHashTxnSize) + { + // Delay hash generation, as may be filtered as having too low gas etc + if (decoderContext.ShouldSliceMemory) + { + // Do not copy the memory in this case. + int currentPosition = decoderContext.Position; + decoderContext.Position = txSequenceStart; + transaction.SetPreHashMemoryNoLock(decoderContext.ReadMemory(transactionSequence.Length)); + decoderContext.Position = currentPosition; + } + else + { + transaction.SetPreHashNoLock(transactionSequence); + } + } + else + { + // Just calculate the Hash immediately as txn too large + transaction.Hash = Keccak.Compute(transactionSequence); + } + } + + public virtual void Encode(Transaction transaction, RlpStream stream, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool forSigning = false, bool isEip155Enabled = false, ulong chainId = 0) + { + int contentLength = GetContentLength(transaction, rlpBehaviors, forSigning, isEip155Enabled, chainId); + + stream.StartSequence(contentLength); + EncodePayload(transaction, stream); + EncodeSignature(transaction.Signature, stream, forSigning, isEip155Enabled, chainId); + } + + public virtual int GetLength(Transaction transaction, RlpBehaviors rlpBehaviors, bool forSigning = false, bool isEip155Enabled = false, ulong chainId = 0) + { + int txContentLength = GetContentLength(transaction, rlpBehaviors, forSigning, isEip155Enabled, chainId); + int txPayloadLength = Rlp.LengthOfSequence(txContentLength); + return txPayloadLength; + } + + protected virtual void DecodePayload(Transaction transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + transaction.Nonce = rlpStream.DecodeUInt256(); + DecodeGasPrice(transaction, rlpStream); + transaction.GasLimit = rlpStream.DecodeLong(); + transaction.To = rlpStream.DecodeAddress(); + transaction.Value = rlpStream.DecodeUInt256(); + transaction.Data = rlpStream.DecodeByteArray(); + } + + protected virtual void DecodeGasPrice(Transaction transaction, RlpStream rlpStream) + { + transaction.GasPrice = rlpStream.DecodeUInt256(); + } + + protected virtual void DecodePayload(Transaction transaction, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + transaction.Nonce = decoderContext.DecodeUInt256(); + DecodeGasPrice(transaction, ref decoderContext); + transaction.GasLimit = decoderContext.DecodeLong(); + transaction.To = decoderContext.DecodeAddress(); + transaction.Value = decoderContext.DecodeUInt256(); + transaction.Data = decoderContext.DecodeByteArrayMemory(); + } + + protected virtual void DecodeGasPrice(Transaction transaction, ref Rlp.ValueDecoderContext decoderContext) + { + transaction.GasPrice = decoderContext.DecodeUInt256(); + } + + private Signature? DecodeSignature(Transaction transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + ulong v = rlpStream.DecodeULong(); + ReadOnlySpan rBytes = rlpStream.DecodeByteArraySpan(); + ReadOnlySpan sBytes = rlpStream.DecodeByteArraySpan(); + return DecodeSignature(v, rBytes, sBytes, transaction.Signature, rlpBehaviors); + } + + private Signature? DecodeSignature(Transaction transaction, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + ulong v = decoderContext.DecodeULong(); + ReadOnlySpan rBytes = decoderContext.DecodeByteArraySpan(); + ReadOnlySpan sBytes = decoderContext.DecodeByteArraySpan(); + return DecodeSignature(v, rBytes, sBytes, transaction.Signature, rlpBehaviors); + } + + protected virtual Signature? DecodeSignature(ulong v, ReadOnlySpan rBytes, ReadOnlySpan sBytes, Signature? fallbackSignature = null, RlpBehaviors rlpBehaviors = RlpBehaviors.None) => + SignatureBuilder.FromBytes(v + Signature.VOffset, rBytes, sBytes, rlpBehaviors) ?? fallbackSignature; + + protected virtual void EncodePayload(Transaction transaction, RlpStream stream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + stream.Encode(transaction.Nonce); + EncodeGasPrice(transaction, stream); + stream.Encode(transaction.GasLimit); + stream.Encode(transaction.To); + stream.Encode(transaction.Value); + stream.Encode(transaction.Data); + } + + protected virtual void EncodeGasPrice(Transaction transaction, RlpStream stream) + { + stream.Encode(transaction.GasPrice); + } + + protected virtual int GetContentLength(Transaction transaction, RlpBehaviors rlpBehaviors, bool forSigning, bool isEip155Enabled = false, ulong chainId = 0) => + GetPayloadLength(transaction) + GetSignatureLength(transaction.Signature, forSigning, isEip155Enabled, chainId); + + protected virtual int GetPayloadLength(Transaction transaction) => + Rlp.LengthOf(transaction.Nonce) + + Rlp.LengthOf(transaction.GasPrice) + + Rlp.LengthOf(transaction.GasLimit) + + Rlp.LengthOf(transaction.To) + + Rlp.LengthOf(transaction.Value) + + Rlp.LengthOf(transaction.Data); + + protected virtual int GetSignatureLength(Signature? signature, bool forSigning, bool isEip155Enabled = false, ulong chainId = 0) + { + int contentLength = 0; + + if (!forSigning) + { + if (signature is null) + { + contentLength += 1; + contentLength += 1; + contentLength += 1; + } + else + { + contentLength += Rlp.LengthOf(GetSignatureFirstElement(signature)); + contentLength += Rlp.LengthOf(signature.RAsSpan.WithoutLeadingZeros()); + contentLength += Rlp.LengthOf(signature.SAsSpan.WithoutLeadingZeros()); + } + } + + return contentLength; + } + + protected virtual ulong GetSignatureFirstElement(Signature signature) => signature.RecoveryId; + + protected virtual void EncodeSignature(Signature? signature, RlpStream stream, bool forSigning, bool isEip155Enabled = false, ulong chainId = 0) + { + if (!forSigning) + { + if (signature is null) + { + stream.Encode(0); + stream.Encode(Bytes.Empty); + stream.Encode(Bytes.Empty); + } + else + { + stream.Encode(GetSignatureFirstElement(signature)); + stream.Encode(signature.RAsSpan.WithoutLeadingZeros()); + stream.Encode(signature.SAsSpan.WithoutLeadingZeros()); + } + } + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/BlobTxDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/BlobTxDecoder.cs new file mode 100644 index 00000000000..5fe1688f170 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/BlobTxDecoder.cs @@ -0,0 +1,186 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; +using Nethermind.Core.Crypto; + +namespace Nethermind.Serialization.Rlp.TxDecoders; + +public sealed class BlobTxDecoder(Func? transactionFactory = null) + : BaseEIP1559TxDecoder(TxType.Blob, transactionFactory) where T : Transaction, new() +{ + public override Transaction? Decode(Span transactionSequence, RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + int positionAfterNetworkWrapper = 0; + if (rlpBehaviors.HasFlag(RlpBehaviors.InMempoolForm)) + { + int networkWrapperLength = rlpStream.ReadSequenceLength(); + positionAfterNetworkWrapper = rlpStream.Position + networkWrapperLength; + int rlpLength = rlpStream.PeekNextRlpLength(); + transactionSequence = rlpStream.Peek(rlpLength); + } + + Transaction? transaction = base.Decode(transactionSequence, rlpStream, rlpBehaviors | RlpBehaviors.ExcludeHashes); + + if (transaction is not null) + { + if (rlpBehaviors.HasFlag(RlpBehaviors.InMempoolForm)) + { + DecodeShardBlobNetworkWrapper(transaction, rlpStream); + + if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) == 0) + { + rlpStream.Check(positionAfterNetworkWrapper); + } + + if ((rlpBehaviors & RlpBehaviors.ExcludeHashes) == 0) + { + transaction.Hash = CalculateHashForNetworkPayloadForm(transactionSequence); + } + } + else if ((rlpBehaviors & RlpBehaviors.ExcludeHashes) == 0) + { + CalculateHash(transaction!, transactionSequence); + } + } + + return transaction; + } + + public override void Decode(ref Transaction? transaction, int txSequenceStart, ReadOnlySpan transactionSequence, + ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + int networkWrapperCheck = 0; + if (rlpBehaviors.HasFlag(RlpBehaviors.InMempoolForm)) + { + int networkWrapperLength = decoderContext.ReadSequenceLength(); + networkWrapperCheck = decoderContext.Position + networkWrapperLength; + int rlpRength = decoderContext.PeekNextRlpLength(); + txSequenceStart = decoderContext.Position; + transactionSequence = decoderContext.Peek(rlpRength); + } + + base.Decode(ref transaction, txSequenceStart, transactionSequence, ref decoderContext, rlpBehaviors | RlpBehaviors.ExcludeHashes); + + if (transaction is not null) + { + if (rlpBehaviors.HasFlag(RlpBehaviors.InMempoolForm)) + { + DecodeShardBlobNetworkWrapper(transaction, ref decoderContext); + + if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) == 0) + { + decoderContext.Check(networkWrapperCheck); + } + + if ((rlpBehaviors & RlpBehaviors.ExcludeHashes) == 0) + { + transaction.Hash = CalculateHashForNetworkPayloadForm(transactionSequence); + } + } + else if ((rlpBehaviors & RlpBehaviors.ExcludeHashes) == 0) + { + CalculateHash(transaction, txSequenceStart, transactionSequence, ref decoderContext); + } + } + } + + protected override void EncodeTypedWrapped(Transaction transaction, RlpStream stream, RlpBehaviors rlpBehaviors, bool forSigning, int contentLength) + { + if (rlpBehaviors.HasFlag(RlpBehaviors.InMempoolForm)) + { + stream.StartSequence(contentLength); + // if the transaction is in mempool form, we started the mempool form sequence + // and now we want to encode the non-mempool form contents, so we need to adjust the content length for that encoding + contentLength = GetContentLength(transaction, rlpBehaviors & ~RlpBehaviors.InMempoolForm, forSigning); + } + + // this always encodes in non-mempool form + base.EncodeTypedWrapped(transaction, stream, rlpBehaviors, forSigning, contentLength); + + // we encode additional mempool form contents if needed + if (rlpBehaviors.HasFlag(RlpBehaviors.InMempoolForm)) + { + EncodeShardBlobNetworkWrapper(transaction, stream); + } + + static void EncodeShardBlobNetworkWrapper(Transaction transaction, RlpStream rlpStream) + { + ShardBlobNetworkWrapper networkWrapper = (ShardBlobNetworkWrapper)transaction.NetworkWrapper!; + rlpStream.Encode(networkWrapper.Blobs); + rlpStream.Encode(networkWrapper.Commitments); + rlpStream.Encode(networkWrapper.Proofs); + } + } + + protected override void DecodePayload(Transaction transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + base.DecodePayload(transaction, rlpStream, rlpBehaviors); + transaction.MaxFeePerBlobGas = rlpStream.DecodeUInt256(); + transaction.BlobVersionedHashes = rlpStream.DecodeByteArrays(); + } + + protected override void DecodePayload(Transaction transaction, ref Rlp.ValueDecoderContext decoderContext, + RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + base.DecodePayload(transaction, ref decoderContext, rlpBehaviors); + transaction.MaxFeePerBlobGas = decoderContext.DecodeUInt256(); + transaction.BlobVersionedHashes = decoderContext.DecodeByteArrays(); + } + + protected override void EncodePayload(Transaction transaction, RlpStream stream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + base.EncodePayload(transaction, stream, rlpBehaviors); + stream.Encode(transaction.MaxFeePerBlobGas!.Value); + stream.Encode(transaction.BlobVersionedHashes!); + } + + private static void DecodeShardBlobNetworkWrapper(Transaction transaction, RlpStream rlpStream) + { + byte[][] blobs = rlpStream.DecodeByteArrays(); + byte[][] commitments = rlpStream.DecodeByteArrays(); + byte[][] proofs = rlpStream.DecodeByteArrays(); + transaction.NetworkWrapper = new ShardBlobNetworkWrapper(blobs, commitments, proofs); + } + + private static void DecodeShardBlobNetworkWrapper(Transaction transaction, ref Rlp.ValueDecoderContext decoderContext) + { + byte[][] blobs = decoderContext.DecodeByteArrays(); + byte[][] commitments = decoderContext.DecodeByteArrays(); + byte[][] proofs = decoderContext.DecodeByteArrays(); + transaction.NetworkWrapper = new ShardBlobNetworkWrapper(blobs, commitments, proofs); + } + + private static Hash256 CalculateHashForNetworkPayloadForm(ReadOnlySpan transactionSequence) + { + KeccakHash hash = KeccakHash.Create(); + Span txType = [(byte)TxType.Blob]; + hash.Update(txType); + hash.Update(transactionSequence); + return new Hash256(hash.Hash); + } + + protected override int GetContentLength(Transaction transaction, RlpBehaviors rlpBehaviors, bool forSigning, + bool isEip155Enabled = false, ulong chainId = 0) + { + int contentLength = base.GetContentLength(transaction, rlpBehaviors, forSigning, isEip155Enabled, chainId); + return rlpBehaviors.HasFlag(RlpBehaviors.InMempoolForm) + ? GetShardBlobNetworkWrapperLength(transaction, contentLength) + : contentLength; + + static int GetShardBlobNetworkWrapperLength(Transaction transaction, int txContentLength) + { + ShardBlobNetworkWrapper networkWrapper = (ShardBlobNetworkWrapper)transaction.NetworkWrapper!; + return Rlp.LengthOfSequence(txContentLength) + + Rlp.LengthOf(networkWrapper.Blobs) + + Rlp.LengthOf(networkWrapper.Commitments) + + Rlp.LengthOf(networkWrapper.Proofs); + } + } + + protected override int GetPayloadLength(Transaction transaction) => + base.GetPayloadLength(transaction) + + Rlp.LengthOf(transaction.MaxFeePerBlobGas) + + Rlp.LengthOf(transaction.BlobVersionedHashes); +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/EIP1559TxDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/EIP1559TxDecoder.cs new file mode 100644 index 00000000000..34d29e1c374 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/EIP1559TxDecoder.cs @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; + +namespace Nethermind.Serialization.Rlp.TxDecoders; + +public class BaseEIP1559TxDecoder(TxType txType, Func? transactionFactory = null) + : BaseAccessListTxDecoder(txType, transactionFactory) where T : Transaction, new() +{ + protected override void DecodeGasPrice(Transaction transaction, RlpStream rlpStream) + { + base.DecodeGasPrice(transaction, rlpStream); + transaction.DecodedMaxFeePerGas = rlpStream.DecodeUInt256(); + } + + protected override void DecodeGasPrice(Transaction transaction, ref Rlp.ValueDecoderContext decoderContext) + { + base.DecodeGasPrice(transaction, ref decoderContext); + transaction.DecodedMaxFeePerGas = decoderContext.DecodeUInt256(); + } + + protected override void EncodeGasPrice(Transaction transaction, RlpStream stream) + { + base.EncodeGasPrice(transaction, stream); + stream.Encode(transaction.DecodedMaxFeePerGas); + } + + protected override int GetPayloadLength(Transaction transaction) => + base.GetPayloadLength(transaction) + Rlp.LengthOf(transaction.DecodedMaxFeePerGas); +} + +public sealed class EIP1559TxDecoder(Func? transactionFactory = null) + : BaseEIP1559TxDecoder(TxType.EIP1559, transactionFactory) where T : Transaction, new(); diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/ITxDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/ITxDecoder.cs new file mode 100644 index 00000000000..30cdcec6a4c --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/ITxDecoder.cs @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; + +namespace Nethermind.Serialization.Rlp.TxDecoders; + +interface ITxDecoder +{ + public TxType Type { get; } + + public Transaction? Decode(Span transactionSequence, RlpStream rlpStream, RlpBehaviors rlpBehaviors); + + public void Decode(ref Transaction? transaction, int txSequenceStart, ReadOnlySpan transactionSequence, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors = RlpBehaviors.None); + + public void Encode(Transaction transaction, RlpStream stream, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool forSigning = false, bool isEip155Enabled = false, ulong chainId = 0); + + public int GetLength(Transaction transaction, RlpBehaviors rlpBehaviors, bool forSigning = false, bool isEip155Enabled = false, ulong chainId = 0); +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/LegacyTxDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/LegacyTxDecoder.cs new file mode 100644 index 00000000000..3937f0c0ae2 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/LegacyTxDecoder.cs @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; +using Nethermind.Core.Crypto; + +namespace Nethermind.Serialization.Rlp.TxDecoders; + +public sealed class LegacyTxDecoder(Func? transactionFactory = null) : BaseTxDecoder(TxType.Legacy, transactionFactory) where T : Transaction, new() +{ + private static bool IncludeSigChainIdHack(bool isEip155Enabled, ulong chainId) => isEip155Enabled && chainId != 0; + + protected override ulong GetSignatureFirstElement(Signature signature) => signature.V; + + protected override void EncodeSignature(Signature? signature, RlpStream stream, bool forSigning, bool isEip155Enabled, ulong chainId) + { + if (forSigning) + { + if (IncludeSigChainIdHack(isEip155Enabled, chainId)) + { + stream.Encode(chainId); + stream.Encode(Rlp.OfEmptyByteArray); + stream.Encode(Rlp.OfEmptyByteArray); + } + } + else + { + base.EncodeSignature(signature, stream, false, isEip155Enabled, chainId); + } + } + + protected override Signature? DecodeSignature(ulong v, ReadOnlySpan rBytes, ReadOnlySpan sBytes, Signature? fallbackSignature = null, RlpBehaviors rlpBehaviors = RlpBehaviors.None) => + SignatureBuilder.FromBytes(v, rBytes, sBytes, rlpBehaviors) ?? fallbackSignature; + + protected override int GetSignatureLength(Signature? signature, bool forSigning, bool isEip155Enabled = false, ulong chainId = 0) + { + if (forSigning) + { + int contentLength = 0; + if (IncludeSigChainIdHack(isEip155Enabled, chainId)) + { + contentLength += Rlp.LengthOf(chainId); + contentLength += 1; + contentLength += 1; + } + + return contentLength; + } + + return base.GetSignatureLength(signature, false, isEip155Enabled, chainId); + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/OptimismTxDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/OptimismTxDecoder.cs new file mode 100644 index 00000000000..18173c5094d --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/OptimismTxDecoder.cs @@ -0,0 +1,70 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core; +using Nethermind.Core.Crypto; + +namespace Nethermind.Serialization.Rlp.TxDecoders; + +public sealed class OptimismTxDecoder(Func? transactionFactory = null) + : BaseEIP1559TxDecoder(TxType.DepositTx, transactionFactory) where T : Transaction, new() +{ + protected override int GetSignatureLength(Signature? signature, bool forSigning, bool isEip155Enabled = false, ulong chainId = 0) => 0; + + protected override void EncodeSignature(Signature? signature, RlpStream stream, bool forSigning, bool isEip155Enabled = false, ulong chainId = 0) + { + } + + protected override void DecodePayload(Transaction transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + transaction.SourceHash = rlpStream.DecodeKeccak(); + transaction.SenderAddress = rlpStream.DecodeAddress(); + transaction.To = rlpStream.DecodeAddress(); + transaction.Mint = rlpStream.DecodeUInt256(); + transaction.Value = rlpStream.DecodeUInt256(); + transaction.GasLimit = rlpStream.DecodeLong(); + transaction.IsOPSystemTransaction = rlpStream.DecodeBool(); + transaction.Data = rlpStream.DecodeByteArray(); + } + + protected override void DecodePayload(Transaction transaction, ref Rlp.ValueDecoderContext decoderContext, + RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + transaction.SourceHash = decoderContext.DecodeKeccak(); + transaction.SenderAddress = decoderContext.DecodeAddress(); + transaction.To = decoderContext.DecodeAddress(); + transaction.Mint = decoderContext.DecodeUInt256(); + transaction.Value = decoderContext.DecodeUInt256(); + transaction.GasLimit = decoderContext.DecodeLong(); + transaction.IsOPSystemTransaction = decoderContext.DecodeBool(); + transaction.Data = decoderContext.DecodeByteArray(); + } + + protected override Signature? DecodeSignature(ulong v, ReadOnlySpan rBytes, ReadOnlySpan sBytes, Signature? fallbackSignature = null, RlpBehaviors rlpBehaviors = RlpBehaviors.None) => + v == 0 && rBytes.IsEmpty && sBytes.IsEmpty + ? fallbackSignature + : base.DecodeSignature(v, rBytes, sBytes, fallbackSignature, rlpBehaviors); + + protected override int GetPayloadLength(Transaction transaction) => + Rlp.LengthOf(transaction.SourceHash) + + Rlp.LengthOf(transaction.SenderAddress) + + Rlp.LengthOf(transaction.To) + + Rlp.LengthOf(transaction.Mint) + + Rlp.LengthOf(transaction.Value) + + Rlp.LengthOf(transaction.GasLimit) + + Rlp.LengthOf(transaction.IsOPSystemTransaction) + + Rlp.LengthOf(transaction.Data); + + protected override void EncodePayload(Transaction transaction, RlpStream stream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + stream.Encode(transaction.SourceHash); + stream.Encode(transaction.SenderAddress); + stream.Encode(transaction.To); + stream.Encode(transaction.Mint); + stream.Encode(transaction.Value); + stream.Encode(transaction.GasLimit); + stream.Encode(transaction.IsOPSystemTransaction); + stream.Encode(transaction.Data); + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/SignatureBuilder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/SignatureBuilder.cs new file mode 100644 index 00000000000..ed4c74eaae9 --- /dev/null +++ b/src/Nethermind/Nethermind.Serialization.Rlp/MyTxDecoder/SignatureBuilder.cs @@ -0,0 +1,44 @@ +// SPDX-FileCopyrightText: 2022 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; + +namespace Nethermind.Serialization.Rlp.TxDecoders; + +public static class SignatureBuilder +{ + public static Signature? FromBytes(ulong v, ReadOnlySpan rBytes, ReadOnlySpan sBytes, RlpBehaviors rlpBehaviors) + { + bool allowUnsigned = rlpBehaviors.HasFlag(RlpBehaviors.AllowUnsigned); + bool isSignatureOk = true; + string signatureError = null; + if (rBytes.Length == 0 || sBytes.Length == 0) + { + isSignatureOk = false; + signatureError = "VRS is 0 length when decoding Transaction"; + } + else if (rBytes[0] == 0 || sBytes[0] == 0) + { + isSignatureOk = false; + signatureError = "VRS starting with 0"; + } + else if (rBytes.Length > 32 || sBytes.Length > 32) + { + isSignatureOk = false; + signatureError = "R and S lengths expected to be less or equal 32"; + } + else if (rBytes.SequenceEqual(Bytes.Zero32) && sBytes.SequenceEqual(Bytes.Zero32)) + { + isSignatureOk = false; + signatureError = "Both 'r' and 's' are zero when decoding a transaction"; + } + + return isSignatureOk + ? new Signature(rBytes, sBytes, v) + : allowUnsigned + ? null + : throw new RlpException(signatureError); + } +} diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/ReceiptRecoveryBlock.cs b/src/Nethermind/Nethermind.Serialization.Rlp/ReceiptRecoveryBlock.cs index 3751bb89074..911dc31c9d5 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/ReceiptRecoveryBlock.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/ReceiptRecoveryBlock.cs @@ -51,9 +51,12 @@ public Transaction GetNextTransaction() return _transactions[_currentTransactionIndex++]; } - Rlp.ValueDecoderContext decoderContext = new(_transactionData, true); - decoderContext.Position = _currentTransactionPosition; - TxDecoder.InstanceWithoutLazyHash.Decode(ref decoderContext, ref _txBuffer, RlpBehaviors.AllowUnsigned); + Rlp.ValueDecoderContext decoderContext = new(_transactionData, true) + { + Position = _currentTransactionPosition + }; + TxDecoder.Instance.Decode(ref decoderContext, ref _txBuffer, RlpBehaviors.AllowUnsigned); + Hash256 _ = _txBuffer.Hash; // Force Hash evaluation _currentTransactionPosition = decoderContext.Position; return _txBuffer; diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs b/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs index f6d97a79bf1..8859089addb 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/Rlp.cs @@ -334,15 +334,8 @@ public static Rlp Encode(Transaction transaction) return Encode(transaction, false); } - public static Rlp Encode( - Transaction transaction, - bool forSigning, - bool isEip155Enabled = false, - ulong chainId = 0) - { - return TxDecoder.Instance.EncodeTx(transaction, RlpBehaviors.SkipTypedWrapping, forSigning, isEip155Enabled, - chainId); - } + public static Rlp Encode(Transaction transaction, bool forSigning, bool isEip155Enabled = false, ulong chainId = 0) => + TxDecoder.Instance.EncodeTx(transaction, RlpBehaviors.SkipTypedWrapping, forSigning, isEip155Enabled, chainId); public static Rlp Encode(int value) { diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs b/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs index 04f0de67207..e5842950ad3 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/RlpStream.cs @@ -24,7 +24,7 @@ public class RlpStream private static readonly HeaderDecoder _headerDecoder = new(); private static readonly BlockDecoder _blockDecoder = new(); private static readonly BlockInfoDecoder _blockInfoDecoder = new(); - private static readonly TxDecoder _txDecoder = new(); + private static readonly TxDecoder _txDecoder = TxDecoder.Instance; private static readonly WithdrawalDecoder _withdrawalDecoder = new(); private static readonly LogEntryDecoder _logEntryDecoder = LogEntryDecoder.Instance; diff --git a/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs b/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs index 66059c44de4..0ef1eb96806 100644 --- a/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs +++ b/src/Nethermind/Nethermind.Serialization.Rlp/TxDecoder.cs @@ -2,887 +2,169 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using Microsoft.Extensions.ObjectPool; using Nethermind.Core; -using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; -using Nethermind.Serialization.Rlp.Eip2930; +using Nethermind.Serialization.Rlp.TxDecoders; -namespace Nethermind.Serialization.Rlp -{ - public sealed class TxDecoder : TxDecoder - { - public const int MaxDelayedHashTxnSize = 32768; - public static TxDecoder Instance = new TxDecoder(); - public static TxDecoder InstanceWithoutLazyHash = new TxDecoder(false); - public static ObjectPool TxObjectPool = new DefaultObjectPool(new Transaction.PoolPolicy(), Environment.ProcessorCount * 4); - - public TxDecoder() : base(true) // Rlp will try to find empty constructor. - { - } - - public TxDecoder(bool lazyHash) : base(lazyHash) - { - } - - protected override Transaction NewTx() - { - return TxObjectPool.Get(); - } - } - public class SystemTxDecoder : TxDecoder { } - public class GeneratedTxDecoder : TxDecoder { } - - public class TxDecoder : - IRlpStreamDecoder, - IRlpValueDecoder - where T : Transaction, new() - { - private readonly AccessListDecoder _accessListDecoder = new(); - private readonly bool _lazyHash; - - protected TxDecoder(bool lazyHash = true) - { - _lazyHash = lazyHash; - } - - protected virtual T NewTx() - { - return new(); - } - - public T? Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) - { - if (rlpStream.IsNextItemNull()) - { - rlpStream.ReadByte(); - return null; - } - - Span transactionSequence = DecodeTxTypeAndGetSequence(rlpStream, rlpBehaviors, out TxType txType); - - T transaction = NewTx(); - transaction.Type = txType; - - int positionAfterNetworkWrapper = 0; - if ((rlpBehaviors & RlpBehaviors.InMempoolForm) == RlpBehaviors.InMempoolForm && transaction.MayHaveNetworkForm) - { - int networkWrapperLength = rlpStream.ReadSequenceLength(); - positionAfterNetworkWrapper = rlpStream.Position + networkWrapperLength; - int rlpLength = rlpStream.PeekNextRlpLength(); - transactionSequence = rlpStream.Peek(rlpLength); - } - - int transactionLength = rlpStream.ReadSequenceLength(); - int lastCheck = rlpStream.Position + transactionLength; +namespace Nethermind.Serialization.Rlp; - switch (transaction.Type) - { - case TxType.Legacy: - TxDecoder.DecodeLegacyPayloadWithoutSig(transaction, rlpStream); - break; - case TxType.AccessList: - DecodeAccessListPayloadWithoutSig(transaction, rlpStream, rlpBehaviors); - break; - case TxType.EIP1559: - DecodeEip1559PayloadWithoutSig(transaction, rlpStream, rlpBehaviors); - break; - case TxType.Blob: - DecodeShardBlobPayloadWithoutSig(transaction, rlpStream, rlpBehaviors); - break; - case TxType.DepositTx: - TxDecoder.DecodeDepositPayloadWithoutSig(transaction, rlpStream, rlpBehaviors); - break; - } - - if (rlpStream.Position < lastCheck) - { - DecodeSignature(rlpStream, rlpBehaviors, transaction); - } +public sealed class TxDecoder : TxDecoder +{ + public static readonly ObjectPool TxObjectPool = new DefaultObjectPool(new Transaction.PoolPolicy(), Environment.ProcessorCount * 4); - if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) != RlpBehaviors.AllowExtraBytes) - { - rlpStream.Check(lastCheck); - } +#pragma warning disable CS0618 + public static readonly TxDecoder Instance = new(); +#pragma warning restore CS0618 - if ((rlpBehaviors & RlpBehaviors.InMempoolForm) == RlpBehaviors.InMempoolForm && transaction.MayHaveNetworkForm) - { - switch (transaction.Type) - { - case TxType.Blob: - TxDecoder.DecodeShardBlobNetworkPayload(transaction, rlpStream, rlpBehaviors); - break; - } + // Rlp will try to find a public parameterless constructor during static initialization. + // The lambda cannot be removed due to static block initialization order. + [Obsolete("Use `TxDecoder.Instance` instead")] + public TxDecoder() : base(() => TxObjectPool.Get()) { } +} - if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) == 0) - { - rlpStream.Check(positionAfterNetworkWrapper); - } +public sealed class SystemTxDecoder : TxDecoder; +public sealed class GeneratedTxDecoder : TxDecoder; - if ((rlpBehaviors & RlpBehaviors.ExcludeHashes) == 0) - { - transaction.Hash = CalculateHashForNetworkPayloadForm(transaction.Type, transactionSequence); - } - } - else if ((rlpBehaviors & RlpBehaviors.ExcludeHashes) == 0) - { - if (transactionSequence.Length <= TxDecoder.MaxDelayedHashTxnSize && _lazyHash) - { - // Delay hash generation, as may be filtered as having too low gas etc - transaction.SetPreHashNoLock(transactionSequence); - } - else - { - // Just calculate the Hash as txn too large - transaction.Hash = Keccak.Compute(transactionSequence); - } - } +public class TxDecoder : IRlpStreamDecoder, IRlpValueDecoder where T : Transaction, new() +{ + private readonly Dictionary _decoders; - return transaction; - } + protected TxDecoder(Func? transactionFactory = null) + { + Func factory = transactionFactory ?? (() => new T()); + _decoders = new() { + { TxType.Legacy, new LegacyTxDecoder(factory) }, + { TxType.AccessList, new AccessListTxDecoder(factory) }, + { TxType.EIP1559, new EIP1559TxDecoder(factory) }, + { TxType.Blob, new BlobTxDecoder(factory) }, + { TxType.DepositTx, new OptimismTxDecoder(factory) } + }; + } - private static Span DecodeTxTypeAndGetSequence(RlpStream rlpStream, RlpBehaviors rlpBehaviors, out TxType txType) + public T? Decode(RlpStream rlpStream, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + void ThrowIfLegacy(TxType txType1) { - static Span DecodeTxType(RlpStream rlpStream, int length, out TxType txType) - { - Span sequence = rlpStream.Peek(length); - txType = (TxType)rlpStream.ReadByte(); - return txType == TxType.Legacy - ? throw new RlpException("Legacy transactions are not allowed in EIP-2718 Typed Transaction Envelope.") - : sequence; - } - - Span transactionSequence = rlpStream.PeekNextItem(); - txType = TxType.Legacy; - if ((rlpBehaviors & RlpBehaviors.SkipTypedWrapping) == RlpBehaviors.SkipTypedWrapping) - { - byte firstByte = rlpStream.PeekByte(); - if (firstByte <= 0x7f) // it is typed transactions - { - transactionSequence = DecodeTxType(rlpStream, rlpStream.Length, out txType); - } - } - else if (!rlpStream.IsSequenceNext()) + if (txType1 == TxType.Legacy) { - transactionSequence = DecodeTxType(rlpStream, rlpStream.ReadPrefixAndContentLength().ContentLength, out txType); + throw new RlpException("Legacy transactions are not allowed in EIP-2718 Typed Transaction Envelope"); } - - return transactionSequence; - } - - private static Hash256 CalculateHashForNetworkPayloadForm(TxType type, ReadOnlySpan transactionSequence) - { - KeccakHash hash = KeccakHash.Create(); - Span txType = stackalloc byte[1]; - txType[0] = (byte)type; - hash.Update(txType); - hash.Update(transactionSequence); - return new Hash256(hash.Hash); - } - - private static void DecodeLegacyPayloadWithoutSig(T transaction, RlpStream rlpStream) - { - transaction.Nonce = rlpStream.DecodeUInt256(); - transaction.GasPrice = rlpStream.DecodeUInt256(); - transaction.GasLimit = rlpStream.DecodeLong(); - transaction.To = rlpStream.DecodeAddress(); - transaction.Value = rlpStream.DecodeUInt256(); - transaction.Data = rlpStream.DecodeByteArray(); } - private void DecodeAccessListPayloadWithoutSig(T transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors) + if (rlpStream.IsNextItemNull()) { - transaction.ChainId = rlpStream.DecodeULong(); - transaction.Nonce = rlpStream.DecodeUInt256(); - transaction.GasPrice = rlpStream.DecodeUInt256(); - transaction.GasLimit = rlpStream.DecodeLong(); - transaction.To = rlpStream.DecodeAddress(); - transaction.Value = rlpStream.DecodeUInt256(); - transaction.Data = rlpStream.DecodeByteArray(); - transaction.AccessList = _accessListDecoder.Decode(rlpStream, rlpBehaviors); + rlpStream.ReadByte(); + return null; } - private void DecodeEip1559PayloadWithoutSig(T transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors) + Span transactionSequence = rlpStream.PeekNextItem(); + TxType txType = TxType.Legacy; + if (rlpBehaviors.HasFlag(RlpBehaviors.SkipTypedWrapping)) { - transaction.ChainId = rlpStream.DecodeULong(); - transaction.Nonce = rlpStream.DecodeUInt256(); - transaction.GasPrice = rlpStream.DecodeUInt256(); // gas premium - transaction.DecodedMaxFeePerGas = rlpStream.DecodeUInt256(); - transaction.GasLimit = rlpStream.DecodeLong(); - transaction.To = rlpStream.DecodeAddress(); - transaction.Value = rlpStream.DecodeUInt256(); - transaction.Data = rlpStream.DecodeByteArray(); - transaction.AccessList = _accessListDecoder.Decode(rlpStream, rlpBehaviors); - } - - private void DecodeShardBlobPayloadWithoutSig(T transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors) - { - transaction.ChainId = rlpStream.DecodeULong(); - transaction.Nonce = rlpStream.DecodeUInt256(); - transaction.GasPrice = rlpStream.DecodeUInt256(); // gas premium - transaction.DecodedMaxFeePerGas = rlpStream.DecodeUInt256(); - transaction.GasLimit = rlpStream.DecodeLong(); - transaction.To = rlpStream.DecodeAddress(); - transaction.Value = rlpStream.DecodeUInt256(); - transaction.Data = rlpStream.DecodeByteArray(); - transaction.AccessList = _accessListDecoder.Decode(rlpStream, rlpBehaviors); - transaction.MaxFeePerBlobGas = rlpStream.DecodeUInt256(); - transaction.BlobVersionedHashes = rlpStream.DecodeByteArrays(); - } - - private static void DecodeShardBlobNetworkPayload(T transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors) - { - byte[][] blobs = rlpStream.DecodeByteArrays(); - byte[][] commitments = rlpStream.DecodeByteArrays(); - byte[][] proofs = rlpStream.DecodeByteArrays(); - transaction.NetworkWrapper = new ShardBlobNetworkWrapper(blobs, commitments, proofs); - } - - private static void DecodeDepositPayloadWithoutSig(T transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors) - { - transaction.SourceHash = rlpStream.DecodeKeccak(); - transaction.SenderAddress = rlpStream.DecodeAddress(); - transaction.To = rlpStream.DecodeAddress(); - transaction.Mint = rlpStream.DecodeUInt256(); - transaction.Value = rlpStream.DecodeUInt256(); - transaction.GasLimit = rlpStream.DecodeLong(); - transaction.IsOPSystemTransaction = rlpStream.DecodeBool(); - transaction.Data = rlpStream.DecodeByteArray(); - } - - private static void DecodeLegacyPayloadWithoutSig(T transaction, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors) - { - transaction.Nonce = decoderContext.DecodeUInt256(); - transaction.GasPrice = decoderContext.DecodeUInt256(); - transaction.GasLimit = decoderContext.DecodeLong(); - transaction.To = decoderContext.DecodeAddress(); - transaction.Value = decoderContext.DecodeUInt256(); - transaction.Data = decoderContext.DecodeByteArrayMemory(); - } - - private void DecodeAccessListPayloadWithoutSig(T transaction, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors) - { - transaction.ChainId = decoderContext.DecodeULong(); - transaction.Nonce = decoderContext.DecodeUInt256(); - transaction.GasPrice = decoderContext.DecodeUInt256(); - transaction.GasLimit = decoderContext.DecodeLong(); - transaction.To = decoderContext.DecodeAddress(); - transaction.Value = decoderContext.DecodeUInt256(); - transaction.Data = decoderContext.DecodeByteArrayMemory(); - transaction.AccessList = _accessListDecoder.Decode(ref decoderContext, rlpBehaviors); - } - - private void DecodeEip1559PayloadWithoutSig(T transaction, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors) - { - transaction.ChainId = decoderContext.DecodeULong(); - transaction.Nonce = decoderContext.DecodeUInt256(); - transaction.GasPrice = decoderContext.DecodeUInt256(); // gas premium - transaction.DecodedMaxFeePerGas = decoderContext.DecodeUInt256(); - transaction.GasLimit = decoderContext.DecodeLong(); - transaction.To = decoderContext.DecodeAddress(); - transaction.Value = decoderContext.DecodeUInt256(); - transaction.Data = decoderContext.DecodeByteArrayMemory(); - transaction.AccessList = _accessListDecoder.Decode(ref decoderContext, rlpBehaviors); - } - - private void DecodeShardBlobPayloadWithoutSig(T transaction, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors) - { - transaction.ChainId = decoderContext.DecodeULong(); - transaction.Nonce = decoderContext.DecodeUInt256(); - transaction.GasPrice = decoderContext.DecodeUInt256(); // gas premium - transaction.DecodedMaxFeePerGas = decoderContext.DecodeUInt256(); - transaction.GasLimit = decoderContext.DecodeLong(); - transaction.To = decoderContext.DecodeAddress(); - transaction.Value = decoderContext.DecodeUInt256(); - transaction.Data = decoderContext.DecodeByteArray(); - transaction.AccessList = _accessListDecoder.Decode(ref decoderContext, rlpBehaviors); - transaction.MaxFeePerBlobGas = decoderContext.DecodeUInt256(); - transaction.BlobVersionedHashes = decoderContext.DecodeByteArrays(); - } - - private static void DecodeShardBlobNetworkPayload(T transaction, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors) - { - byte[][] blobs = decoderContext.DecodeByteArrays(); - byte[][] commitments = decoderContext.DecodeByteArrays(); - byte[][] proofs = decoderContext.DecodeByteArrays(); - transaction.NetworkWrapper = new ShardBlobNetworkWrapper(blobs, commitments, proofs); - } - - private static void DecodeDepositPayloadWithoutSig(T transaction, ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors) - { - transaction.SourceHash = decoderContext.DecodeKeccak(); - transaction.SenderAddress = decoderContext.DecodeAddress(); - transaction.To = decoderContext.DecodeAddress(); - transaction.Mint = decoderContext.DecodeUInt256(); - transaction.Value = decoderContext.DecodeUInt256(); - transaction.GasLimit = decoderContext.DecodeLong(); - transaction.IsOPSystemTransaction = decoderContext.DecodeBool(); - transaction.Data = decoderContext.DecodeByteArray(); - } - - private static void EncodeLegacyWithoutPayload(T item, RlpStream stream) - { - stream.Encode(item.Nonce); - stream.Encode(item.GasPrice); - stream.Encode(item.GasLimit); - stream.Encode(item.To); - stream.Encode(item.Value); - stream.Encode(item.Data); - } - - private void EncodeAccessListPayloadWithoutPayload(T item, RlpStream stream, RlpBehaviors rlpBehaviors) - { - stream.Encode(item.ChainId ?? 0); - stream.Encode(item.Nonce); - stream.Encode(item.GasPrice); - stream.Encode(item.GasLimit); - stream.Encode(item.To); - stream.Encode(item.Value); - stream.Encode(item.Data); - _accessListDecoder.Encode(stream, item.AccessList, rlpBehaviors); - } - - private void EncodeEip1559PayloadWithoutPayload(T item, RlpStream stream, RlpBehaviors rlpBehaviors) - { - stream.Encode(item.ChainId ?? 0); - stream.Encode(item.Nonce); - stream.Encode(item.GasPrice); // gas premium - stream.Encode(item.DecodedMaxFeePerGas); - stream.Encode(item.GasLimit); - stream.Encode(item.To); - stream.Encode(item.Value); - stream.Encode(item.Data); - _accessListDecoder.Encode(stream, item.AccessList, rlpBehaviors); - } - - private void EncodeShardBlobPayloadWithoutPayload(T item, RlpStream stream, RlpBehaviors rlpBehaviors) - { - stream.Encode(item.ChainId ?? 0); - stream.Encode(item.Nonce); - stream.Encode(item.GasPrice); // gas premium - stream.Encode(item.DecodedMaxFeePerGas); - stream.Encode(item.GasLimit); - stream.Encode(item.To); - stream.Encode(item.Value); - stream.Encode(item.Data); - _accessListDecoder.Encode(stream, item.AccessList, rlpBehaviors); - stream.Encode(item.MaxFeePerBlobGas.Value); - stream.Encode(item.BlobVersionedHashes); - } - - private static void EncodeDepositTxPayloadWithoutPayload(T item, RlpStream stream) - { - stream.Encode(item.SourceHash); - stream.Encode(item.SenderAddress); - stream.Encode(item.To); - stream.Encode(item.Mint); - stream.Encode(item.Value); - stream.Encode(item.GasLimit); - stream.Encode(item.IsOPSystemTransaction); - stream.Encode(item.Data); - } - - // b9018201f9017e86796f6c6f763304843b9aca00829ab0948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f90111f859940000000000000000000000000000000000001337f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000133700000000000000000000000f859940000000000000000000000000000000000001337f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000133700000000000000000000000f859940000000000000000000000000000000000001337f842a00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000013370000000000000000000000080a09e41e382c76d2913521d7191ecced4a1a16fe0f3e5e22d83d50dd58adbe409e1a07c0e036eff80f9ca192ac26d533fc49c280d90c8b62e90c1a1457b50e51e6144 - // b8__ca01f8__c786796f6c6f763304843b9aca00829ab0948a8eafb1cf62bfbeb1741769dae1a9dd479961928080f8__5bf859940000000000000000000000000000000000001337f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000133700000000000000000000000f859940000000000000000000000000000000000001337f842a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000133700000000000000000000000f859940000000000000000000000000000000000001337f842a00000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000013370000000000000000000000080a09e41e382c76d2913521d7191ecced4a1a16fe0f3e5e22d83d50dd58adbe409e1a07c0e036eff80f9ca192ac26d533fc49c280d90c8b62e90c1a1457b50e51e6144000000 - - public T? Decode(ref Rlp.ValueDecoderContext decoderContext, - RlpBehaviors rlpBehaviors = RlpBehaviors.None) - - { - T transaction = null; - Decode(ref decoderContext, ref transaction, rlpBehaviors); - - return transaction; - } - - - public void Decode(ref Rlp.ValueDecoderContext decoderContext, ref T? transaction, - RlpBehaviors rlpBehaviors = RlpBehaviors.None) - { - if (decoderContext.IsNextItemNull()) + if (rlpStream.PeekByte() <= Transaction.MaxTxType) // it is typed transactions { - decoderContext.ReadByte(); - transaction = null; - return; - } - - transaction ??= NewTx(); - transaction.Type = TxType.Legacy; - - int txSequenceStart = decoderContext.Position; - ReadOnlySpan transactionSequence = decoderContext.PeekNextItem(); - - if ((rlpBehaviors & RlpBehaviors.SkipTypedWrapping) == RlpBehaviors.SkipTypedWrapping) - { - byte firstByte = decoderContext.PeekByte(); - if (firstByte <= 0x7f) // it is typed transactions - { - txSequenceStart = decoderContext.Position; - transactionSequence = decoderContext.Peek(decoderContext.Length); - transaction.Type = (TxType)decoderContext.ReadByte(); - } - } - else - { - if (!decoderContext.IsSequenceNext()) - { - (int PrefixLength, int ContentLength) prefixAndContentLength = - decoderContext.ReadPrefixAndContentLength(); - txSequenceStart = decoderContext.Position; - transactionSequence = decoderContext.Peek(prefixAndContentLength.ContentLength); - transaction.Type = (TxType)decoderContext.ReadByte(); - } - } - - int networkWrapperCheck = 0; - if ((rlpBehaviors & RlpBehaviors.InMempoolForm) == RlpBehaviors.InMempoolForm && transaction.MayHaveNetworkForm) - { - int networkWrapperLength = decoderContext.ReadSequenceLength(); - networkWrapperCheck = decoderContext.Position + networkWrapperLength; - int rlpRength = decoderContext.PeekNextRlpLength(); - txSequenceStart = decoderContext.Position; - transactionSequence = decoderContext.Peek(rlpRength); - } - - int transactionLength = decoderContext.ReadSequenceLength(); - int lastCheck = decoderContext.Position + transactionLength; - - switch (transaction.Type) - { - case TxType.Legacy: - TxDecoder.DecodeLegacyPayloadWithoutSig(transaction, ref decoderContext, rlpBehaviors); - break; - case TxType.AccessList: - DecodeAccessListPayloadWithoutSig(transaction, ref decoderContext, rlpBehaviors); - break; - case TxType.EIP1559: - DecodeEip1559PayloadWithoutSig(transaction, ref decoderContext, rlpBehaviors); - break; - case TxType.Blob: - DecodeShardBlobPayloadWithoutSig(transaction, ref decoderContext, rlpBehaviors); - break; - case TxType.DepositTx: - TxDecoder.DecodeDepositPayloadWithoutSig(transaction, ref decoderContext, rlpBehaviors); - break; - } - - if (decoderContext.Position < lastCheck) - { - DecodeSignature(ref decoderContext, rlpBehaviors, transaction); - } - - if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) != RlpBehaviors.AllowExtraBytes) - { - decoderContext.Check(lastCheck); - } - - if ((rlpBehaviors & RlpBehaviors.InMempoolForm) == RlpBehaviors.InMempoolForm && transaction.MayHaveNetworkForm) - { - switch (transaction.Type) - { - case TxType.Blob: - TxDecoder.DecodeShardBlobNetworkPayload(transaction, ref decoderContext, rlpBehaviors); - break; - } - - if ((rlpBehaviors & RlpBehaviors.AllowExtraBytes) == 0) - { - decoderContext.Check(networkWrapperCheck); - } - - if ((rlpBehaviors & RlpBehaviors.ExcludeHashes) == 0) - { - transaction.Hash = CalculateHashForNetworkPayloadForm(transaction.Type, transactionSequence); - } - } - else if ((rlpBehaviors & RlpBehaviors.ExcludeHashes) == 0) - { - if (transactionSequence.Length <= TxDecoder.MaxDelayedHashTxnSize && _lazyHash) - { - // Delay hash generation, as may be filtered as having too low gas etc - if (decoderContext.ShouldSliceMemory) - { - // Do not copy the memory in this case. - int currentPosition = decoderContext.Position; - decoderContext.Position = txSequenceStart; - transaction.SetPreHashMemoryNoLock(decoderContext.ReadMemory(transactionSequence.Length)); - decoderContext.Position = currentPosition; - } - else - { - transaction.SetPreHashNoLock(transactionSequence); - } - } - else - { - // Just calculate the Hash immediately as txn too large - transaction.Hash = Keccak.Compute(transactionSequence); - } + transactionSequence = rlpStream.Peek(rlpStream.Length); + txType = (TxType)rlpStream.ReadByte(); // read tx type + ThrowIfLegacy(txType); } } - - private static void DecodeSignature( - RlpStream rlpStream, - RlpBehaviors rlpBehaviors, - T transaction) + else if (!rlpStream.IsSequenceNext()) { - ulong v = rlpStream.DecodeULong(); - ReadOnlySpan rBytes = rlpStream.DecodeByteArraySpan(); - ReadOnlySpan sBytes = rlpStream.DecodeByteArraySpan(); - ApplySignature(transaction, v, rBytes, sBytes, rlpBehaviors); + (int _, int contentLength) = rlpStream.ReadPrefixAndContentLength(); + transactionSequence = rlpStream.Peek(contentLength); + txType = (TxType)rlpStream.ReadByte(); + ThrowIfLegacy(txType); } - private static void DecodeSignature( - ref Rlp.ValueDecoderContext decoderContext, - RlpBehaviors rlpBehaviors, - T transaction) - { - ulong v = decoderContext.DecodeULong(); - ReadOnlySpan rBytes = decoderContext.DecodeByteArraySpan(); - ReadOnlySpan sBytes = decoderContext.DecodeByteArraySpan(); - ApplySignature(transaction, v, rBytes, sBytes, rlpBehaviors); - } - - private static void ApplySignature( - T transaction, - ulong v, - ReadOnlySpan rBytes, - ReadOnlySpan sBytes, - RlpBehaviors rlpBehaviors) - { - if (transaction.Type == TxType.DepositTx && v == 0 && rBytes.IsEmpty && sBytes.IsEmpty) return; - - bool allowUnsigned = (rlpBehaviors & RlpBehaviors.AllowUnsigned) == RlpBehaviors.AllowUnsigned; - bool isSignatureOk = true; - string signatureError = null; - if (rBytes.Length == 0 || sBytes.Length == 0) - { - isSignatureOk = false; - signatureError = "VRS is 0 length when decoding Transaction"; - } - else if (rBytes[0] == 0 || sBytes[0] == 0) - { - isSignatureOk = false; - signatureError = "VRS starting with 0"; - } - else if (rBytes.Length > 32 || sBytes.Length > 32) - { - isSignatureOk = false; - signatureError = "R and S lengths expected to be less or equal 32"; - } - else if (rBytes.SequenceEqual(Bytes.Zero32) && sBytes.SequenceEqual(Bytes.Zero32)) - { - isSignatureOk = false; - signatureError = "Both 'r' and 's' are zero when decoding a transaction."; - } - - if (isSignatureOk) - { - if (transaction.Type != TxType.Legacy) - { - v += Signature.VOffset; - } + return (T)GetDecoder(txType).Decode(transactionSequence, rlpStream, rlpBehaviors); + } - Signature signature = new(rBytes, sBytes, v); - transaction.Signature = signature; - } - else if (!allowUnsigned) - { - throw new RlpException(signatureError); - } - } + private ITxDecoder GetDecoder(TxType txType) => + _decoders.TryGetValue(txType, out ITxDecoder decoder) + ? decoder + : throw new RlpException($"Unknown transaction type {txType}") { Data = { { "txType", txType } } }; - public Rlp Encode(T item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) - { - RlpStream rlpStream = new(GetLength(item, rlpBehaviors)); - Encode(rlpStream, item, rlpBehaviors); - return new Rlp(rlpStream.Data.ToArray()); - } + public T? Decode(ref Rlp.ValueDecoderContext decoderContext, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + T transaction = null; + Decode(ref decoderContext, ref transaction, rlpBehaviors); + return transaction; + } - public void Encode(RlpStream stream, T? item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + public void Decode(ref Rlp.ValueDecoderContext decoderContext, ref T? transaction, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + if (decoderContext.IsNextItemNull()) { - EncodeTx(stream, item, rlpBehaviors); + decoderContext.ReadByte(); + transaction = null; + return; } - public Rlp EncodeTx(T? item, RlpBehaviors rlpBehaviors = RlpBehaviors.None, - bool forSigning = false, bool isEip155Enabled = false, ulong chainId = 0) - { - RlpStream rlpStream = new(GetTxLength(item, rlpBehaviors, forSigning, isEip155Enabled, chainId)); - EncodeTx(rlpStream, item, rlpBehaviors, forSigning, isEip155Enabled, chainId); - return new Rlp(rlpStream.Data.ToArray()); - } + int txSequenceStart = decoderContext.Position; + ReadOnlySpan transactionSequence = decoderContext.PeekNextItem(); - private void EncodeTx(RlpStream stream, T? item, RlpBehaviors rlpBehaviors = RlpBehaviors.None, - bool forSigning = false, bool isEip155Enabled = false, ulong chainId = 0) + TxType txType = TxType.Legacy; + if (rlpBehaviors.HasFlag(RlpBehaviors.SkipTypedWrapping)) { - if (item is null) - { - stream.WriteByte(Rlp.NullObjectByte); - return; - } - - bool includeSigChainIdHack = isEip155Enabled && chainId != 0 && item.Type == TxType.Legacy; - - int contentLength = GetContentLength(item, forSigning, isEip155Enabled, chainId, - (rlpBehaviors & RlpBehaviors.InMempoolForm) == RlpBehaviors.InMempoolForm); - int sequenceLength = Rlp.LengthOfSequence(contentLength); - - if (item.Type != TxType.Legacy) + if (decoderContext.PeekByte() <= Transaction.MaxTxType) // it is typed transactions { - if ((rlpBehaviors & RlpBehaviors.SkipTypedWrapping) == RlpBehaviors.None) - { - stream.StartByteArray(sequenceLength + 1, false); - } - - stream.WriteByte((byte)item.Type); - } - - if ((rlpBehaviors & RlpBehaviors.InMempoolForm) == RlpBehaviors.InMempoolForm && item.MayHaveNetworkForm) - { - stream.StartSequence(contentLength); - contentLength = GetContentLength(item, forSigning, isEip155Enabled, chainId, false); - } - - stream.StartSequence(contentLength); - - switch (item.Type) - { - case TxType.Legacy: - TxDecoder.EncodeLegacyWithoutPayload(item, stream); - break; - case TxType.AccessList: - EncodeAccessListPayloadWithoutPayload(item, stream, rlpBehaviors); - break; - case TxType.EIP1559: - EncodeEip1559PayloadWithoutPayload(item, stream, rlpBehaviors); - break; - case TxType.Blob: - EncodeShardBlobPayloadWithoutPayload(item, stream, rlpBehaviors); - break; - case TxType.DepositTx: - TxDecoder.EncodeDepositTxPayloadWithoutPayload(item, stream); - break; - } - - EncodeSignature(stream, item, forSigning, chainId, includeSigChainIdHack); - - if ((rlpBehaviors & RlpBehaviors.InMempoolForm) == RlpBehaviors.InMempoolForm && item.MayHaveNetworkForm) - { - switch (item.Type) - { - case TxType.Blob: - TxDecoder.EncodeShardBlobNetworkPayload(item, stream, rlpBehaviors); - break; - } + txSequenceStart = decoderContext.Position; + transactionSequence = decoderContext.Peek(decoderContext.Length); + txType = (TxType)decoderContext.ReadByte(); } } - - private static void EncodeSignature(RlpStream stream, T item, bool forSigning, ulong chainId, bool includeSigChainIdHack) + else { - if (item.Type == TxType.DepositTx) - return; - - if (forSigning) + if (!decoderContext.IsSequenceNext()) { - if (includeSigChainIdHack) - { - stream.Encode(chainId); - stream.Encode(Rlp.OfEmptyByteArray); - stream.Encode(Rlp.OfEmptyByteArray); - } - } - else - { - // TODO: move it to a signature decoder - if (item.Signature is null) - { - stream.Encode(0); - stream.Encode(Bytes.Empty); - stream.Encode(Bytes.Empty); - } - else - { - stream.Encode(item.Type == TxType.Legacy ? item.Signature.V : item.Signature.RecoveryId); - stream.Encode(item.Signature.RAsSpan.WithoutLeadingZeros()); - stream.Encode(item.Signature.SAsSpan.WithoutLeadingZeros()); - } + (_, int contentLength) = decoderContext.ReadPrefixAndContentLength(); + txSequenceStart = decoderContext.Position; + transactionSequence = decoderContext.Peek(contentLength); + txType = (TxType)decoderContext.ReadByte(); } } - private static void EncodeShardBlobNetworkPayload(T transaction, RlpStream rlpStream, RlpBehaviors rlpBehaviors) - { - ShardBlobNetworkWrapper networkWrapper = transaction.NetworkWrapper as ShardBlobNetworkWrapper; - rlpStream.Encode(networkWrapper.Blobs); - rlpStream.Encode(networkWrapper.Commitments); - rlpStream.Encode(networkWrapper.Proofs); - } - - private static int GetLegacyContentLength(T item) - { - return Rlp.LengthOf(item.Nonce) - + Rlp.LengthOf(item.GasPrice) - + Rlp.LengthOf(item.GasLimit) - + Rlp.LengthOf(item.To) - + Rlp.LengthOf(item.Value) - + Rlp.LengthOf(item.Data); - } - - private int GetAccessListContentLength(T item) - { - return Rlp.LengthOf(item.Nonce) - + Rlp.LengthOf(item.GasPrice) - + Rlp.LengthOf(item.GasLimit) - + Rlp.LengthOf(item.To) - + Rlp.LengthOf(item.Value) - + Rlp.LengthOf(item.Data) - + Rlp.LengthOf(item.ChainId ?? 0) - + _accessListDecoder.GetLength(item.AccessList, RlpBehaviors.None); - } - - private int GetEip1559ContentLength(T item) - { - return Rlp.LengthOf(item.Nonce) - + Rlp.LengthOf(item.GasPrice) // gas premium - + Rlp.LengthOf(item.DecodedMaxFeePerGas) - + Rlp.LengthOf(item.GasLimit) - + Rlp.LengthOf(item.To) - + Rlp.LengthOf(item.Value) - + Rlp.LengthOf(item.Data) - + Rlp.LengthOf(item.ChainId ?? 0) - + _accessListDecoder.GetLength(item.AccessList, RlpBehaviors.None); - } - - private int GetShardBlobContentLength(T item) - { - return Rlp.LengthOf(item.Nonce) - + Rlp.LengthOf(item.GasPrice) // gas premium - + Rlp.LengthOf(item.DecodedMaxFeePerGas) - + Rlp.LengthOf(item.GasLimit) - + Rlp.LengthOf(item.To) - + Rlp.LengthOf(item.Value) - + Rlp.LengthOf(item.Data) - + Rlp.LengthOf(item.ChainId ?? 0) - + _accessListDecoder.GetLength(item.AccessList, RlpBehaviors.None) - + Rlp.LengthOf(item.MaxFeePerBlobGas) - + Rlp.LengthOf(item.BlobVersionedHashes); - } - - private static int GetShardBlobNetworkWrapperContentLength(T item, int txContentLength) - { - ShardBlobNetworkWrapper networkWrapper = item.NetworkWrapper as ShardBlobNetworkWrapper; - return Rlp.LengthOfSequence(txContentLength) - + Rlp.LengthOf(networkWrapper.Blobs) - + Rlp.LengthOf(networkWrapper.Commitments) - + Rlp.LengthOf(networkWrapper.Proofs); - } - - private static int GetDepositTxContentLength(T item) - { - return Rlp.LengthOf(item.SourceHash) - + Rlp.LengthOf(item.SenderAddress) - + Rlp.LengthOf(item.To) - + Rlp.LengthOf(item.Mint) - + Rlp.LengthOf(item.Value) - + Rlp.LengthOf(item.GasLimit) - + Rlp.LengthOf(item.IsOPSystemTransaction) - + Rlp.LengthOf(item.Data); - } - - private int GetContentLength(T item, bool forSigning, bool isEip155Enabled = false, ulong chainId = 0, - bool withNetworkWrapper = false) - { - bool includeSigChainIdHack = isEip155Enabled && chainId != 0 && item.Type == TxType.Legacy; - int contentLength = 0; - switch (item.Type) - { - case TxType.Legacy: - contentLength = TxDecoder.GetLegacyContentLength(item); - break; - case TxType.AccessList: - contentLength = GetAccessListContentLength(item); - break; - case TxType.EIP1559: - contentLength = GetEip1559ContentLength(item); - break; - case TxType.Blob: - contentLength = GetShardBlobContentLength(item); - break; - case TxType.DepositTx: - contentLength = TxDecoder.GetDepositTxContentLength(item); - break; - } - - contentLength += GetSignatureContentLength(item, forSigning, chainId, includeSigChainIdHack); - - if (withNetworkWrapper) - { - if (item.Type == TxType.Blob) - { - contentLength = TxDecoder.GetShardBlobNetworkWrapperContentLength(item, contentLength); - } - } - return contentLength; - } + GetDecoder(txType).Decode(ref Unsafe.As(ref transaction), txSequenceStart, transactionSequence, ref decoderContext, rlpBehaviors); + } - private static int GetSignatureContentLength(T item, bool forSigning, ulong chainId, bool includeSigChainIdHack) - { - if (item.Type == TxType.DepositTx) - return 0; + public Rlp Encode(T item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + RlpStream rlpStream = new(GetLength(item, rlpBehaviors)); + Encode(rlpStream, item, rlpBehaviors); + return new Rlp(rlpStream.Data.ToArray() ?? []); + } - int contentLength = 0; + public void Encode(RlpStream stream, T? item, RlpBehaviors rlpBehaviors = RlpBehaviors.None) + { + EncodeTx(stream, item, rlpBehaviors, forSigning: false, isEip155Enabled: false, chainId: 0); + } - if (forSigning) - { - if (includeSigChainIdHack) - { - contentLength += Rlp.LengthOf(chainId); - contentLength += 1; - contentLength += 1; - } - } - else - { - bool signatureIsNull = item.Signature is null; - contentLength += signatureIsNull ? 1 : Rlp.LengthOf(item.Type == TxType.Legacy ? item.Signature.V : item.Signature.RecoveryId); - contentLength += signatureIsNull ? 1 : Rlp.LengthOf(item.Signature.RAsSpan.WithoutLeadingZeros()); - contentLength += signatureIsNull ? 1 : Rlp.LengthOf(item.Signature.SAsSpan.WithoutLeadingZeros()); - } + public Rlp EncodeTx(T? item, RlpBehaviors rlpBehaviors = RlpBehaviors.None, bool forSigning = false, bool isEip155Enabled = false, ulong chainId = 0) + { + RlpStream rlpStream = new(GetLength(item, rlpBehaviors, forSigning, isEip155Enabled, chainId)); + EncodeTx(rlpStream, item, rlpBehaviors, forSigning, isEip155Enabled, chainId); + return new Rlp(rlpStream.Data.ToArray() ?? []); + } - return contentLength; - } + /// + /// https://eips.ethereum.org/EIPS/eip-2718 + /// + public int GetLength(T tx, RlpBehaviors rlpBehaviors) => GetLength(tx, rlpBehaviors, forSigning: false, isEip155Enabled: false, chainId: 0); - /// - /// https://eips.ethereum.org/EIPS/eip-2718 - /// - public int GetLength(T tx, RlpBehaviors rlpBehaviors) + private void EncodeTx(RlpStream stream, T? item, RlpBehaviors rlpBehaviors, bool forSigning, bool isEip155Enabled, ulong chainId) + { + if (item is null) { - int txContentLength = GetContentLength(tx, false, - withNetworkWrapper: (rlpBehaviors & RlpBehaviors.InMempoolForm) == RlpBehaviors.InMempoolForm); - int txPayloadLength = Rlp.LengthOfSequence(txContentLength); - - bool isForTxRoot = (rlpBehaviors & RlpBehaviors.SkipTypedWrapping) == RlpBehaviors.SkipTypedWrapping; - int result = tx.Type != TxType.Legacy - ? isForTxRoot - ? (1 + txPayloadLength) - : Rlp.LengthOfSequence(1 + txPayloadLength) // Rlp(TransactionType || TransactionPayload) - : txPayloadLength; - return result; + stream.WriteByte(Rlp.NullObjectByte); + return; } - private int GetTxLength(T tx, RlpBehaviors rlpBehaviors, bool forSigning = false, bool isEip155Enabled = false, - ulong chainId = 0) - { - int txContentLength = GetContentLength(tx, forSigning, isEip155Enabled, chainId, - (rlpBehaviors & RlpBehaviors.InMempoolForm) == RlpBehaviors.InMempoolForm); - int txPayloadLength = Rlp.LengthOfSequence(txContentLength); - - bool isForTxRoot = (rlpBehaviors & RlpBehaviors.SkipTypedWrapping) == RlpBehaviors.SkipTypedWrapping; - int result = tx.Type != TxType.Legacy - ? isForTxRoot - ? (1 + txPayloadLength) - : Rlp.LengthOfSequence(1 + txPayloadLength) // Rlp(TransactionType || TransactionPayload) - : txPayloadLength; - return result; - } + GetDecoder(item.Type).Encode(item, stream, rlpBehaviors, forSigning, isEip155Enabled, chainId); } + + private int GetLength(T? tx, RlpBehaviors rlpBehaviors, bool forSigning, bool isEip155Enabled, ulong chainId) => + tx is null ? Rlp.LengthOfNull : GetDecoder(tx.Type).GetLength(tx, rlpBehaviors, forSigning, isEip155Enabled, chainId); } diff --git a/src/Nethermind/Nethermind.State/Proofs/TxTrie.cs b/src/Nethermind/Nethermind.State/Proofs/TxTrie.cs index f17a955bad4..d0858baf54c 100644 --- a/src/Nethermind/Nethermind.State/Proofs/TxTrie.cs +++ b/src/Nethermind/Nethermind.State/Proofs/TxTrie.cs @@ -16,7 +16,7 @@ namespace Nethermind.State.Proofs; /// public class TxTrie : PatriciaTrie { - private static readonly TxDecoder _txDecoder = new(); + private static readonly TxDecoder _txDecoder = TxDecoder.Instance; /// /// The transactions to build the trie of. diff --git a/src/Nethermind/Nethermind.TxPool/BlobTxStorage.cs b/src/Nethermind/Nethermind.TxPool/BlobTxStorage.cs index e05f726d6e1..1c231f52acf 100644 --- a/src/Nethermind/Nethermind.TxPool/BlobTxStorage.cs +++ b/src/Nethermind/Nethermind.TxPool/BlobTxStorage.cs @@ -16,7 +16,7 @@ namespace Nethermind.TxPool; public class BlobTxStorage : IBlobTxStorage { - private static readonly TxDecoder _txDecoder = new(); + private static readonly TxDecoder _txDecoder = TxDecoder.Instance; private readonly IDb _fullBlobTxsDb; private readonly IDb _lightBlobTxsDb; private readonly IDb _processedBlobTxsDb; diff --git a/src/Nethermind/Nethermind.TxPool/TransactionExtensions.cs b/src/Nethermind/Nethermind.TxPool/TransactionExtensions.cs index 65102975786..44598b63e01 100644 --- a/src/Nethermind/Nethermind.TxPool/TransactionExtensions.cs +++ b/src/Nethermind/Nethermind.TxPool/TransactionExtensions.cs @@ -14,7 +14,7 @@ namespace Nethermind.TxPool public static class TransactionExtensions { private static readonly long MaxSizeOfTxForBroadcast = 4.KiB(); //4KB, as in Geth https://github.com/ethereum/go-ethereum/pull/27618 - private static readonly ITransactionSizeCalculator _transactionSizeCalculator = new NetworkTransactionSizeCalculator(new TxDecoder()); + private static readonly ITransactionSizeCalculator _transactionSizeCalculator = new NetworkTransactionSizeCalculator(TxDecoder.Instance); public static int GetLength(this Transaction tx) {