Skip to content

Commit 63f58f7

Browse files
fjlspencer-tb
authored andcommitted
core/vm: implement EIP-7939: CLZ opcode (ethereum#31989)
1 parent 6ab4faf commit 63f58f7

File tree

5 files changed

+70
-1
lines changed

5 files changed

+70
-1
lines changed

core/vm/eips.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,13 @@ func opBlobBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
293293
return nil, nil
294294
}
295295

296+
func opCLZ(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
297+
x := scope.Stack.pop()
298+
// count leading zero bits in x
299+
scope.Stack.push(new(uint256.Int).SetUint64(256 - uint64(x.BitLen())))
300+
return nil, nil
301+
}
302+
296303
// enable4844 applies EIP-4844 (BLOBHASH opcode)
297304
func enable4844(jt *JumpTable) {
298305
jt[BLOBHASH] = &operation{
@@ -303,6 +310,15 @@ func enable4844(jt *JumpTable) {
303310
}
304311
}
305312

313+
func enable7939(jt *JumpTable) {
314+
jt[CLZ] = &operation{
315+
execute: opCLZ,
316+
constantGas: GasFastestStep,
317+
minStack: minStack(1, 1),
318+
maxStack: maxStack(1, 1),
319+
}
320+
}
321+
306322
// enable7516 applies EIP-7516 (BLOBBASEFEE opcode)
307323
func enable7516(jt *JumpTable) {
308324
jt[BLOBBASEFEE] = &operation{

core/vm/instructions_test.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,3 +972,47 @@ func TestPush(t *testing.T) {
972972
}
973973
}
974974
}
975+
976+
func TestOpCLZ(t *testing.T) {
977+
// set up once
978+
evm := NewEVM(BlockContext{}, nil, params.TestChainConfig, Config{})
979+
980+
tests := []struct {
981+
name string
982+
inputHex string // hexadecimal input for clarity
983+
want uint64 // expected CLZ result
984+
}{
985+
{"zero", "0x0", 256},
986+
{"one", "0x1", 255},
987+
{"all-ones (256 bits)", "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 0},
988+
{"low-10-bytes ones", "0xffffffffff", 216}, // 10 bytes = 80 bits, so 256-80=176? Actually input is 0xffffffffff = 40 bits so 256-40=216
989+
}
990+
991+
for _, tc := range tests {
992+
t.Run(tc.name, func(t *testing.T) {
993+
994+
// prepare a fresh stack and PC
995+
stack := newstack()
996+
pc := uint64(0)
997+
998+
// parse input
999+
val := new(uint256.Int)
1000+
if _, err := fmt.Sscan(tc.inputHex, val); err != nil {
1001+
// fallback: try hex
1002+
val.SetFromHex(tc.inputHex)
1003+
}
1004+
1005+
stack.push(val)
1006+
opCLZ(&pc, evm.interpreter, &ScopeContext{Stack: stack})
1007+
1008+
if gotLen := stack.len(); gotLen != 1 {
1009+
t.Fatalf("stack length = %d; want 1", gotLen)
1010+
}
1011+
result := stack.pop()
1012+
1013+
if got := result.Uint64(); got != tc.want {
1014+
t.Fatalf("clz(%q) = %d; want %d", tc.inputHex, got, tc.want)
1015+
}
1016+
})
1017+
}
1018+
}

core/vm/jump_table.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,12 @@ func newEOFInstructionSetForTesting() JumpTable {
102102
return validate(instructionSet)
103103
}
104104

105+
func newOsakaInstructionSet() JumpTable {
106+
instructionSet := newPragueInstructionSet()
107+
enable7939(&instructionSet) // EIP-7939 (CLZ opcode)
108+
return validate(instructionSet)
109+
}
110+
105111
func newPragueInstructionSet() JumpTable {
106112
instructionSet := newCancunInstructionSet()
107113
enable7702(&instructionSet) // EIP-7702 Setcode transaction type

core/vm/jump_table_export.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func LookupInstructionSet(rules params.Rules) (JumpTable, error) {
2929
case rules.IsVerkle:
3030
return newCancunInstructionSet(), errors.New("verkle-fork not defined yet")
3131
case rules.IsOsaka:
32-
return newPragueInstructionSet(), errors.New("osaka-fork not defined yet")
32+
return newOsakaInstructionSet(), nil
3333
case rules.IsPrague:
3434
return newPragueInstructionSet(), nil
3535
case rules.IsCancun:

core/vm/opcodes.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ const (
6262
SHL OpCode = 0x1b
6363
SHR OpCode = 0x1c
6464
SAR OpCode = 0x1d
65+
CLZ OpCode = 0x1e
6566
)
6667

6768
// 0x20 range - crypto.
@@ -282,6 +283,7 @@ var opCodeToString = [256]string{
282283
SHL: "SHL",
283284
SHR: "SHR",
284285
SAR: "SAR",
286+
CLZ: "CLZ",
285287
ADDMOD: "ADDMOD",
286288
MULMOD: "MULMOD",
287289

@@ -484,6 +486,7 @@ var stringToOp = map[string]OpCode{
484486
"SHL": SHL,
485487
"SHR": SHR,
486488
"SAR": SAR,
489+
"CLZ": CLZ,
487490
"ADDMOD": ADDMOD,
488491
"MULMOD": MULMOD,
489492
"KECCAK256": KECCAK256,

0 commit comments

Comments
 (0)