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/eip 3074 #6944

Open
wants to merge 38 commits into
base: master
Choose a base branch
from
Open

Feature/eip 3074 #6944

wants to merge 38 commits into from

Conversation

ak88
Copy link
Contributor

@ak88 ak88 commented Apr 23, 2024

Note that AUTHCALL gas rules are different than normal CALL

https://eips.ethereum.org/EIPS/eip-3074

image

Changes

Introduces opcodes:
AUTH
AUTHCALL

What types of changes does your code introduce?

  • Bugfix (a non-breaking change that fixes an issue)
  • New feature (a non-breaking change that adds functionality)
  • Breaking change (a change that causes existing functionality not to work as expected)
  • Optimization
  • Refactoring
  • Documentation update
  • Build-related changes
  • Other: Description

Testing

Requires testing

  • Yes
  • No

If yes, did you write tests?

  • Yes
  • No

Remarks

@ak88 ak88 added the wip Work in Progress label Apr 23, 2024
@@ -2095,6 +2104,90 @@ private CallResult ExecuteCall<TTracingInstructions>(EvmState vmState, ReadOnlyM
break;
}
}
case Instruction.AUTH:
Copy link
Member

Choose a reason for hiding this comment

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

This is pretty stack heavy (x3 stackalloc, x3 byte[], several other locals), and fairly large. As stack costs are paid upfront at the method level and the Jit will bail on optimizing if a method gets too big, this will cause knock on effects to all tx whether or not they call AUTH.

Could it instead be moved out to a method InstructionAuth; see InstructionLog, InstructionSLoad, InstructionSStore, InstructionRevert, etc as examples

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes I wanted to refactor it to separate method. Do you think the zero padding and byte[] alloc should be done with stackalloc instead?

Copy link
Member

Choose a reason for hiding this comment

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

Can look at that later, is more the method bloat of ExecuteCode that bothers me since its already vastly long which means the Jit is way passed trying to optimize local variables; so every one added even if just an int causes more space to be reserved and zeroed at the start of the method which adds to every call even if the code that uses that local is never run.

It will get even worse if going to add a try/catch for _ecdsa.RecoverPublicKey. Whereas if you move it to another method then the Jit will optimize that hard since its short and it won't add to the stack clearing and code size of the general dispatch method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

try/catch will not help with _ecdsa.RecoverPublicKey since it will throw a CSE.
Should i use [SkipLocalsInit] flag for the new method you think, or better to let JIT handle zeroing?

src/Nethermind/Nethermind.Evm/VirtualMachine.cs Outdated Show resolved Hide resolved
src/Nethermind/Chains/foundation.json Outdated Show resolved Hide resolved
@@ -48,6 +50,16 @@ public Signature Sign(PrivateKey privateKey, Hash256 message)
return signature;
}

public PublicKey? TryRecoverPublicKey(ReadOnlySpan<byte> r, ReadOnlySpan<byte> s, byte v, Hash256 message)
Copy link
Member

Choose a reason for hiding this comment

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

Not used?

src/Nethermind/Nethermind.Evm/VirtualMachine.cs Outdated Show resolved Hide resolved
Comment on lines 2130 to 2150
Span<byte> sigData = stackalloc byte[64];
Span<byte> commit = stackalloc byte[32];
//Skip init flag is active so we have to iterate all data
for (int i = 0; i < 97; i++)
{
byte data = 0;
if (i < b)
data = memData[i];
switch (i)
{
case 0:
yParity = data;
break;
case >= 1 and <= 64:
sigData[i - 1] = data;
break;
default:
commit[i - 65] = data;
break;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Can you just break up original Span to smaller ones and use https://learn.microsoft.com/en-us/dotnet/api/system.memoryextensions.reverse?view=net-8.0 on them, instead of allocating new ones?

Comment on lines 2154 to 2157
byte[] chainId = _chainId.PadLeft(32);
byte[] authorityNonce = _state.GetNonce(authority).PaddedBytes(32);
//TODO is ExecutingAccount correct when DELEGATECALL and CALLCODE?
byte[] invokerAddress = vmState.Env.ExecutingAccount.Bytes.PadLeft(32);
Copy link
Member

Choose a reason for hiding this comment

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

I think those don't need to be allocated at all, they only should be written to msg Span in correct places.

src/Nethermind/Nethermind.Evm/VirtualMachine.cs Outdated Show resolved Hide resolved
src/Nethermind/Nethermind.Evm/VirtualMachine.cs Outdated Show resolved Hide resolved
src/Nethermind/Nethermind.Evm/VirtualMachine.cs Outdated Show resolved Hide resolved
src/Nethermind/Nethermind.Specs/MainnetSpecProvider.cs Outdated Show resolved Hide resolved
src/Nethermind/Nethermind.Evm/VirtualMachine.cs Outdated Show resolved Hide resolved
@ak88 ak88 marked this pull request as ready for review May 6, 2024 13:29
@ak88 ak88 added Pectra eip and removed wip Work in Progress labels May 6, 2024
@ak88 ak88 requested a review from MarekM25 May 6, 2024 14:23
Comment on lines +2192 to +2193
if (!stack.PopUInt256(out UInt256 offset)) return EvmExceptionType.StackUnderflow;
if (!stack.PopUInt256(out UInt256 length)) return EvmExceptionType.StackUnderflow;
Copy link
Member

Choose a reason for hiding this comment

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

We have

        SkipInit(out UInt256 a);
        SkipInit(out UInt256 b);
        SkipInit(out UInt256 c);
        SkipInit(out UInt256 result);
        SkipInit(out StorageCell storageCell);

Can those be reused rather that allocating more on stack? Passing them by ref here? This can be also used for other methods in VM?

@benaadams

Copy link
Member

Choose a reason for hiding this comment

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

Not really as it will allocate more stack to pass the parameters to the method; abi calling convetion means only have about 2 registers for an instance generic (first register is this pointer, second is generic type)

image

Copy link
Member

Choose a reason for hiding this comment

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

I assume AUTH and AUTHCALL won't be so heavily used that the stack amount should be significant on them

Copy link
Member

Choose a reason for hiding this comment

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

One UInt256 is 32 bytes, but passing it by ref should be only 8 bytes, no?

Comment on lines 2262 to 2280
byte[] chainId = _chainId.PadLeft(32);
UInt256 nonce = _state.GetNonce(authority);
byte[] invokerAddress = invoker.Bytes;
Span<byte> msg = stackalloc byte[1 + 32 * 4];
msg[0] = Eip3074Constants.AuthMagic;
for (int i = 0; i < 32; i++)
{
int offset = i + 1;
msg[offset] = chainId[i];
msg[32 + 32 - i] = (byte)(nonce[i / 8] >> (8 * (i % 8)));

if (i < 12)
msg[offset + 64] = 0;
else if (i - 12 < invokerAddress.Length)
msg[offset + 64] = invokerAddress[i - 12];

if (i < commit.Length)
msg[offset + 96] = commit[i];
}
Copy link
Member

Choose a reason for hiding this comment

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

This is probably suboptimal, you can write chainid directly to message rather than creating byte array, similar you can probably SIMD and not for some other bytes.

src/Nethermind/Nethermind.Evm/VirtualMachine.cs Outdated Show resolved Hide resolved
Comment on lines +2216 to +2233
for (int i = 0; i < 97; i++)
{
byte data = 0;
if (i < length)
data = memData[i];
switch (i)
{
case 0:
yParity = data;
break;
case >= 1 and <= 64:
sigData[i - 1] = data;
break;
default:
commit[i - 65] = data;
break;
}
}
Copy link
Member

Choose a reason for hiding this comment

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

looks SIMD-able. @benaadams ?

@MarekM25 MarekM25 mentioned this pull request May 8, 2024
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants