-
Notifications
You must be signed in to change notification settings - Fork 70
feat(core/vm): implement EIP-8024 #33095 #33361 #33614 #33785 #33787 #33928 #34635 #2268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev-upgrade
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -926,6 +926,128 @@ func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, erro | |
| return nil, errStopToken | ||
| } | ||
|
|
||
| // decodeSingle decodes the immediate operand of a backward-compatible DUPN or SWAPN instruction (EIP-8024) | ||
| // https://eips.ethereum.org/EIPS/eip-8024 | ||
| func decodeSingle(x byte) int { | ||
| // Depths 1-16 are already covered by the legacy opcodes. The forbidden byte range [91, 127] removes | ||
| // 37 values from the 256 possible immediates, leaving 219 usable values, so this encoding covers depths | ||
| // 17 through 235. The immediate is encoded as (x + 111) % 256, where 111 is chosen so that these values | ||
| // avoid the forbidden range. Decoding is simply the modular inverse (i.e. 111+145=256). | ||
| return (int(x) + 145) % 256 | ||
| } | ||
|
|
||
| // decodePair decodes the immediate operand of a backward-compatible EXCHANGE | ||
| // instruction (EIP-8024) into stack indices (n, m) where 1 <= n < m | ||
| // and n + m <= 30. The forbidden byte range [82, 127] removes 46 values from | ||
| // the 256 possible immediates, leaving exactly 210 usable bytes. | ||
| // https://eips.ethereum.org/EIPS/eip-8024 | ||
| func decodePair(x byte) (int, int) { | ||
| // XOR with 143 remaps the forbidden bytes [82, 127] to an unused corner | ||
| // of the 16x16 grid below. | ||
| k := int(x ^ 143) | ||
| // Split into row q and column r of a 16x16 grid. The 210 valid pairs | ||
| // occupy two triangles within this grid. | ||
| q, r := k/16, k%16 | ||
| // Upper triangle (q < r): pairs where m <= 16, encoded directly as | ||
| // (q+1, r+1). | ||
| if q < r { | ||
| return q + 1, r + 1 | ||
| } | ||
| // Lower triangle: pairs where m > 16, recovered as (r+1, 29-q). | ||
| return r + 1, 29 - q | ||
| } | ||
|
|
||
| func opDupN(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { | ||
| code := scope.Contract.Code | ||
| i := *pc + 1 | ||
|
|
||
| // If the immediate byte is missing, treat as 0x00 (same convention as PUSHn). | ||
| var x byte | ||
| if i < uint64(len(code)) { | ||
| x = code[i] | ||
| } | ||
|
|
||
| // This range is excluded to preserve compatibility with existing opcodes. | ||
| if x > 90 && x < 128 { | ||
| operand := x | ||
| return nil, &ErrInvalidOpCode{opcode: DUPN, operand: &operand} | ||
| } | ||
|
Comment on lines
+970
to
+974
|
||
| n := decodeSingle(x) | ||
|
|
||
| // DUPN duplicates the n'th stack item, so the stack must contain at least n elements. | ||
| if scope.Stack.len() < n { | ||
| return nil, &ErrStackUnderflow{stackLen: scope.Stack.len(), required: n} | ||
| } | ||
|
|
||
| //The n‘th stack item is duplicated at the top of the stack. | ||
| scope.Stack.push(scope.Stack.Back(n - 1)) | ||
| *pc += 1 | ||
| return nil, nil | ||
| } | ||
|
|
||
| func opSwapN(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { | ||
| code := scope.Contract.Code | ||
| i := *pc + 1 | ||
|
|
||
| // If the immediate byte is missing, treat as 0x00 (same convention as PUSHn). | ||
| var x byte | ||
| if i < uint64(len(code)) { | ||
| x = code[i] | ||
| } | ||
|
|
||
| // This range is excluded to preserve compatibility with existing opcodes. | ||
| if x > 90 && x < 128 { | ||
| operand := x | ||
| return nil, &ErrInvalidOpCode{opcode: SWAPN, operand: &operand} | ||
| } | ||
|
Comment on lines
+998
to
+1002
|
||
| n := decodeSingle(x) | ||
|
|
||
| // SWAPN operates on the top and n+1 stack items, so the stack must contain at least n+1 elements. | ||
| if scope.Stack.len() < n+1 { | ||
| return nil, &ErrStackUnderflow{stackLen: scope.Stack.len(), required: n + 1} | ||
| } | ||
|
|
||
| // The (n+1)‘th stack item is swapped with the top of the stack. | ||
| indexTop := scope.Stack.len() - 1 | ||
| indexN := scope.Stack.len() - 1 - n | ||
| scope.Stack.data[indexTop], scope.Stack.data[indexN] = scope.Stack.data[indexN], scope.Stack.data[indexTop] | ||
| *pc += 1 | ||
| return nil, nil | ||
| } | ||
|
|
||
| func opExchange(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) { | ||
| code := scope.Contract.Code | ||
| i := *pc + 1 | ||
|
|
||
| // If the immediate byte is missing, treat as 0x00 (same convention as PUSHn). | ||
| var x byte | ||
| if i < uint64(len(code)) { | ||
| x = code[i] | ||
| } | ||
|
|
||
| // This range is excluded both to preserve compatibility with existing opcodes | ||
| // and to keep decode_pair’s 16-aligned arithmetic mapping valid (0–81, 128–255). | ||
| if x > 81 && x < 128 { | ||
| operand := x | ||
| return nil, &ErrInvalidOpCode{opcode: EXCHANGE, operand: &operand} | ||
| } | ||
| n, m := decodePair(x) | ||
| need := max(n, m) + 1 | ||
|
|
||
| // EXCHANGE operates on the (n+1)'th and (m+1)'th stack items, | ||
| // so the stack must contain at least max(n, m)+1 elements. | ||
| if scope.Stack.len() < need { | ||
| return nil, &ErrStackUnderflow{stackLen: scope.Stack.len(), required: need} | ||
| } | ||
|
|
||
| // The (n+1)‘th stack item is swapped with the (m+1)‘th stack item. | ||
| indexN := scope.Stack.len() - 1 - n | ||
| indexM := scope.Stack.len() - 1 - m | ||
| scope.Stack.data[indexN], scope.Stack.data[indexM] = scope.Stack.data[indexM], scope.Stack.data[indexN] | ||
| *pc += 1 | ||
| return nil, nil | ||
| } | ||
|
|
||
| // following functions are used by the instruction jump table | ||
|
|
||
| // make log instruction function | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
enable8024setsminStackvalues that are too low for these opcodes. With the currentdecodeSinglemapping, DUPN always requires at least 17 stack items and SWAPN requires at least 18; EXCHANGE requires at least 3 (since 1 <= n < m). Because the interpreter checksminStackbefore chargingconstantGas, the current values can change trace/gas behavior for underflow cases and delay underflow detection. SetminStackto the actual minimums (or the tightest safe lower bounds) for these operations.