Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/javascript tracers #6217

Merged
merged 102 commits into from
Dec 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
772b402
review
eliashezron Sep 13, 2023
56fc27d
changes
eliashezron Sep 14, 2023
2be49d2
writing tests
eliashezron Sep 14, 2023
6a150ff
Import proper packages for all platforms
LukaszRozmej Sep 15, 2023
56c3092
Refactoring
LukaszRozmej Sep 15, 2023
a3678d8
Add built-in tracers
LukaszRozmej Sep 15, 2023
3956052
Hook-up (with incorrect serialization for now!) javascript tracer to …
LukaszRozmej Sep 15, 2023
5322bb9
included more tests
eliashezron Sep 15, 2023
fcc5a95
testing builtings
eliashezron Sep 29, 2023
b279b8b
Fix loading built-in tracers from files.
LukaszRozmej Sep 29, 2023
9bd8c46
merge feature/jstracer
eliashezron Sep 30, 2023
698072e
Merge remote-tracking branch 'upstream/feature/javascript_tracers' in…
eliashezron Sep 30, 2023
85ff11b
Writing tests.
eliashezron Oct 3, 2023
b723e70
builtIns-implementation
eliashezron Oct 5, 2023
df7f8fb
More conversions
LukaszRozmej Oct 5, 2023
290622a
ready for debug 4byteTracer, Evmdis Tracer, PrestateTrace and Call tr…
eliashezron Oct 12, 2023
f4d3a69
fix test submodule
LukaszRozmej Oct 12, 2023
ef57a8f
fixes
LukaszRozmej Oct 12, 2023
10f3cf8
changes to peek()
eliashezron Oct 13, 2023
6b84caa
fix test
LukaszRozmej Oct 13, 2023
05f4bda
further changes
LukaszRozmej Oct 13, 2023
995a827
more changes
LukaszRozmej Oct 13, 2023
0d5d6e6
fix getValue in GethJavascriptStyleLog
LukaszRozmej Oct 13, 2023
ddc4e37
More fixes
LukaszRozmej Oct 16, 2023
781904a
fix null
LukaszRozmej Oct 17, 2023
ab703a5
partial address and stack refactoring
LukaszRozmej Oct 17, 2023
b6d0dc5
Improve stack access, fix ctx.to for contracts
LukaszRozmej Oct 20, 2023
8cb3ff0
move to TraceMemory
LukaszRozmej Oct 20, 2023
fedb565
fix BigInteger by using latest js code
LukaszRozmej Oct 20, 2023
4865f19
more improvements
LukaszRozmej Oct 20, 2023
5d452a7
more changes to make it inline with spec
LukaszRozmej Oct 24, 2023
97faddc
fix Ethereum Tests
LukaszRozmej Oct 25, 2023
67849cf
Merge remote-tracking branch 'origin/master' into feature/javascript_…
LukaszRozmej Oct 30, 2023
c3da94e
fix merge
LukaszRozmej Oct 30, 2023
b64cd75
Changes and bugfixes
LukaszRozmej Nov 2, 2023
c9f3201
Merge remote-tracking branch 'origin/master' into feature/javascript_…
LukaszRozmej Nov 2, 2023
52d9a93
Merge branch 'feature/javascript_tracers' of https://github.com/Nethe…
LukaszRozmej Nov 2, 2023
6ae8bd7
Make stack, memory and contract not nullable
LukaszRozmej Nov 2, 2023
8b3ef89
Move BigIntegerJS to own class
LukaszRozmej Nov 2, 2023
f2684b4
Properly hook up Enter & Exit
LukaszRozmej Nov 2, 2023
7ca1f38
Refactors
LukaszRozmej Nov 2, 2023
258d8d8
simplify conversions
LukaszRozmej Nov 2, 2023
de4bfb4
Move to type arrays
LukaszRozmej Nov 2, 2023
2c7a3c0
Move to MemoryTrace for simplicity
LukaszRozmej Nov 2, 2023
2df4a79
Call fault on actual errors.
LukaszRozmej Nov 3, 2023
5d805a6
Fix context and lazy parameters
LukaszRozmej Nov 3, 2023
f3ab771
fix memory slice
LukaszRozmej Nov 3, 2023
56da2a6
Make GethLikeTxTrace disposable
LukaszRozmej Nov 3, 2023
268d978
fix NullReferenceException
LukaszRozmej Nov 3, 2023
1ef1d21
Refactor Engine.cs
LukaszRozmej Nov 6, 2023
1eb0b26
Fix db.exist param
LukaszRozmej Nov 6, 2023
cbb2ab2
Move to V8Runtime
LukaszRozmej Nov 7, 2023
7ed1690
Bunch of fixes
LukaszRozmej Nov 7, 2023
9c66bfb
fix slice
LukaszRozmej Nov 8, 2023
796595f
fix BigInteger marshaling
LukaszRozmej Nov 8, 2023
ffc6836
Add postStep function
LukaszRozmej Nov 9, 2023
04e8536
more tests
LukaszRozmej Nov 9, 2023
b7cb292
fix gasPrice and gas
LukaszRozmej Nov 10, 2023
4b53a27
Make error in frameresult undefined if needed
LukaszRozmej Nov 10, 2023
ce3baf0
Force serialization of numerals to raw
LukaszRozmej Nov 13, 2023
f6776fd
clear refund before invoking step
LukaszRozmej Nov 13, 2023
935898c
Populate context more in BlockTracer based on transaction
LukaszRozmej Nov 13, 2023
e6346b9
fix FrameResult.GasUsed
LukaszRozmej Nov 13, 2023
a913085
Merge remote-tracking branch 'origin/master' into feature/javascript_…
LukaszRozmej Nov 13, 2023
f458331
fixes
LukaszRozmej Nov 13, 2023
60cca73
fix gasCost
LukaszRozmej Nov 14, 2023
6084fc2
Remove undefined errors
LukaszRozmej Nov 14, 2023
ba7916e
fix contracts
LukaszRozmej Nov 14, 2023
6f387f8
Fix wrong values cached in CallFrame & FrameResult
LukaszRozmej Nov 14, 2023
481c5b4
fix refund as accumulator
LukaszRozmej Nov 14, 2023
e69242b
fix tests
LukaszRozmej Nov 14, 2023
5386df0
ignore value in STATICCALL
LukaszRozmej Nov 14, 2023
9615dd0
Allow to access storage slots by hash
LukaszRozmej Nov 15, 2023
8c132dc
fix contract stack
LukaszRozmej Nov 15, 2023
09167c7
exclude "Data" folder
LukaszRozmej Nov 15, 2023
8f563b7
fix delegate call
LukaszRozmej Nov 16, 2023
523a874
fix clearing state array
LukaszRozmej Nov 17, 2023
70dedc9
review fixes
LukaszRozmej Nov 17, 2023
b6ceb80
fix
LukaszRozmej Nov 17, 2023
a96a446
StorageCell optimizations
LukaszRozmej Nov 17, 2023
a8fb14c
Handle depth for create transactions
LukaszRozmej Nov 20, 2023
2255513
fix DELEGATECALL again
LukaszRozmej Nov 20, 2023
a9d6f5e
fix
LukaszRozmej Nov 20, 2023
047fa11
Allign errors more with geth
LukaszRozmej Nov 23, 2023
06c9b23
fix contract address for DELEGATECALL and STATICCALL
LukaszRozmej Nov 24, 2023
370935d
Remove traces from test results
LukaszRozmej Nov 24, 2023
69ac963
fix ToContract2
LukaszRozmej Nov 24, 2023
8242087
fix static call
LukaszRozmej Nov 27, 2023
a87f488
cosmetic cleanup noticced during review
MarekM25 Nov 27, 2023
5cd0a43
Merge branch 'feature/javascript_tracers' of https://github.com/nethe…
MarekM25 Nov 27, 2023
334fa1b
Fix revert gas and output
LukaszRozmej Nov 28, 2023
d6909fc
Merge remote-tracking branch 'origin/feature/javascript_tracers' into…
LukaszRozmej Nov 28, 2023
3b6bbe2
rename Javascript -> JavaScript
LukaszRozmej Nov 28, 2023
eaafdb3
Change license headers on tracer and link to go-ethereum
LukaszRozmej Nov 28, 2023
d741617
Add comments
LukaszRozmej Nov 28, 2023
8b466d1
Temporary rename
rubo Nov 28, 2023
b61aff0
Rename `Javascript` to `JavaScript`
rubo Nov 28, 2023
eeb110e
Replace .js files with originals
rubo Nov 28, 2023
1b7b90b
Moving BigIntegerJavaScript to seperate file
LukaszRozmej Nov 28, 2023
4650347
Merge remote-tracking branch 'origin/feature/javascript_tracers' into…
LukaszRozmej Nov 28, 2023
9ce7539
Refactor and include .js files in the publishing
rubo Nov 28, 2023
82cd25a
Merge remote-tracking branch 'origin/master' into feature/javascript_…
LukaszRozmej Nov 30, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion scripts/deployment/build-runner.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ do
-p:IncludeAllContentForSelfExtract=true \
-p:PublishSingleFile=true

cp -r configs $output_path/$rid
mkdir $output_path/$rid/keystore

# A temporary symlink for Linux and macOS to support existing scripts if any
Expand Down
6 changes: 6 additions & 0 deletions src/Nethermind/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Extensions" Version="7.0.9" />
<PackageVersion Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageVersion Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
<PackageVersion Include="Microsoft.ClearScript.V8" Version="7.4.3" />
<PackageVersion Include="Microsoft.ClearScript.V8.Native.linux-arm64" Version="7.4.3" />
<PackageVersion Include="Microsoft.ClearScript.V8.Native.linux-x64" Version="7.4.3" />
<PackageVersion Include="Microsoft.ClearScript.V8.Native.osx-arm64" Version="7.4.3" />
<PackageVersion Include="Microsoft.ClearScript.V8.Native.osx-x64" Version="7.4.3" />
<PackageVersion Include="Microsoft.ClearScript.V8.Native.win-x64" Version="7.4.3" />
<PackageVersion Include="Microsoft.Extensions.CommandLineUtils" Version="1.1.1" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="7.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="7.0.0" />
Expand Down
3 changes: 2 additions & 1 deletion src/Nethermind/Ethereum.Blockchain.Test/MetaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ public class MetaTests
"runtimes",
"ref",
"TestFiles",
"Blockhash"
"Blockhash",
"Data"
};

[Test]
Expand Down
10 changes: 4 additions & 6 deletions src/Nethermind/Nethermind.Consensus/Processing/BlockProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -314,12 +314,10 @@ private void ApplyMinerRewards(Block block, IBlockTracer tracer, IReleaseSpec sp
{
BlockReward reward = rewards[i];

ITxTracer txTracer = NullTxTracer.Instance;
if (tracer.IsTracingRewards)
{
// we need this tracer to be able to track any potential miner account creation
txTracer = tracer.StartNewTxTrace(null);
}
using ITxTracer txTracer = tracer.IsTracingRewards
? // we need this tracer to be able to track any potential miner account creation
tracer.StartNewTxTrace(null)
: NullTxTracer.Instance;

ApplyMinerReward(block, reward, spec);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ public static void ProcessTransaction(this ITransactionProcessorAdapter transact
{
if (processingOptions.ContainsFlag(ProcessingOptions.DoNotVerifyNonce))
{
currentTx.Nonce = stateProvider.GetNonce(currentTx.SenderAddress);
currentTx.Nonce = stateProvider.GetNonce(currentTx.SenderAddress!);
}

receiptsTracer.StartNewTxTrace(currentTx);
using ITxTracer tracer = receiptsTracer.StartNewTxTrace(currentTx);
transactionProcessor.Execute(currentTx, blkCtx, receiptsTracer);
receiptsTracer.EndTxTrace();
}
Expand Down
32 changes: 23 additions & 9 deletions src/Nethermind/Nethermind.Consensus/Tracing/GethStyleTracer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,40 @@
using Nethermind.Consensus.Processing;
using Nethermind.Core;
using Nethermind.Core.Crypto;
using Nethermind.Core.Specs;
using Nethermind.Crypto;
using Nethermind.Evm.Tracing;
using Nethermind.Evm.Tracing.GethStyle;
using Nethermind.Evm.Tracing.GethStyle.JavaScript;
using Nethermind.Evm.TransactionProcessing;
using Nethermind.Serialization.Rlp;
using Nethermind.State;

namespace Nethermind.Consensus.Tracing;

public class GethStyleTracer : IGethStyleTracer
{
private readonly IBlockTree _blockTree;
private readonly ISpecProvider _specProvider;
private readonly ChangeableTransactionProcessorAdapter _transactionProcessorAdapter;
private readonly IBlockchainProcessor _processor;
private readonly IWorldState _worldState;
private readonly IReceiptStorage _receiptStorage;
private readonly IFileSystem _fileSystem;

public GethStyleTracer(
IBlockchainProcessor processor,
public GethStyleTracer(IBlockchainProcessor processor,
IWorldState worldState,
IReceiptStorage receiptStorage,
IBlockTree blockTree,
ISpecProvider specProvider,
ChangeableTransactionProcessorAdapter transactionProcessorAdapter,
IFileSystem fileSystem)
{
_processor = processor ?? throw new ArgumentNullException(nameof(processor));
_worldState = worldState;
_receiptStorage = receiptStorage ?? throw new ArgumentNullException(nameof(receiptStorage));
_blockTree = blockTree ?? throw new ArgumentNullException(nameof(blockTree));
_specProvider = specProvider;
_transactionProcessorAdapter = transactionProcessorAdapter;
_fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
}
Expand Down Expand Up @@ -110,18 +118,19 @@ public GethLikeTxTrace Trace(Hash256 blockHash, int txIndex, GethTraceOptions op
if (tx.Hash is null) throw new InvalidOperationException("Cannot trace transactions without tx hash set.");

block = block.WithReplacedBodyCloned(BlockBody.WithOneTransactionOnly(tx));
GethLikeBlockMemoryTracer blockTracer = new(options with { TxHash = tx.Hash });
IBlockTracer<GethLikeTxTrace> blockTracer = CreateOptionsTracer(block.Header, options with { TxHash = tx.Hash });
_processor.Process(block, ProcessingOptions.Trace, blockTracer.WithCancellation(cancellationToken));
return blockTracer.BuildResult().SingleOrDefault();
}
public GethLikeTxTrace[] TraceBlock(BlockParameter blockParameter, GethTraceOptions options, CancellationToken cancellationToken)

public IReadOnlyCollection<GethLikeTxTrace> TraceBlock(BlockParameter blockParameter, GethTraceOptions options, CancellationToken cancellationToken)
{
var block = _blockTree.FindBlock(blockParameter);

return TraceBlock(block, options, cancellationToken);
}

public GethLikeTxTrace[] TraceBlock(Rlp blockRlp, GethTraceOptions options, CancellationToken cancellationToken)
public IReadOnlyCollection<GethLikeTxTrace> TraceBlock(Rlp blockRlp, GethTraceOptions options, CancellationToken cancellationToken)
{
return TraceBlock(GetBlockToTrace(blockRlp), options, cancellationToken);
}
Expand Down Expand Up @@ -152,14 +161,19 @@ public IEnumerable<string> TraceBlockToFile(Hash256 blockHash, GethTraceOptions
{
ArgumentNullException.ThrowIfNull(txHash);

var tracer = new GethLikeBlockMemoryTracer(options with { TxHash = txHash });
IBlockTracer<GethLikeTxTrace> tracer = CreateOptionsTracer(block.Header, options with { TxHash = txHash });

_processor.Process(block, ProcessingOptions.Trace, tracer.WithCancellation(cancellationToken));

return tracer.BuildResult().SingleOrDefault();
}

private GethLikeTxTrace[] TraceBlock(Block? block, GethTraceOptions options, CancellationToken cancellationToken)
private IBlockTracer<GethLikeTxTrace> CreateOptionsTracer(BlockHeader block, GethTraceOptions options) =>
!string.IsNullOrEmpty(options.Tracer)
? new GethLikeBlockJavaScriptTracer(_worldState, _specProvider.GetSpec(block), options)
: new GethLikeBlockMemoryTracer(options);

private IReadOnlyCollection<GethLikeTxTrace> TraceBlock(Block? block, GethTraceOptions options, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(block);

Expand All @@ -174,9 +188,9 @@ private GethLikeTxTrace[] TraceBlock(Block? block, GethTraceOptions options, Can
if (!_blockTree.IsMainChain(parent.Hash)) throw new InvalidOperationException("Cannot trace orphaned blocks");
}

GethLikeBlockMemoryTracer tracer = new(options);
IBlockTracer<GethLikeTxTrace> tracer = CreateOptionsTracer(block.Header, options);
_processor.Process(block, ProcessingOptions.Trace, tracer.WithCancellation(cancellationToken));
return tracer.BuildResult().ToArray();
return tracer.BuildResult();
}

private static Block GetBlockToTrace(Rlp blockRlp)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public interface IGethStyleTracer
GethLikeTxTrace Trace(Hash256 blockHash, int txIndex, GethTraceOptions options, CancellationToken cancellationToken);
GethLikeTxTrace Trace(Rlp blockRlp, Hash256 txHash, GethTraceOptions options, CancellationToken cancellationToken);
GethLikeTxTrace? Trace(BlockParameter blockParameter, Transaction tx, GethTraceOptions options, CancellationToken cancellationToken);
GethLikeTxTrace[] TraceBlock(BlockParameter blockParameter, GethTraceOptions options, CancellationToken cancellationToken);
GethLikeTxTrace[] TraceBlock(Rlp blockRlp, GethTraceOptions options, CancellationToken cancellationToken);
IReadOnlyCollection<GethLikeTxTrace> TraceBlock(BlockParameter blockParameter, GethTraceOptions options, CancellationToken cancellationToken);
IReadOnlyCollection<GethLikeTxTrace> TraceBlock(Rlp blockRlp, GethTraceOptions options, CancellationToken cancellationToken);
IEnumerable<string> TraceBlockToFile(Hash256 blockHash, GethTraceOptions options, CancellationToken cancellationToken);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class UInt256ConverterTests : ConverterTestBase<UInt256>
{
[TestCase(NumberConversion.Hex)]
[TestCase(NumberConversion.Decimal)]
[TestCase(NumberConversion.Raw)]
public void Test_roundtrip(NumberConversion numberConversion)
{
UInt256Converter converter = new(numberConversion);
Expand All @@ -25,8 +26,7 @@ public void Test_roundtrip(NumberConversion numberConversion)
}

[TestCase((NumberConversion)99)]
[TestCase(NumberConversion.Raw)]
public void Raw_not_supported(NumberConversion notSupportedConversion)
public void Undefined_not_supported(NumberConversion notSupportedConversion)
{
UInt256Converter converter = new(notSupportedConversion);
Assert.Throws<NotSupportedException>(
Expand Down
24 changes: 23 additions & 1 deletion src/Nethermind/Nethermind.Core/Address.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,17 @@ public static bool IsValidAddress(string hexString, bool allowPrefix)

public Address(string hexString) : this(Extensions.Bytes.FromHexString(hexString)) { }

/// <summary>
/// Parses string value to Address. String has to be exactly 20 bytes long.
/// </summary>
public static bool TryParse(string? value, out Address? address)
{
if (value is not null)
{
try
{
byte[] bytes = Extensions.Bytes.FromHexString(value);
if (bytes?.Length == Size)
if (bytes.Length == Size)
{
address = new Address(bytes);
return true;
Expand All @@ -81,6 +84,25 @@ public static bool TryParse(string? value, out Address? address)
return false;
}

/// <summary>
/// Parses string value to Address. String can be shorter than 20 bytes long, it is padded with leading 0's then.
/// </summary>
public static bool TryParseVariableLength(string? value, out Address? address)
LukaszRozmej marked this conversation as resolved.
Show resolved Hide resolved
{
if (value is not null)
{
try
{
address = new Address(Extensions.Bytes.FromHexString(value, Size));
return true;
}
catch (IndexOutOfRangeException) { }
}

address = default;
return false;
}

public Address(byte[] bytes)
{
if (bytes is null)
Expand Down
85 changes: 59 additions & 26 deletions src/Nethermind/Nethermind.Core/Extensions/Bytes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -685,24 +685,24 @@ private static string ByteArrayToHexViaLookup32(byte[] bytes, bool withZeroX, bo
return withEip55Checksum
? ByteArrayToHexViaLookup32Checksum(length, stateToPass)
: string.Create(length, stateToPass, static (chars, state) =>
{
int skip = state.LeadingZeros / 2;
byte[] bytes = state.Bytes;
if (bytes.Length == 0)
{
if (state.WithZeroX)
int skip = state.LeadingZeros / 2;
byte[] bytes = state.Bytes;
if (bytes.Length == 0)
{
chars[1] = 'x';
chars[0] = '0';
}
if (state.WithZeroX)
{
chars[1] = 'x';
chars[0] = '0';
}

return;
}
return;
}

ref byte input = ref Unsafe.Add(ref bytes[0], skip);
ref char charsRef = ref MemoryMarshal.GetReference(chars);
OutputBytesToCharHex(ref input, state.Bytes.Length, ref charsRef, state.WithZeroX, state.LeadingZeros);
});
ref byte input = ref Unsafe.Add(ref bytes[0], skip);
ref char charsRef = ref MemoryMarshal.GetReference(chars);
OutputBytesToCharHex(ref input, state.Bytes.Length, ref charsRef, state.WithZeroX, state.LeadingZeros);
});
}

internal static void OutputBytesToCharHex(ref byte input, int length, ref char charsRef, bool withZeroX, int leadingZeros)
Expand Down Expand Up @@ -737,9 +737,9 @@ internal static void OutputBytesToCharHex(ref byte input, int length, ref char c
0xFF, 0xFF, 2, 0xFF, 0xFF, 0xFF, 3, 0xFF);

Vector128<byte> asciiTable = Vector128.Create((byte)'0', (byte)'1', (byte)'2', (byte)'3',
(byte)'4', (byte)'5', (byte)'6', (byte)'7',
(byte)'8', (byte)'9', (byte)'a', (byte)'b',
(byte)'c', (byte)'d', (byte)'e', (byte)'f');
(byte)'4', (byte)'5', (byte)'6', (byte)'7',
(byte)'8', (byte)'9', (byte)'a', (byte)'b',
(byte)'c', (byte)'d', (byte)'e', (byte)'f');

nuint pos = 0;
Debug.Assert(toProcess >= 4);
Expand Down Expand Up @@ -768,6 +768,7 @@ static Vector128<byte> Shuffle(Vector128<byte> value, Vector128<byte> mask)
{
ThrowHelper.ThrowNotSupportedException();
}

return AdvSimd.Arm64.VectorTableLookup(value, mask);
}

Expand All @@ -777,9 +778,7 @@ static Vector128<byte> Shuffle(Vector128<byte> value, Vector128<byte> mask)

// ExtractVector128 is not entirely the same as ShiftRightLogical128BitLane, but it works here since
// first two bytes in lowNibbles are guaranteed to be zeros
Vector128<byte> shifted = Sse2.IsSupported ?
Sse2.ShiftRightLogical128BitLane(lowNibbles, 2) :
AdvSimd.ExtractVector128(lowNibbles, lowNibbles, 2);
Vector128<byte> shifted = Sse2.IsSupported ? Sse2.ShiftRightLogical128BitLane(lowNibbles, 2) : AdvSimd.ExtractVector128(lowNibbles, lowNibbles, 2);

Vector128<byte> highNibbles = Vector128.ShiftRightLogical(shifted.AsInt32(), 4).AsByte();

Expand All @@ -803,7 +802,6 @@ static Vector128<byte> Shuffle(Vector128<byte> value, Vector128<byte> mask)
{
pos = lengthSubVector128;
}

} while (true);
}
else
Expand Down Expand Up @@ -934,23 +932,58 @@ public static byte[] FromUtf8HexString(ReadOnlySpan<byte> hexString)
}

[DebuggerStepThrough]
public static byte[] FromHexString(string hexString)
public static byte[] FromHexString(string hexString, int length) =>
hexString is null ? throw new ArgumentNullException(nameof(hexString)) : FromHexString(hexString.AsSpan(), length);

[DebuggerStepThrough]
private static byte[] FromHexString(ReadOnlySpan<char> hexString, int length)
{
if (hexString is null)
int start = hexString is ['0', 'x', ..] ? 2 : 0;
ReadOnlySpan<char> chars = hexString.Slice(start);

if (chars.Length == 0)
{
throw new ArgumentNullException(nameof(hexString));
return Array.Empty<byte>();
}

int oddMod = hexString.Length % 2;
int actualLength = (chars.Length >> 1) + oddMod;
byte[] result = GC.AllocateArray<byte>(length);
Span<byte> writeToSpan = result.AsSpan(length - actualLength);

bool isSuccess;
if (oddMod == 0 &&
BitConverter.IsLittleEndian && (Ssse3.IsSupported || AdvSimd.Arm64.IsSupported) &&
chars.Length >= Vector128<ushort>.Count * 2)
{
isSuccess = HexConverter.TryDecodeFromUtf16_Vector128(chars, writeToSpan);
}
else
{
isSuccess = HexConverter.TryDecodeFromUtf16(chars, writeToSpan, oddMod == 1);
}

return isSuccess ? result : throw new FormatException("Incorrect hex string");
}

[DebuggerStepThrough]
public static byte[] FromHexString(string hexString) =>
hexString is null ? throw new ArgumentNullException(nameof(hexString)) : FromHexString(hexString.AsSpan());

[DebuggerStepThrough]
private static byte[] FromHexString(ReadOnlySpan<char> hexString)
{
int start = hexString is ['0', 'x', ..] ? 2 : 0;
ReadOnlySpan<char> chars = hexString.AsSpan(start);
ReadOnlySpan<char> chars = hexString.Slice(start);

if (chars.Length == 0)
{
return Array.Empty<byte>();
}

int oddMod = hexString.Length % 2;
byte[] result = GC.AllocateUninitializedArray<byte>((chars.Length >> 1) + oddMod);
int actualLength = (chars.Length >> 1) + oddMod;
byte[] result = GC.AllocateUninitializedArray<byte>(actualLength);

bool isSuccess;
if (oddMod == 0 &&
Expand Down
5 changes: 5 additions & 0 deletions src/Nethermind/Nethermind.Core/Extensions/SpanExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ private static string ToHexViaLookup(ReadOnlySpan<byte> bytes, bool withZeroX, b
int leadingZeros = skipLeadingZeros ? Bytes.CountLeadingZeros(bytes) : 0;
int length = bytes.Length * 2 + (withZeroX ? 2 : 0) - leadingZeros;

if (skipLeadingZeros && length == (withZeroX ? 2 : 0))
{
return withZeroX ? "0x0" : "0";
}

char[] charArray = ArrayPool<char>.Shared.Rent(length);

ref byte input = ref Unsafe.Add(ref MemoryMarshal.GetReference(bytes), leadingZeros / 2);
Expand Down
Loading
Loading