EIP7939: Count leading zeros (CLZ) opcode (#1246)

* Cherry pick eip7939

* sync latest
This commit is contained in:
georgehao 2025-10-21 15:48:48 +08:00 committed by GitHub
parent 9aa8b3f38f
commit 9668c2c322
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 82 additions and 2 deletions

View file

@ -39,6 +39,7 @@ var activators = map[int]func(*JumpTable){
1344: enable1344, 1344: enable1344,
1153: enable1153, 1153: enable1153,
7702: enable7702, 7702: enable7702,
7939: enable7939,
} }
// EnableEIP enables the given EIP on the config. // EnableEIP enables the given EIP on the config.
@ -280,3 +281,20 @@ func enable7702(jt *JumpTable) {
jt[STATICCALL].dynamicGas = gasStaticCallEIP7702 jt[STATICCALL].dynamicGas = gasStaticCallEIP7702
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7702 jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7702
} }
// opCLZ implements the CLZ opcode (count leading zero bits)
func opCLZ(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
x := scope.Stack.peek()
x.SetUint64(256 - uint64(x.BitLen()))
return nil, nil
}
// enable7939 enables EIP-7939 (CLZ opcode)
func enable7939(jt *JumpTable) {
jt[CLZ] = &operation{
execute: opCLZ,
constantGas: GasFastStep,
minStack: minStack(1, 1),
maxStack: maxStack(1, 1),
}
}

View file

@ -848,3 +848,43 @@ func TestOpMCopy(t *testing.T) {
} }
} }
} }
func TestOpCLZ(t *testing.T) {
evm := NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
tests := []struct {
inputHex string
want uint64 // expected CLZ result
}{
{"0x0", 256},
{"0x1", 255},
{"0x6ff", 245}, // 0x6ff = 0b11011111111 (11 bits), so 256-11 = 245
{"0xffffffffff", 216}, // 40 bits, so 256-40 = 216
{"0x4000000000000000000000000000000000000000000000000000000000000000", 1},
{"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1},
{"0x8000000000000000000000000000000000000000000000000000000000000000", 0},
{"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0},
}
for _, tc := range tests {
// prepare a fresh stack and PC
stack := newstack()
pc := uint64(0)
// parse input
val := new(uint256.Int)
if err := val.SetFromHex(tc.inputHex); err != nil {
t.Fatal("invalid hex uint256:", tc.inputHex)
}
stack.push(val)
opCLZ(&pc, evm.interpreter, &ScopeContext{Stack: stack})
if gotLen := stack.len(); gotLen != 1 {
t.Fatalf("stack length = %d; want 1", gotLen)
}
result := stack.pop()
if got := result.Uint64(); got != tc.want {
t.Fatalf("clz(%q) = %d; want %d", tc.inputHex, got, tc.want)
}
}
}

View file

@ -74,6 +74,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
if cfg.JumpTable[STOP] == nil { if cfg.JumpTable[STOP] == nil {
var jt JumpTable var jt JumpTable
switch { switch {
case evm.chainRules.IsGalileo:
jt = galileoInstructionSet
case evm.chainRules.IsFeynman: case evm.chainRules.IsFeynman:
jt = feynmanInstructionSet jt = feynmanInstructionSet
case evm.chainRules.IsEuclidV2: case evm.chainRules.IsEuclidV2:

View file

@ -63,11 +63,21 @@ var (
darwinInstructionSet = newDarwinInstructionSet() darwinInstructionSet = newDarwinInstructionSet()
euclidV2InstructionSet = newEuclidV2InstructionSet() euclidV2InstructionSet = newEuclidV2InstructionSet()
feynmanInstructionSet = newFeynmanInstructionSet() feynmanInstructionSet = newFeynmanInstructionSet()
galileoInstructionSet = newGalileoInstructionSet()
) )
// JumpTable contains the EVM opcodes supported at a given fork. // JumpTable contains the EVM opcodes supported at a given fork.
type JumpTable [256]*operation type JumpTable [256]*operation
// newGalileoInstructionSet returns the frontier, homestead, byzantium,
// contantinople, istanbul, petersburg, berlin, london, shanghai, curie, darwin, euclidV2,
// feynman and galileo instructions.
func newGalileoInstructionSet() JumpTable {
instructionSet := newFeynmanInstructionSet()
enable7939(&instructionSet) // EIP-7939 (CLZ opcode)
return instructionSet
}
// newFeynmanInstructionSet returns the frontier, homestead, byzantium, // newFeynmanInstructionSet returns the frontier, homestead, byzantium,
// contantinople, istanbul, petersburg, berlin, london, shanghai, curie, darwin, euclidV2, // contantinople, istanbul, petersburg, berlin, london, shanghai, curie, darwin, euclidV2,
// and feynman instructions. // and feynman instructions.

View file

@ -69,6 +69,7 @@ const (
SHL OpCode = 0x1b SHL OpCode = 0x1b
SHR OpCode = 0x1c SHR OpCode = 0x1c
SAR OpCode = 0x1d SAR OpCode = 0x1d
CLZ OpCode = 0x1e
SHA3 OpCode = 0x20 SHA3 OpCode = 0x20
) )
@ -255,6 +256,7 @@ var opCodeToString = map[OpCode]string{
SHL: "SHL", SHL: "SHL",
SHR: "SHR", SHR: "SHR",
SAR: "SAR", SAR: "SAR",
CLZ: "CLZ",
ADDMOD: "ADDMOD", ADDMOD: "ADDMOD",
MULMOD: "MULMOD", MULMOD: "MULMOD",
@ -430,6 +432,7 @@ var stringToOp = map[string]OpCode{
"SHL": SHL, "SHL": SHL,
"SHR": SHR, "SHR": SHR,
"SAR": SAR, "SAR": SAR,
"CLZ": CLZ,
"ADDMOD": ADDMOD, "ADDMOD": ADDMOD,
"MULMOD": MULMOD, "MULMOD": MULMOD,
"SHA3": SHA3, "SHA3": SHA3,

View file

@ -670,6 +670,7 @@ type ChainConfig struct {
EuclidTime *uint64 `json:"euclidTime,omitempty"` // Euclid switch time (nil = no fork, 0 = already on euclid) EuclidTime *uint64 `json:"euclidTime,omitempty"` // Euclid switch time (nil = no fork, 0 = already on euclid)
EuclidV2Time *uint64 `json:"euclidv2Time,omitempty"` // EuclidV2 switch time (nil = no fork, 0 = already on euclidv2) EuclidV2Time *uint64 `json:"euclidv2Time,omitempty"` // EuclidV2 switch time (nil = no fork, 0 = already on euclidv2)
FeynmanTime *uint64 `json:"feynmanTime,omitempty"` // Feynman switch time (nil = no fork, 0 = already on feynman) FeynmanTime *uint64 `json:"feynmanTime,omitempty"` // Feynman switch time (nil = no fork, 0 = already on feynman)
GalileoTime *uint64 `json:"galileoTime,omitempty"` // Galileo switch time (nil = no fork, 0 = already on galileo)
// TerminalTotalDifficulty is the amount of total difficulty reached by // TerminalTotalDifficulty is the amount of total difficulty reached by
// the network that triggers the consensus upgrade. // the network that triggers the consensus upgrade.
@ -1014,6 +1015,10 @@ func (c *ChainConfig) IsFeynman(now uint64) bool {
return isForkedTime(now, c.FeynmanTime) return isForkedTime(now, c.FeynmanTime)
} }
func (c *ChainConfig) IsGalileo(now uint64) bool {
return isForkedTime(now, c.GalileoTime)
}
// IsFeynmanTransitionBlock returns whether the given block timestamp corresponds to the first Feynman block. // IsFeynmanTransitionBlock returns whether the given block timestamp corresponds to the first Feynman block.
func (c *ChainConfig) IsFeynmanTransitionBlock(blockTimestamp uint64, parentTimestamp uint64) bool { func (c *ChainConfig) IsFeynmanTransitionBlock(blockTimestamp uint64, parentTimestamp uint64) bool {
return isForkedTime(blockTimestamp, c.FeynmanTime) && !isForkedTime(parentTimestamp, c.FeynmanTime) return isForkedTime(blockTimestamp, c.FeynmanTime) && !isForkedTime(parentTimestamp, c.FeynmanTime)
@ -1247,7 +1252,7 @@ type Rules struct {
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
IsBerlin, IsLondon, IsArchimedes, IsShanghai bool IsBerlin, IsLondon, IsArchimedes, IsShanghai bool
IsBernoulli, IsCurie, IsDarwin, IsEuclid, IsEuclidV2 bool IsBernoulli, IsCurie, IsDarwin, IsEuclid, IsEuclidV2 bool
IsFeynman bool IsFeynman, IsGalileo bool
} }
// Rules ensures c's ChainID is not nil. // Rules ensures c's ChainID is not nil.
@ -1276,5 +1281,6 @@ func (c *ChainConfig) Rules(num *big.Int, time uint64) Rules {
IsEuclid: c.IsEuclid(time), IsEuclid: c.IsEuclid(time),
IsEuclidV2: c.IsEuclidV2(time), IsEuclidV2: c.IsEuclidV2(time),
IsFeynman: c.IsFeynman(time), IsFeynman: c.IsFeynman(time),
IsGalileo: c.IsGalileo(time),
} }
} }

View file

@ -5,7 +5,7 @@ go 1.22
replace github.com/scroll-tech/go-ethereum => ../../.. replace github.com/scroll-tech/go-ethereum => ../../..
require ( require (
github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6 github.com/scroll-tech/da-codec v0.1.3-0.20250825071838-cddc263e5ef6
github.com/scroll-tech/go-ethereum v1.10.14-0.20250625112225-a67863c65587 github.com/scroll-tech/go-ethereum v1.10.14-0.20250625112225-a67863c65587
github.com/spf13/cobra v1.9.1 github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0 github.com/stretchr/testify v1.10.0

View file

@ -173,6 +173,7 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1 h1:Dhd58LE1D+dnoxpgLVeQBMF9uweL/fhQfZHWtWSiOlE= github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1 h1:Dhd58LE1D+dnoxpgLVeQBMF9uweL/fhQfZHWtWSiOlE=
github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1/go.mod h1:yhTS9OVC0xQGhg7DN5iV5KZJvnSIlFWAxDdp+6jxQtY= github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1/go.mod h1:yhTS9OVC0xQGhg7DN5iV5KZJvnSIlFWAxDdp+6jxQtY=
github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6/go.mod h1:Z6kN5u2khPhiqHyk172kGB7o38bH/nj7Ilrb/46wZGg= github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6/go.mod h1:Z6kN5u2khPhiqHyk172kGB7o38bH/nj7Ilrb/46wZGg=
github.com/scroll-tech/da-codec v0.1.3-0.20250825071838-cddc263e5ef6/go.mod h1:Z6kN5u2khPhiqHyk172kGB7o38bH/nj7Ilrb/46wZGg=
github.com/scroll-tech/zktrie v0.8.4 h1:UagmnZ4Z3ITCk+aUq9NQZJNAwnWl4gSxsLb2Nl7IgRE= github.com/scroll-tech/zktrie v0.8.4 h1:UagmnZ4Z3ITCk+aUq9NQZJNAwnWl4gSxsLb2Nl7IgRE=
github.com/scroll-tech/zktrie v0.8.4/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk= github.com/scroll-tech/zktrie v0.8.4/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=