Skip to content
This repository has been archived by the owner on Jul 5, 2024. It is now read-only.

Merge Transfer and TransferWithGas gadget #1777

Merged
merged 22 commits into from
Mar 5, 2024
Merged

Conversation

ChihChengLiang
Copy link
Collaborator

@ChihChengLiang ChihChengLiang commented Feb 21, 2024

Description

  • Transfer and TransferWithGas gadgets are mostly the same. We can merge them and duplicate them.
  • There are duplicated logics for creating the receiver account (writing empty hash). We remove the duplicated one.
  • The update balance gadget is vague, it is hard to understand whether we want to add or subtract balance. We extract a helpful method for that.
  • deduplicate the transfer_to logic in busmapping.
  • Fix the receiver creation condition on busmapping.
  • Fix the order of the sender account read-write in begin tx assignment.

Thank @curryrasul for initializing this conversation.

Issue Link

Type of change

Refactor (no updates to logic)

Content

Test

cargo test -p zkevm-circuits create
cargo test -p zkevm-circuits begin_tx
cargo test -p zkevm-circuits end_tx 

@github-actions github-actions bot added the crate-zkevm-circuits Issues related to the zkevm-circuits workspace member label Feb 21, 2024
@ChihChengLiang ChihChengLiang force-pushed the merge-transfer-gadget branch 2 times, most recently from 788cb4b to 8d4d596 Compare February 22, 2024 14:03
@github-actions github-actions bot added the crate-bus-mapping Issues related to the bus-mapping workspace member label Feb 22, 2024
@ChihChengLiang ChihChengLiang marked this pull request as ready for review February 22, 2024 17:34
value: Word,
reversible: bool,
) -> Result<(), Error> {
// If receiver doesn't exist, create it
if (!receiver_exists && !value.is_zero()) || must_create {
if !receiver_exists && (!value.is_zero() || opcode_is_create) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We have to think carefully about this condition. I would love input from reviewers.

  • What is receiver_exists?
    • We can check if code_hash is 0. It is an artificial rule we added before.
    • Nonce is 0. Should we add this? address_collision in create has this check.
  • opcode_is_create
    • originally called must_create. I think this covers 3 cases:
    • EOA transfer to create an account. Can we call this situtation "opcode_is_create"?
    • Create receiver with CREATE opcode
    • Create receiver with CREATE2 opcode

Copy link
Contributor

@KimiWu123 KimiWu123 Feb 26, 2024

Choose a reason for hiding this comment

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

From geth impl., an account which doesn't exist will be created only when transfer value is non-zero.

But, no matter this account exsists or not, it'll be created again in CREATE/2 opcode. You can see createAccount is called in create and then will find this

// createObject creates a new state object. If there is an existing account with
// the given address, it is overwritten and returned as the second return value.

in createObject

The conclusion is the original condition is correct. In your fix, the account won't be re-created if the receiver exists.

Copy link
Contributor

@KimiWu123 KimiWu123 Feb 26, 2024

Choose a reason for hiding this comment

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

For the naming of opcode_is_create. I would vote is_create_account or force_create_account as you mentioned "EOA transfer to create an account", it doesn't fix the name.

Copy link
Collaborator Author

@ChihChengLiang ChihChengLiang Mar 4, 2024

Choose a reason for hiding this comment

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

an account which doesn't exist will be created only when transfer value is non-zero.

I think this is correct and it matches our behavior.

no matter this account exists or not, it'll be created again in CREATE/2 opcode.

This is incorrect. Three lines above your quoted createAccount locates a nonzero nonce and non-empty hash check that early exits the function and returns an ErrContractAddressCollision error.

The EIP-1014: Skinny CREATE2 also states "if nonce or code (of the create2 destination address) is nonzero, then the create-operation fails."

So for this PR, the conclusion is that a CREATEX opcode does not guarantee an overwrite of an account.


But you might wonder, why the create opcode only checks non empty code hash and non zero nonce. What about storage root and balance? Can we have an account that has code="" and nonce=0, but storage root !=0 or balance !=0? The tl;dr is we can't, for non-trivial reason.

I fell into a rabbit hole of a historical study and how the EIP was defined. Fortunately, Gary wrote the best summary of this issue. See ethereum/go-ethereum#28666.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry for the confusion. Maybe we could put the link ethereum/go-ethereum#28666 in our comment.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I added to the comment in the constraint part, which might be the place people will check more frequently 77cfbcb.

Copy link
Member

Choose a reason for hiding this comment

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

EOA transfer to create an account. Can we call this situtation "opcode_is_create"?

My opinion for opcode_is_create to represents create an account with tx.to = null is a bit confusing. How about just name it is_create to align with other place e.g. call.is_create?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sorry I missed the renaming part before. I fixed the renaming here 7449289

zkevm-circuits/src/evm_circuit/execution/begin_tx.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@KimiWu123 KimiWu123 left a comment

Choose a reason for hiding this comment

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

Not finished the review yet, but seems some changes needed and the light testing is falied.

Copy link
Collaborator Author

@ChihChengLiang ChihChengLiang left a comment

Choose a reason for hiding this comment

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

Fixing the read-write inconsistency. I'll this back to draft

@ChihChengLiang ChihChengLiang marked this pull request as draft February 23, 2024 09:56
@ChihChengLiang ChihChengLiang marked this pull request as ready for review February 23, 2024 11:25
Copy link
Collaborator Author

@ChihChengLiang ChihChengLiang left a comment

Choose a reason for hiding this comment

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

I collected all the reads and writes from assignment functions everywhere back to the transfer gadget. I hope this reduces the code complexity.

value: Word,
reversible: bool,
) -> Result<(), Error> {
// If receiver doesn't exist, create it
if (!receiver_exists && !value.is_zero()) || must_create {
if !receiver_exists && (!value.is_zero() || opcode_is_create) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We have to think carefully about this condition. I would love input from reviewers.

  • What is receiver_exists?
    • We can check if code_hash is 0. It is an artificial rule we added before.
    • Nonce is 0. Should we add this? address_collision in create has this check.
  • opcode_is_create
    • originally called must_create. I think this covers 3 cases:
    • EOA transfer to create an account. Can we call this situtation "opcode_is_create"?
    • Create receiver with CREATE opcode
    • Create receiver with CREATE2 opcode

Copy link
Contributor

@KimiWu123 KimiWu123 left a comment

Choose a reason for hiding this comment

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

Not finished yet, but would like to discuss the callee existence earlier.

Copy link
Collaborator Author

@ChihChengLiang ChihChengLiang left a comment

Choose a reason for hiding this comment

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

Replied some comments that I have quick answers

@KimiWu123 KimiWu123 self-requested a review February 27, 2024 02:04
Copy link
Contributor

@KimiWu123 KimiWu123 left a comment

Choose a reason for hiding this comment

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

LGTM. It looks nice and clean now. Great work! The remaining thing is this dicussion thread

zkevm-circuits/src/evm_circuit/execution/create.rs Outdated Show resolved Hide resolved
Copy link
Collaborator Author

@ChihChengLiang ChihChengLiang left a comment

Choose a reason for hiding this comment

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

Hi @KimiWu123, I replied to the feedback to the receiver creation check.

value: Word,
reversible: bool,
) -> Result<(), Error> {
// If receiver doesn't exist, create it
if (!receiver_exists && !value.is_zero()) || must_create {
if !receiver_exists && (!value.is_zero() || opcode_is_create) {
Copy link
Collaborator Author

@ChihChengLiang ChihChengLiang Mar 4, 2024

Choose a reason for hiding this comment

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

an account which doesn't exist will be created only when transfer value is non-zero.

I think this is correct and it matches our behavior.

no matter this account exists or not, it'll be created again in CREATE/2 opcode.

This is incorrect. Three lines above your quoted createAccount locates a nonzero nonce and non-empty hash check that early exits the function and returns an ErrContractAddressCollision error.

The EIP-1014: Skinny CREATE2 also states "if nonce or code (of the create2 destination address) is nonzero, then the create-operation fails."

So for this PR, the conclusion is that a CREATEX opcode does not guarantee an overwrite of an account.


But you might wonder, why the create opcode only checks non empty code hash and non zero nonce. What about storage root and balance? Can we have an account that has code="" and nonce=0, but storage root !=0 or balance !=0? The tl;dr is we can't, for non-trivial reason.

I fell into a rabbit hole of a historical study and how the EIP was defined. Fortunately, Gary wrote the best summary of this issue. See ethereum/go-ethereum#28666.

@hero78119 hero78119 self-requested a review March 5, 2024 06:22
Copy link
Member

@hero78119 hero78119 left a comment

Choose a reason for hiding this comment

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

Thanks for the code cleanup!
LGTM with few nitpicks

zkevm-circuits/src/evm_circuit/util/common_gadget.rs Outdated Show resolved Hide resolved
zkevm-circuits/src/evm_circuit/util/common_gadget.rs Outdated Show resolved Hide resolved
value: Word,
reversible: bool,
) -> Result<(), Error> {
// If receiver doesn't exist, create it
if (!receiver_exists && !value.is_zero()) || must_create {
if !receiver_exists && (!value.is_zero() || opcode_is_create) {
Copy link
Member

Choose a reason for hiding this comment

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

EOA transfer to create an account. Can we call this situtation "opcode_is_create"?

My opinion for opcode_is_create to represents create an account with tx.to = null is a bit confusing. How about just name it is_create to align with other place e.g. call.is_create?

Copy link
Member

@hero78119 hero78119 left a comment

Choose a reason for hiding this comment

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

👍 👍

@hero78119 hero78119 added this pull request to the merge queue Mar 5, 2024
Merged via the queue into main with commit 41e3408 Mar 5, 2024
15 checks passed
@hero78119 hero78119 deleted the merge-transfer-gadget branch March 5, 2024 12:44
@ed255 ed255 added this to the Feature Completeness milestone Mar 7, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
crate-bus-mapping Issues related to the bus-mapping workspace member crate-zkevm-circuits Issues related to the zkevm-circuits workspace member
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

4 participants