Skip to content

Conversation

cuiweixie
Copy link
Contributor

goos: darwin
goarch: arm64
pkg: github.com/ethereum/go-ethereum/core/vm/runtime
cpu: Apple M1 Pro
                    │   old.txt    │               new.txt               │
                    │    sec/op    │   sec/op     vs base                │
EVM_CREATE_500-10     16.285m ± 2%   1.701m ± 1%  -89.55% (p=0.000 n=10)
EVM_CREATE2_500-10     97.04m ± 0%   93.26m ± 1%   -3.90% (p=0.000 n=10)
EVM_CREATE_1200-10    22.029m ± 1%   1.338m ± 1%  -93.93% (p=0.000 n=10)
EVM_CREATE2_1200-10    82.56m ± 0%   80.26m ± 1%   -2.78% (p=0.001 n=10)
geomean                41.17m        11.43m       -72.25%

                    │    old.txt     │               new.txt                │
                    │      B/op      │     B/op      vs base                │
EVM_CREATE_500-10     144.244Mi ± 0%   1.091Mi ± 0%  -99.24% (p=0.000 n=10)
EVM_CREATE2_500-10    37926.2Ki ± 0%   660.1Ki ± 0%  -98.26% (p=0.000 n=10)
EVM_CREATE_1200-10    256.900Mi ± 0%   1.606Mi ± 0%  -99.37% (p=0.000 n=10)
EVM_CREATE2_1200-10    32.245Mi ± 0%   1.208Mi ± 0%  -96.25% (p=0.000 n=10)
geomean                 81.56Mi        1.081Mi       -98.67%

                    │   old.txt   │              new.txt               │
                    │  allocs/op  │  allocs/op   vs base               │
EVM_CREATE_500-10     15.28k ± 0%   14.52k ± 0%  -4.93% (p=0.000 n=10)
EVM_CREATE2_500-10    3.888k ± 0%   3.700k ± 0%  -4.82% (p=0.000 n=10)
EVM_CREATE_1200-10    11.72k ± 0%   10.90k ± 0%  -6.94% (p=0.000 n=10)
EVM_CREATE2_1200-10   1.425k ± 0%   1.348k ± 0%  -5.44% (p=0.000 n=10)
geomean               5.612k        5.301k       -5.54%

@jwasinger
Copy link
Contributor

It seems like it could be okay. But also could potentially be a very dangerous change.

contract creation happens relatively less often and isn't a bottleneck in the EVM. So based on the risk/reward tradeoff here, I'd be inclined to close this.

@cuiweixie
Copy link
Contributor Author

cuiweixie commented Oct 15, 2025

  1. The OP_CREATE and OP_CREATE2 operations are similar to OP_CALL. I noticed that OP_CALL was updated from using a copy to using a pointer in this commit.
  2. In both OP_CREATE and OP_CREATE2, the init code is used as the contract code. Contract code is immutable by consensus in the blockchain community. During the execution of OP_CREATE/OP_CREATE2, new mem obj will be create in this code, and opReturn already uses memory.GetCopy. Therefore, there should be no potential risk in this case.

offset, size = scope.Stack.pop(), scope.Stack.pop()
salt = scope.Stack.pop()
input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
input = scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
Copy link
Member

Choose a reason for hiding this comment

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

By changing to the GetPtr, the same byte slice will be referenced/used in two call frames: one in the contract creation and another is the caller frame.

It might introduce some unexpected edge cases where the slice mutations from one side affect another side. It feels a very dangerous change to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

  1. in ethereum white paper mention that "code is theoretically immutable".
  2. "By changing to the GetPtr, the same byte slice will be referenced/used in two call frames: one in the contract creation and another is the caller frame.

It might introduce some unexpected edge cases where the slice mutations from one side affect another side. "
this is same to opcall. but op call already change to GetPtr!.
3. as in this pr. "I'm not 100% sure we need it, but Create/Create2 holds on to that code, and stores it in a struct for later processing. I didn't consider it safe to change that now", actually, Not only code(input) is stores in struct for later processing, but also the opCall input. GetPtr is call here. and assign to contract.Input here. Which is next next to Code field in contract here.

May ask @holiman and @fjl and @karalabe to help with us.

Copy link
Member

@gballet gballet left a comment

Choose a reason for hiding this comment

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

I have digged into this, and even though this thing looks wrong, I am convinced that it is ok:

  • the input data would be a reference, but accessing it from the contract would cause a copy as CALLDATALOAD calls getData, which makes a copy. There is no way for the caller to directly modify the input slice.
  • the data that would be saved to disk is also copied from memory, so there is no issue there
  • As the author notes, this optimization is already used in opCall and it works

Nonetheless, I think it's a bad idea because although it works today, we are not protected from future modifications that would somehow uncover that problem. I'm going to approve but let Gary have the final say.

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.

4 participants