Repository navigation
Conversation
… fork EVM-internal CREATE used hash(txHash||origin), so every CREATE inside one transaction produced the same address. EVM.Create also accepted an existing account silently, so a factory deploying two children in one call had the second overwrite the first while the transaction still reported success. Gate the Ethereum behaviour behind ForkEVMContractCreateV2. Registered at MaxHeight (off by default, opt-in per chain) like ForkEVMTransferRevert: enabling it from genesis on an existing chain would rewrite historical execution. - CREATE address = CreateAddress(executing account, its nonce); the caller nonce is incremented before any create snapshot and persisted in contract state, so it survives across transactions and failed creates. - Newly created contracts start at nonce 1 (EIP-161); an account with a nonce is no longer treated as non-existent. - CREATE/CREATE2 fail with ErrContractAddressCollision (consuming the forwarded gas) when the target already has a nonce or code. - ErrCodeStoreOutOfGas rolls the creation back instead of keeping the account and pushing its address. - CREATE/CREATE2 forward 63/64 of the remaining gas (EIP-150); CREATE2 no longer halves it, and converts value precision for ETH-signed transactions the same way CREATE does. Address construction for the new path bypasses the chain33 address driver: a btc-format driver cannot parse a 0x hex string and produced an all-zero raw address. Fork-off execution is unchanged; the nonceChange KV write is gated on the same fork. Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Claude <noreply@anthropic.com>
withdrawTicketBalance compared the post-withdraw coins balance with the pre-withdraw ticket balance. The ticket balance also grows with mining rewards, so the check only held when it happened to be exactly 100000 and failed intermittently otherwise (observed 100005 vs 100001 on the same head). Record the coins balance before the withdraw and assert the increase is the withdrawn amount, allowing a small tolerance for fees and rounding. Co-authored-by: Claude <noreply@anthropic.com>
The withdraw moves 100000 coins minus a ~0.1 coin fee, so the expected increase is exactly 99999.9 and the previous 99999.9 lower bound sat on the floating-point boundary: the same balances can compare either side of it depending on their magnitude and on the awk implementation. Use a 99999 lower bound instead, and make jq fail on a missing balance rather than silently comparing a null as zero. Co-authored-by: Claude <noreply@anthropic.com>
ForkEVMContractCreateV2 and ForkEVMTransferRevert are independent forks, but the full Ethereum semantics need both: this fork keeps the nonce of a failed CREATE, and the transfer-revert fork rolls back the value attached to it. Add a test that enables both, and document the pairing next to the fork. Co-authored-by: Claude <noreply@anthropic.com>
The fork now covers the whole Ethereum compatibility set (CREATE semantics, new opcodes and the gas schedule), so a semantic name without a version suffix is clearer: there is no "V1" fork to pair with. Co-authored-by: Claude <noreply@anthropic.com>
This was referenced Nov 11, 2026
Solidity 0.8.20 and later emit PUSH0 by default, so a contract built with a current solc cannot be deployed without it. The fork selects a Berlin-based instruction set that adds PUSH0 (0x5f) and BASEFEE (0x48); the chain has no base-fee mechanism, so BASEFEE pushes zero. Both cost 2 gas. Fork off keeps the existing instruction set, so the two bytes remain invalid and nothing else changes. Co-Authored-By: Claude Code <noreply@anthropic.com>
bysomeone
force-pushed
the
feat/evm-compat
branch
from
November 12, 2026 07:36
db0a569 to
19e346b
Compare
bysomeone
marked this pull request as ready for review
November 12, 2026 07:49
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Two EVM incompatibilities, both gated by one new dapp fork,
ForkEVMCompat:CREATEderived a colliding address (EVM 合约内部 create(Solidity new)地址冲突:同一笔交易内 factory 创建的多个子合约地址相同,后者覆盖前者,且交易显示成功 #1351).opCreateusedhash(txHash || origin), so every internal create in a transaction received the same address, andEVM.Createaccepted an existing account: a second child silently overwrote the first while the transaction still reported success.PUSH0(0x5f) andBASEFEE(0x48) were missing (evm: PUSH0 (0x5f) and other Shanghai/Cancun opcodes are missing from the instruction set #1352). Solidity 0.8.20 and later emitPUSH0by default, so a contract compiled with a current solc cannot be deployed at all.How
ForkEVMCompatis registered atMaxHeight(off by default) and the templates keep it at-1, so each chain opts in with a concrete height. Enabling it from genesis on an existing chain would rewrite historical execution.Fork on:
CREATEaddress =CreateAddress(executing account, its nonce). The caller nonce is incremented before any create snapshot and persisted in contract state, so it survives across transactions and failed creates. New contracts start at nonce 1 (EIP-161), an account with a nonce is no longer treated as non-existent, andCREATE/CREATE2returnErrContractAddressCollisionwhen the target already has a nonce or code.CREATE/CREATE2forward 63/64 of the remaining gas (EIP-150), forward value with the same precision conversionCREATEuses, and roll back onErrCodeStoreOutOfGasinstead of keeping the account.PUSH0pushes zero andBASEFEEreturns zero, both at 2 gas. The chain has no base-fee mechanism.Fork off, execution is unchanged: the previous
CREATEaddress derivation, the Berlin instruction set, the gas constants and the refund handling are untouched.Address construction for the new path bypasses the chain33 address driver: a btc-format driver cannot parse a
0xhex string and produced an all-zero raw address.Testing
go test ./plugin/dapp/evm/...(bn256/cloudflarefails on macOS with adyld: missing LC_UUID load commanderror; the package does not depend on anything here)CREATEaddress matchingcrypto.CreateAddress, twice in one transaction; nonce persistence across transactions and executor instances; a failedCREATEstill consuming a nonceCREATE2collision returning zeroPUSH0/BASEFEEbeinginvalid opcodewhen off and executing when onCREATEkeeps when onlyForkEVMCompatis ongo test ./plugin/dapp/evm/executor/ -shuffle=<seed>is order-independent; a pre-existing global address-driver leak fromquery_test.gowas fixed for that.Deployment note
The fork has to be enabled with a real height in the deployment repository; the templates here keep it off (
-1). Every existing chain configuration must listForkEVMCompatonce this release is deployed — the fork is registered in code, and a configuration that omits it fails the fork check at startup.ForkEVMTransferRevert. That fork rolls back the value attached to a failedCREATE, which this one alone does not do; the full Ethereum semantics need both.[exec.sub.evm] addressDriver="eth". The internalCREATEpath here does not depend on the driver, but the pre-existing top-level ETH-signed deploy path still does.CREATEdoes not match Ethereum exactly; contracts deployed after the fork match exactly.Not covered
No gas metering changes: cold/warm access accounting, net
SSTOREmetering and EIP-3529 refunds are not part of this fork, so gas numbers differ from Ethereum for contracts that are sensitive to them.MCOPY,TLOAD/TSTORE,DIFFICULTY/PREVRANDAO, the precompile table and theSELFDESTRUCTpricing are unchanged.