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

Merkle Node Keccak #104

Merged
merged 8 commits into from
Jun 28, 2023
Merged

Merkle Node Keccak #104

merged 8 commits into from
Jun 28, 2023

Conversation

emlautarom1
Copy link
Contributor

Compute the Keccak or RLP of Merkle Nodes (Branches, Leaves and Extensions)


namespace Paprika.RLP;

public static class Rlp
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are multiple "magic numbers" in the class. We should extract them into constants and document the reason for their existence (spec, optimization, etc.)

Comment on lines +98 to +102
// TODO: If keccak is a known one like `Keccak.OfAnEmptyString` or `Keccak.OfAnEmptySequenceRlp`
// we can cache those `Rlp`s to be reused

WriteByte(160);
Write(keccak.BytesAsSpan);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optimization opportunity by reusing results on known inputs.

@Scooletz Scooletz mentioned this pull request Jun 26, 2023
10 tasks
{
new object[]
{
Values.Key0, Values.Balance0, Values.Nonce0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be super useful if you could provide a link to a branch in NethermindEth maybe where you calculate these. Once they are merged, it will be fine but for now, keeping any kind of link would make the life much easier.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had the tests locally, but I'll create a branch with the actual tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you commit this link to the repository? Otherwise it will get lost. Just add it to the tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually did that but I forgot to push the changes. Whoops!

src/Paprika.Tests/Merkle/RootHashTests.cs Show resolved Hide resolved
@emlautarom1
Copy link
Contributor Author

Removed all allocations with a little help of scoped and manually inlining methods.


namespace Paprika.RLP;

public readonly ref struct KeccakOrRlp
Copy link
Contributor Author

@emlautarom1 emlautarom1 Jun 26, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also tried a different approach that uses a fixed size array instead of a Keccak:

public ref struct KeccakOrRlp
{
    public enum Type : byte
    {
        Keccak = 0,
        Rlp = 1,
    }

    private unsafe ref struct Buffer
    {
        private const int Size = 32;

#pragma warning disable CS0649
        // Compiler cannot figure out that the fields is being used
        private fixed byte _data[Size];
#pragma warning restore CS0649

        public Span<byte> AsSpan()
        {
            fixed (byte* p = _data)
            {
                return new Span<byte>(p, Size);
            }
        }
    }

    public Type DataType { get; }
    private Buffer _buffer;

    public Span<byte> AsSpan() => _buffer.AsSpan();

    private KeccakOrRlp(Type dataType, Buffer buffer)
    {
        DataType = dataType;
        _buffer = buffer;
    }

    public static KeccakOrRlp FromSpan(scoped Span<byte> data)
    {
        var buffer = new Buffer();
        var output = buffer.AsSpan();

        if (data.Length < 32)
        {
            output[0] = (byte)data.Length;
            data.CopyTo(output[1..]);
            return new KeccakOrRlp(Type.Rlp, buffer);
        }
        else
        {
            KeccakHash.ComputeHash(data, output);
            return new KeccakOrRlp(Type.Keccak, buffer);
        }
    }
}

On benchmarks, this approach is as performant as the Keccak implementation, while in my opinion being a bit clearer since we're not using a Keccak as a backing field (which, when we store an Rlp is not even a Keccak).

I don't see any extra copies on IL, but I'm not fully sure about that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should work as you describe.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use this approach then? Or would you rather stick with the Keccak approach?

Copy link
Contributor

@Scooletz Scooletz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One remark and good to merge.

@github-actions
Copy link

Code Coverage

Package Line Rate Branch Rate Health
Paprika 86% 75%
Summary 86% (1871 / 2163) 75% (463 / 614)

Minimum allowed line rate is 75%

@emlautarom1 emlautarom1 dismissed Scooletz’s stale review June 27, 2023 21:26

Change has been made as requested

Copy link
Contributor

@Scooletz Scooletz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to go!

@Scooletz Scooletz merged commit 965ede7 into main Jun 28, 2023
@Scooletz Scooletz deleted the feature/merkle_keccak branch June 28, 2023 07:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants