Skip to content

Commit 9668c2c

Browse files
authored
EIP7939: Count leading zeros (CLZ) opcode (#1246)
* Cherry pick eip7939 * sync latest
1 parent 9aa8b3f commit 9668c2c

File tree

8 files changed

+82
-2
lines changed

8 files changed

+82
-2
lines changed

core/vm/eips.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ var activators = map[int]func(*JumpTable){
3939
1344: enable1344,
4040
1153: enable1153,
4141
7702: enable7702,
42+
7939: enable7939,
4243
}
4344

4445
// EnableEIP enables the given EIP on the config.
@@ -280,3 +281,20 @@ func enable7702(jt *JumpTable) {
280281
jt[STATICCALL].dynamicGas = gasStaticCallEIP7702
281282
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP7702
282283
}
284+
285+
// opCLZ implements the CLZ opcode (count leading zero bits)
286+
func opCLZ(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
287+
x := scope.Stack.peek()
288+
x.SetUint64(256 - uint64(x.BitLen()))
289+
return nil, nil
290+
}
291+
292+
// enable7939 enables EIP-7939 (CLZ opcode)
293+
func enable7939(jt *JumpTable) {
294+
jt[CLZ] = &operation{
295+
execute: opCLZ,
296+
constantGas: GasFastStep,
297+
minStack: minStack(1, 1),
298+
maxStack: maxStack(1, 1),
299+
}
300+
}

core/vm/instructions_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,3 +848,43 @@ func TestOpMCopy(t *testing.T) {
848848
}
849849
}
850850
}
851+
852+
func TestOpCLZ(t *testing.T) {
853+
evm := NewEVM(BlockContext{}, TxContext{}, nil, params.TestChainConfig, Config{})
854+
855+
tests := []struct {
856+
inputHex string
857+
want uint64 // expected CLZ result
858+
}{
859+
{"0x0", 256},
860+
{"0x1", 255},
861+
{"0x6ff", 245}, // 0x6ff = 0b11011111111 (11 bits), so 256-11 = 245
862+
{"0xffffffffff", 216}, // 40 bits, so 256-40 = 216
863+
{"0x4000000000000000000000000000000000000000000000000000000000000000", 1},
864+
{"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1},
865+
{"0x8000000000000000000000000000000000000000000000000000000000000000", 0},
866+
{"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0},
867+
}
868+
for _, tc := range tests {
869+
// prepare a fresh stack and PC
870+
stack := newstack()
871+
pc := uint64(0)
872+
873+
// parse input
874+
val := new(uint256.Int)
875+
if err := val.SetFromHex(tc.inputHex); err != nil {
876+
t.Fatal("invalid hex uint256:", tc.inputHex)
877+
}
878+
879+
stack.push(val)
880+
opCLZ(&pc, evm.interpreter, &ScopeContext{Stack: stack})
881+
882+
if gotLen := stack.len(); gotLen != 1 {
883+
t.Fatalf("stack length = %d; want 1", gotLen)
884+
}
885+
result := stack.pop()
886+
if got := result.Uint64(); got != tc.want {
887+
t.Fatalf("clz(%q) = %d; want %d", tc.inputHex, got, tc.want)
888+
}
889+
}
890+
}

core/vm/interpreter.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ func NewEVMInterpreter(evm *EVM, cfg Config) *EVMInterpreter {
7474
if cfg.JumpTable[STOP] == nil {
7575
var jt JumpTable
7676
switch {
77+
case evm.chainRules.IsGalileo:
78+
jt = galileoInstructionSet
7779
case evm.chainRules.IsFeynman:
7880
jt = feynmanInstructionSet
7981
case evm.chainRules.IsEuclidV2:

core/vm/jump_table.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,21 @@ var (
6363
darwinInstructionSet = newDarwinInstructionSet()
6464
euclidV2InstructionSet = newEuclidV2InstructionSet()
6565
feynmanInstructionSet = newFeynmanInstructionSet()
66+
galileoInstructionSet = newGalileoInstructionSet()
6667
)
6768

6869
// JumpTable contains the EVM opcodes supported at a given fork.
6970
type JumpTable [256]*operation
7071

72+
// newGalileoInstructionSet returns the frontier, homestead, byzantium,
73+
// contantinople, istanbul, petersburg, berlin, london, shanghai, curie, darwin, euclidV2,
74+
// feynman and galileo instructions.
75+
func newGalileoInstructionSet() JumpTable {
76+
instructionSet := newFeynmanInstructionSet()
77+
enable7939(&instructionSet) // EIP-7939 (CLZ opcode)
78+
return instructionSet
79+
}
80+
7181
// newFeynmanInstructionSet returns the frontier, homestead, byzantium,
7282
// contantinople, istanbul, petersburg, berlin, london, shanghai, curie, darwin, euclidV2,
7383
// and feynman instructions.

core/vm/opcodes.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ const (
6969
SHL OpCode = 0x1b
7070
SHR OpCode = 0x1c
7171
SAR OpCode = 0x1d
72+
CLZ OpCode = 0x1e
7273

7374
SHA3 OpCode = 0x20
7475
)
@@ -255,6 +256,7 @@ var opCodeToString = map[OpCode]string{
255256
SHL: "SHL",
256257
SHR: "SHR",
257258
SAR: "SAR",
259+
CLZ: "CLZ",
258260
ADDMOD: "ADDMOD",
259261
MULMOD: "MULMOD",
260262

@@ -430,6 +432,7 @@ var stringToOp = map[string]OpCode{
430432
"SHL": SHL,
431433
"SHR": SHR,
432434
"SAR": SAR,
435+
"CLZ": CLZ,
433436
"ADDMOD": ADDMOD,
434437
"MULMOD": MULMOD,
435438
"SHA3": SHA3,

params/config.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,7 @@ type ChainConfig struct {
670670
EuclidTime *uint64 `json:"euclidTime,omitempty"` // Euclid switch time (nil = no fork, 0 = already on euclid)
671671
EuclidV2Time *uint64 `json:"euclidv2Time,omitempty"` // EuclidV2 switch time (nil = no fork, 0 = already on euclidv2)
672672
FeynmanTime *uint64 `json:"feynmanTime,omitempty"` // Feynman switch time (nil = no fork, 0 = already on feynman)
673+
GalileoTime *uint64 `json:"galileoTime,omitempty"` // Galileo switch time (nil = no fork, 0 = already on galileo)
673674

674675
// TerminalTotalDifficulty is the amount of total difficulty reached by
675676
// the network that triggers the consensus upgrade.
@@ -1014,6 +1015,10 @@ func (c *ChainConfig) IsFeynman(now uint64) bool {
10141015
return isForkedTime(now, c.FeynmanTime)
10151016
}
10161017

1018+
func (c *ChainConfig) IsGalileo(now uint64) bool {
1019+
return isForkedTime(now, c.GalileoTime)
1020+
}
1021+
10171022
// IsFeynmanTransitionBlock returns whether the given block timestamp corresponds to the first Feynman block.
10181023
func (c *ChainConfig) IsFeynmanTransitionBlock(blockTimestamp uint64, parentTimestamp uint64) bool {
10191024
return isForkedTime(blockTimestamp, c.FeynmanTime) && !isForkedTime(parentTimestamp, c.FeynmanTime)
@@ -1247,7 +1252,7 @@ type Rules struct {
12471252
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
12481253
IsBerlin, IsLondon, IsArchimedes, IsShanghai bool
12491254
IsBernoulli, IsCurie, IsDarwin, IsEuclid, IsEuclidV2 bool
1250-
IsFeynman bool
1255+
IsFeynman, IsGalileo bool
12511256
}
12521257

12531258
// Rules ensures c's ChainID is not nil.
@@ -1276,5 +1281,6 @@ func (c *ChainConfig) Rules(num *big.Int, time uint64) Rules {
12761281
IsEuclid: c.IsEuclid(time),
12771282
IsEuclidV2: c.IsEuclidV2(time),
12781283
IsFeynman: c.IsFeynman(time),
1284+
IsGalileo: c.IsGalileo(time),
12791285
}
12801286
}

rollup/missing_header_fields/export-headers-toolkit/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.22
55
replace github.com/scroll-tech/go-ethereum => ../../..
66

77
require (
8-
github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6
8+
github.com/scroll-tech/da-codec v0.1.3-0.20250825071838-cddc263e5ef6
99
github.com/scroll-tech/go-ethereum v1.10.14-0.20250625112225-a67863c65587
1010
github.com/spf13/cobra v1.9.1
1111
github.com/stretchr/testify v1.10.0

rollup/missing_header_fields/export-headers-toolkit/go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
173173
github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1 h1:Dhd58LE1D+dnoxpgLVeQBMF9uweL/fhQfZHWtWSiOlE=
174174
github.com/scroll-tech/da-codec v0.1.3-0.20250313120912-344f2d5e33e1/go.mod h1:yhTS9OVC0xQGhg7DN5iV5KZJvnSIlFWAxDdp+6jxQtY=
175175
github.com/scroll-tech/da-codec v0.1.3-0.20250626091118-58b899494da6/go.mod h1:Z6kN5u2khPhiqHyk172kGB7o38bH/nj7Ilrb/46wZGg=
176+
github.com/scroll-tech/da-codec v0.1.3-0.20250825071838-cddc263e5ef6/go.mod h1:Z6kN5u2khPhiqHyk172kGB7o38bH/nj7Ilrb/46wZGg=
176177
github.com/scroll-tech/zktrie v0.8.4 h1:UagmnZ4Z3ITCk+aUq9NQZJNAwnWl4gSxsLb2Nl7IgRE=
177178
github.com/scroll-tech/zktrie v0.8.4/go.mod h1:XvNo7vAk8yxNyTjBDj5WIiFzYW4bx/gJ78+NK6Zn6Uk=
178179
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=

0 commit comments

Comments
 (0)