From 6bcade025b0b0cbab25bad54c260769971158453 Mon Sep 17 00:00:00 2001 From: GheisMohammadi Date: Sat, 8 Nov 2025 19:30:44 +0800 Subject: [PATCH 1/2] core/vm: EIP-3855 (PUSH0) --- core/blockchain_impl.go | 3 +++ core/blockchain_leader_rotation.go | 3 ++- core/evm_test.go | 5 ++++- core/vm/analysis.go | 5 ++++- core/vm/eips.go | 12 ++++++++++++ core/vm/instructions.go | 7 +++++++ core/vm/interpreter.go | 12 ++++++++++++ core/vm/opcodes.go | 4 +++- internal/params/config.go | 16 ++++++++++++++++ node/harmony/worker/worker_test.go | 6 +++--- 10 files changed, 66 insertions(+), 7 deletions(-) diff --git a/core/blockchain_impl.go b/core/blockchain_impl.go index 0b0b084e7a..2e09bbf690 100644 --- a/core/blockchain_impl.go +++ b/core/blockchain_impl.go @@ -757,6 +757,9 @@ func (bc *BlockChainImpl) State() (*state.DB, error) { } func (bc *BlockChainImpl) StateAt(root common.Hash) (*state.DB, error) { + if bc.stateCache == nil { + return nil, fmt.Errorf("stateCache is nil, blockchain not properly initialized") + } return state.New(root, bc.stateCache, bc.snaps) } diff --git a/core/blockchain_leader_rotation.go b/core/blockchain_leader_rotation.go index 8b2683780b..143f619bae 100644 --- a/core/blockchain_leader_rotation.go +++ b/core/blockchain_leader_rotation.go @@ -54,7 +54,8 @@ func (bc *BlockChainImpl) buildLeaderRotationMeta(curHeader *block.Header) error return nil } if curHeader.NumberU64() == 0 { - return errors.New("current header is genesis") + // Skip building leader rotation meta for genesis block + return nil } curPubKey, err := bc.getLeaderPubKeyFromCoinbase(curHeader) if err != nil { diff --git a/core/evm_test.go b/core/evm_test.go index a40d0a10ec..e095e5917b 100644 --- a/core/evm_test.go +++ b/core/evm_test.go @@ -47,7 +47,10 @@ func getTestEnvironment(testBankKey ecdsa.PrivateKey) (*BlockChainImpl, *state.D // fake blockchain cacheConfig := &CacheConfig{SnapshotLimit: 0} - chain, _ := NewBlockChain(database, nil, nil, cacheConfig, gspec.Config, engine, vm.Config{}) + chain, err := NewBlockChain(database, nil, nil, cacheConfig, gspec.Config, engine, vm.Config{}) + if err != nil { + panic(fmt.Sprintf("failed to create blockchain: %v", err)) + } db, _ := chain.StateAt(genesis.Root()) // make a fake block header (use epoch 1 so that locked tokens can be tested) diff --git a/core/vm/analysis.go b/core/vm/analysis.go index 0ccf47b979..127134a91b 100644 --- a/core/vm/analysis.go +++ b/core/vm/analysis.go @@ -43,7 +43,10 @@ func codeBitmap(code []byte) bitvec { for pc := uint64(0); pc < uint64(len(code)); { op := OpCode(code[pc]) - if op >= PUSH1 && op <= PUSH32 { + if op == PUSH0 { + // PUSH0 doesn't read any bytes, just increment pc + pc++ + } else if op >= PUSH1 && op <= PUSH32 { numbits := op - PUSH1 + 1 pc++ for ; numbits >= 8; numbits -= 8 { diff --git a/core/vm/eips.go b/core/vm/eips.go index 2e7406f4ff..8e92c343ae 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -233,3 +233,15 @@ func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by scope.Stack.push(new(uint256.Int)) return nil, nil } + +// enable3855 applies EIP-3855 (PUSH0 opcode) +// - Adds PUSH0 opcode that pushes the value 0 onto the stack +func enable3855(jt *JumpTable) { + jt[PUSH0] = operation{ + execute: opPush0, + constantGas: GasQuickStep, + minStack: minStack(0, 1), + maxStack: maxStack(0, 1), + valid: true, + } +} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 6f6f59120e..0627521014 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -880,6 +880,13 @@ func makeLog(size int) executionFunc { } } +// opPush0 pushes the value 0 onto the stack +func opPush0(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { + stack.push(interpreter.intPool.getZero()) + *pc++ + return nil, nil +} + // opPush1 is a specialized version of pushN func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 2b7e9f46b8..6b1228d91d 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -101,6 +101,18 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { default: cfg.JumpTable = &frontierInstructionSet } + // Enable epoch-based EIPs + epochBasedEIPs := []int{} + if evm.ChainConfig().IsEIP3855(evm.EpochNumber) { + epochBasedEIPs = append(epochBasedEIPs, 3855) + } + // Enable all epoch-based EIPs + for _, eip := range epochBasedEIPs { + if err := EnableEIP(eip, &jt); err != nil { + utils.Logger().Error().Int("eip", eip).Err(err).Msg("Epoch-based EIP activation failed") + } + } + // Enable manually specified extra EIPs for i, eip := range cfg.ExtraEips { copy := *cfg.JumpTable if err := EnableEIP(eip, ©); err != nil { diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index c5ba815203..17a50c548c 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -26,7 +26,7 @@ type OpCode byte // IsPush specifies if an opcode is a PUSH opcode. func (op OpCode) IsPush() bool { switch op { - case PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: + case PUSH0, PUSH1, PUSH2, PUSH3, PUSH4, PUSH5, PUSH6, PUSH7, PUSH8, PUSH9, PUSH10, PUSH11, PUSH12, PUSH13, PUSH14, PUSH15, PUSH16, PUSH17, PUSH18, PUSH19, PUSH20, PUSH21, PUSH22, PUSH23, PUSH24, PUSH25, PUSH26, PUSH27, PUSH28, PUSH29, PUSH30, PUSH31, PUSH32: return true } return false @@ -307,6 +307,7 @@ var opCodeToString = map[OpCode]string{ GAS: "GAS", JUMPDEST: "JUMPDEST", PUSH0: "PUSH0", + PUSH0: "PUSH0", // 0x60 range - push. PUSH1: "PUSH1", @@ -477,6 +478,7 @@ var stringToOp = map[string]OpCode{ "PUSH0": PUSH0, "TLOAD": TLOAD, "TSTORE": TSTORE, + "PUSH0": PUSH0, "PUSH1": PUSH1, "PUSH2": PUSH2, "PUSH3": PUSH3, diff --git a/internal/params/config.go b/internal/params/config.go index df7353c881..ce32859386 100644 --- a/internal/params/config.go +++ b/internal/params/config.go @@ -83,6 +83,7 @@ var ( HIP32Epoch: big.NewInt(2152), // 2024-10-31 13:02 UTC IsOneSecondEpoch: EpochTBD, EIP2537PrecompileEpoch: EpochTBD, + EIP3855Epoch: EpochTBD, } // TestnetChainConfig contains the chain parameters to run a node on the harmony test network. @@ -133,6 +134,7 @@ var ( TestnetExternalEpoch: big.NewInt(3044), IsOneSecondEpoch: EpochTBD, EIP2537PrecompileEpoch: EpochTBD, + EIP3855Epoch: EpochTBD, } // PangaeaChainConfig contains the chain parameters for the Pangaea network. // All features except for CrossLink are enabled at launch. @@ -183,6 +185,7 @@ var ( TestnetExternalEpoch: EpochTBD, IsOneSecondEpoch: EpochTBD, EIP2537PrecompileEpoch: EpochTBD, + EIP3855Epoch: EpochTBD, } // PartnerChainConfig contains the chain parameters for the Partner network. @@ -286,6 +289,7 @@ var ( TestnetExternalEpoch: EpochTBD, IsOneSecondEpoch: EpochTBD, EIP2537PrecompileEpoch: EpochTBD, + EIP3855Epoch: EpochTBD, } // LocalnetChainConfig contains the chain parameters to run for local development. @@ -336,6 +340,7 @@ var ( TestnetExternalEpoch: EpochTBD, IsOneSecondEpoch: big.NewInt(4), EIP2537PrecompileEpoch: EpochTBD, + EIP3855Epoch: EpochTBD, } // AllProtocolChanges ... @@ -390,6 +395,7 @@ var ( big.NewInt(0), big.NewInt(0), // EIP2537PrecompileEpoch big.NewInt(0), // 1153 transient storage + big.NewInt(0), // EIP3855Epoch } // TestChainConfig ... @@ -444,6 +450,7 @@ var ( big.NewInt(0), big.NewInt(0), // EIP2537PrecompileEpoch big.NewInt(0), // 1153 transient storage + big.NewInt(0), // EIP3855Epoch } // TestRules ... @@ -574,6 +581,9 @@ type ChainConfig struct { // EIP2537PrecompileEpoch is the first epoch to support the EIP-2537 precompiles EIP2537PrecompileEpoch *big.Int `json:"eip2537-precompile-epoch,omitempty"` + // EIP3855Epoch is the first epoch to support EIP-3855 (PUSH0 opcode) + EIP3855Epoch *big.Int `json:"eip3855-epoch,omitempty"` + // ChainIdFixEpoch is the first epoch to return ethereum compatible chain id by ChainID() op code ChainIdFixEpoch *big.Int `json:"chain-id-fix-epoch,omitempty"` @@ -877,6 +887,12 @@ func (c *ChainConfig) IsEIP1153TransientStorage(epoch *big.Int) bool { return isForked(c.EIP1153TransientStorageEpoch, epoch) } +// IsEIP3855 determines whether EIP-3855 (PUSH0 opcode) +// is available in the EVM +func (c *ChainConfig) IsEIP3855(epoch *big.Int) bool { + return isForked(c.EIP3855Epoch, epoch) +} + // IsChainIdFix returns whether epoch is either equal to the ChainId Fix fork epoch or greater. func (c *ChainConfig) IsChainIdFix(epoch *big.Int) bool { return isForked(c.ChainIdFixEpoch, epoch) diff --git a/node/harmony/worker/worker_test.go b/node/harmony/worker/worker_test.go index 8dc84e14e7..4f7344efda 100644 --- a/node/harmony/worker/worker_test.go +++ b/node/harmony/worker/worker_test.go @@ -37,7 +37,7 @@ func TestNewWorker(t *testing.T) { Config: chainConfig, Factory: blockFactory, Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, - ShardID: 10, + ShardID: 0, } engine = chain2.NewEngine() ) @@ -45,10 +45,10 @@ func TestNewWorker(t *testing.T) { genesis := gspec.MustCommit(database) _ = genesis cacheConfig := &core.CacheConfig{SnapshotLimit: 0} - chain, err := core.NewBlockChain(database, nil, &core.BlockChainImpl{}, cacheConfig, gspec.Config, engine, vm.Config{}) + chain, err := core.NewBlockChain(database, nil, nil, cacheConfig, gspec.Config, engine, vm.Config{}) if err != nil { - t.Error(err) + t.Fatalf("failed to create blockchain: %v", err) } // Create a new worker worker := New(chain, nil) From 2b21c6540005720f766d0ab0e6d04141a109bf37 Mon Sep 17 00:00:00 2001 From: frozen <355847+Frozen@users.noreply.github.com> Date: Sat, 22 Nov 2025 18:08:27 -0400 Subject: [PATCH 2/2] Cleanup after rebase. --- core/vm/eips.go | 12 ------------ core/vm/instructions.go | 7 ------- core/vm/interpreter.go | 12 ------------ core/vm/opcodes.go | 2 -- 4 files changed, 33 deletions(-) diff --git a/core/vm/eips.go b/core/vm/eips.go index 8e92c343ae..2e7406f4ff 100644 --- a/core/vm/eips.go +++ b/core/vm/eips.go @@ -233,15 +233,3 @@ func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by scope.Stack.push(new(uint256.Int)) return nil, nil } - -// enable3855 applies EIP-3855 (PUSH0 opcode) -// - Adds PUSH0 opcode that pushes the value 0 onto the stack -func enable3855(jt *JumpTable) { - jt[PUSH0] = operation{ - execute: opPush0, - constantGas: GasQuickStep, - minStack: minStack(0, 1), - maxStack: maxStack(0, 1), - valid: true, - } -} diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 0627521014..6f6f59120e 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -880,13 +880,6 @@ func makeLog(size int) executionFunc { } } -// opPush0 pushes the value 0 onto the stack -func opPush0(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) { - stack.push(interpreter.intPool.getZero()) - *pc++ - return nil, nil -} - // opPush1 is a specialized version of pushN func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { var ( diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 6b1228d91d..2b7e9f46b8 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -101,18 +101,6 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter { default: cfg.JumpTable = &frontierInstructionSet } - // Enable epoch-based EIPs - epochBasedEIPs := []int{} - if evm.ChainConfig().IsEIP3855(evm.EpochNumber) { - epochBasedEIPs = append(epochBasedEIPs, 3855) - } - // Enable all epoch-based EIPs - for _, eip := range epochBasedEIPs { - if err := EnableEIP(eip, &jt); err != nil { - utils.Logger().Error().Int("eip", eip).Err(err).Msg("Epoch-based EIP activation failed") - } - } - // Enable manually specified extra EIPs for i, eip := range cfg.ExtraEips { copy := *cfg.JumpTable if err := EnableEIP(eip, ©); err != nil { diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go index 17a50c548c..abddc3842c 100644 --- a/core/vm/opcodes.go +++ b/core/vm/opcodes.go @@ -307,7 +307,6 @@ var opCodeToString = map[OpCode]string{ GAS: "GAS", JUMPDEST: "JUMPDEST", PUSH0: "PUSH0", - PUSH0: "PUSH0", // 0x60 range - push. PUSH1: "PUSH1", @@ -475,7 +474,6 @@ var stringToOp = map[string]OpCode{ "MSIZE": MSIZE, "GAS": GAS, "JUMPDEST": JUMPDEST, - "PUSH0": PUSH0, "TLOAD": TLOAD, "TSTORE": TSTORE, "PUSH0": PUSH0,