From 6b032fcd991deb4969c4bc841170bf178470cc3a Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Wed, 11 Feb 2026 16:58:19 -0300 Subject: [PATCH 1/4] Update EIP-8024: Switch to branchless normalization --- EIPS/eip-8024.md | 42 ++++++++++++++++++------------------------ 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/EIPS/eip-8024.md b/EIPS/eip-8024.md index b9a5fa56ec91e4..e8ea6b0cffa369 100644 --- a/EIPS/eip-8024.md +++ b/EIPS/eip-8024.md @@ -2,7 +2,7 @@ eip: 8024 title: Backward compatible SWAPN, DUPN, EXCHANGE description: Introduce additional instructions for manipulating the stack which allow accessing the stack at higher depths -author: Francisco Giordano (@frangio), Charles Cooper (@charles-cooper), Alex Beregszaszi (@axic) +author: Francisco Giordano (@frangio), Charles Cooper (@charles-cooper), Alex Beregszaszi (@axic), Paweł Bylica (@chfast) discussions-to: https://ethereum-magicians.org/t/eip-8024-backward-compatible-swapn-dupn-exchange/25486 status: Review type: Standards Track @@ -78,14 +78,11 @@ The auxiliary functions `decode_single` and `decode_pair` are defined by the fol ```python def decode_single(x: int) -> int: assert 0 <= x <= 90 or 128 <= x <= 255 - if x <= 90: - return x + 17 - else: - return x - 20 + return (x + 145) & 0xff def decode_pair(x: int) -> tuple[int, int]: assert 0 <= x <= 79 or 128 <= x <= 255 - k = x if x <= 79 else x - 48 + k = (x + 128) & 0xff q, r = divmod(k, 16) if q < r: return q + 1, r + 1 @@ -98,10 +95,7 @@ Assemblers and compilers must emit a 1-byte immediate after each of these opcode ```python def encode_single(n: int) -> int: assert 17 <= n <= 235 - if n <= 107: - return n - 17 - else: - return n + 20 + return (n + 111) & 0xff def encode_pair(n: int, m: int) -> int: assert 1 <= n <= 13 and n < m <= 29 and n + m <= 30 @@ -110,7 +104,7 @@ def encode_pair(n: int, m: int) -> int: else: q, r = 29 - m, n - 1 k = 16 * q + r - return k if k <= 79 else k + 48 + return (k + 128) & 0xff ``` ## Rationale @@ -166,29 +160,29 @@ This has no effect on contracts that would never attempt to execute the opcodes ### Assembly/Disassembly -- `e600` is `[DUPN 17]` -- `e780` is `[SWAPN 108]` -- `e6005b` is `[DUPN 17, JUMPDEST]` +- `e680` is `[DUPN 17]` +- `e7db` is `[SWAPN 108]` +- `e6805b` is `[DUPN 17, JUMPDEST]` - `e75b` is `[INVALID_SWAPN, JUMPDEST]` - `e6605b` is `[INVALID_DUPN, PUSH1 0x5b]` - `e7610000` is `[INVALID_SWAPN, PUSH2 0x0000]` - `e65f` is `[INVALID_DUPN, PUSH0]` -- `e812` is `[EXCHANGE 2 3]` -- `e8d0` is `[EXCHANGE 1 19]` +- `e892` is `[EXCHANGE 2 3]` +- `e820` is `[EXCHANGE 1 19]` - `e850` is `[INVALID_EXCHANGE, POP]` ### Execution -- `60016000808080808080808080808080808080e600` results in 18 stack items, the top of the stack valued 1, the bottom of the stack valued 1, the rest valued 0 - - `60016000808080808080808080808080808080e6` at the end of the code is equivalent to above -- `600160008080808080808080808080808080806002e700` results in 18 stack items, the top of the stack valued 1, the bottom of the stack valued 2, the rest valued 0 - - `600160008080808080808080808080808080806002e7` at the end of the code is equivalent to above -- `600060016002e801` results in 3 stack items, from top to bottom: [2, 0, 1] -- `600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060006000600060016002e8` results in 30 stack items, the top of the stack valued 2, the bottom of the stack valued 1, the rest valued 0 +- `60016000808080808080808080808080808080e680` results in 18 stack items, the top of the stack valued 1, the bottom of the stack valued 1, the rest valued 0 + - `60016000808080808080808080808080808080e6` at the end of the code results in exceptional halt (`code[pc + 1] = 0`, so `DUPN 145`) +- `600160008080808080808080808080808080806002e780` results in 18 stack items, the top of the stack valued 1, the bottom of the stack valued 2, the rest valued 0 + - `600160008080808080808080808080808080806002e7` at the end of the code results in exceptional halt (`code[pc + 1] = 0`, so `SWAPN 145`) +- `600060016002e881` results in 3 stack items, from top to bottom: [2, 0, 1] +- `600080808080808080808080808080808080808080808080808080808060016002e880` results in 30 stack items, the top of the stack valued 2, the bottom of the stack valued 1, the rest valued 0 - `e75b` reverts - `600456e65b` executes successfully (`PUSH 04 JUMP INVALID_DUPN JUMPDEST`) -- `600060006000e80115` results in 3 stack items, the top of the stack valued 1, the rest valued 0 -- `6000808080808080808080808080808080e600` results in exceptional halt +- `60008080e88115` results in 3 stack items, the top of the stack valued 1, the rest valued 0 +- `6000808080808080808080808080808080e680` results in exceptional halt ## Security Considerations From 2fc595a002cdf18ab21928e5d32255006b40b1bb Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Fri, 13 Feb 2026 10:27:45 -0300 Subject: [PATCH 2/4] use decimal --- EIPS/eip-8024.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/EIPS/eip-8024.md b/EIPS/eip-8024.md index e8ea6b0cffa369..b54979da2b8cb4 100644 --- a/EIPS/eip-8024.md +++ b/EIPS/eip-8024.md @@ -78,11 +78,11 @@ The auxiliary functions `decode_single` and `decode_pair` are defined by the fol ```python def decode_single(x: int) -> int: assert 0 <= x <= 90 or 128 <= x <= 255 - return (x + 145) & 0xff + return (x + 145) % 256 def decode_pair(x: int) -> tuple[int, int]: assert 0 <= x <= 79 or 128 <= x <= 255 - k = (x + 128) & 0xff + k = (x + 128) % 256 q, r = divmod(k, 16) if q < r: return q + 1, r + 1 @@ -95,7 +95,7 @@ Assemblers and compilers must emit a 1-byte immediate after each of these opcode ```python def encode_single(n: int) -> int: assert 17 <= n <= 235 - return (n + 111) & 0xff + return (n + 111) % 256 def encode_pair(n: int, m: int) -> int: assert 1 <= n <= 13 and n < m <= 29 and n + m <= 30 @@ -104,7 +104,7 @@ def encode_pair(n: int, m: int) -> int: else: q, r = 29 - m, n - 1 k = 16 * q + r - return (k + 128) & 0xff + return (k + 128) % 256 ``` ## Rationale From d4f457867d3df4e2c63605ad52542b0a3132dc8d Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Fri, 13 Feb 2026 10:39:34 -0300 Subject: [PATCH 3/4] increase valid range for exchange --- EIPS/eip-8024.md | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/EIPS/eip-8024.md b/EIPS/eip-8024.md index b54979da2b8cb4..5727b497dfa405 100644 --- a/EIPS/eip-8024.md +++ b/EIPS/eip-8024.md @@ -61,7 +61,7 @@ Formally, when `code[pc]` is one of these opcodes, the VM executes as follows: - `EXCHANGE`: 1. Charge 3 gas. 2. Let `x = code[pc + 1]`. - 3. If `79 < x < 128`, halt with exceptional failure. + 3. If `81 < x < 128`, halt with exceptional failure. 4. Let `n, m = decode_pair(x)`. 5. If `m + 1 > len(stack)`, halt with exceptional failure. 5. Swap `stack[top - n]` and `stack[top - m]`. @@ -81,8 +81,8 @@ def decode_single(x: int) -> int: return (x + 145) % 256 def decode_pair(x: int) -> tuple[int, int]: - assert 0 <= x <= 79 or 128 <= x <= 255 - k = (x + 128) % 256 + assert 0 <= x <= 81 or 128 <= x <= 255 + k = x ^ 143 q, r = divmod(k, 16) if q < r: return q + 1, r + 1 @@ -98,13 +98,13 @@ def encode_single(n: int) -> int: return (n + 111) % 256 def encode_pair(n: int, m: int) -> int: - assert 1 <= n <= 13 and n < m <= 29 and n + m <= 30 + assert 1 <= n <= 14 and n < m <= 29 and n + m <= 30 if m <= 16: q, r = n - 1, m - 1 else: q, r = 29 - m, n - 1 k = 16 * q + r - return (k + 128) % 256 + return k ^ 143 ``` ## Rationale @@ -167,9 +167,11 @@ This has no effect on contracts that would never attempt to execute the opcodes - `e6605b` is `[INVALID_DUPN, PUSH1 0x5b]` - `e7610000` is `[INVALID_SWAPN, PUSH2 0x0000]` - `e65f` is `[INVALID_DUPN, PUSH0]` -- `e892` is `[EXCHANGE 2 3]` -- `e820` is `[EXCHANGE 1 19]` -- `e850` is `[INVALID_EXCHANGE, POP]` +- `e89d` is `[EXCHANGE 2 3]` +- `e82f` is `[EXCHANGE 1 19]` +- `e850` is `[EXCHANGE 14 16]` +- `e851` is `[EXCHANGE 14 15]` +- `e852` is `[INVALID_EXCHANGE, MSTORE]` ### Execution @@ -177,11 +179,12 @@ This has no effect on contracts that would never attempt to execute the opcodes - `60016000808080808080808080808080808080e6` at the end of the code results in exceptional halt (`code[pc + 1] = 0`, so `DUPN 145`) - `600160008080808080808080808080808080806002e780` results in 18 stack items, the top of the stack valued 1, the bottom of the stack valued 2, the rest valued 0 - `600160008080808080808080808080808080806002e7` at the end of the code results in exceptional halt (`code[pc + 1] = 0`, so `SWAPN 145`) -- `600060016002e881` results in 3 stack items, from top to bottom: [2, 0, 1] -- `600080808080808080808080808080808080808080808080808080808060016002e880` results in 30 stack items, the top of the stack valued 2, the bottom of the stack valued 1, the rest valued 0 +- `600060016002e88e` results in 3 stack items, from top to bottom: [2, 0, 1] +- `600080808080808080808080808080808080808080808080808080808060016002e88f` results in 30 stack items, the top of the stack valued 2, the bottom of the stack valued 1, the rest valued 0 - `e75b` reverts - `600456e65b` executes successfully (`PUSH 04 JUMP INVALID_DUPN JUMPDEST`) -- `60008080e88115` results in 3 stack items, the top of the stack valued 1, the rest valued 0 +- `60008080e88e15` results in 3 stack items, the top of the stack valued 1, the rest valued 0 +- `e852` reverts - `6000808080808080808080808080808080e680` results in exceptional halt ## Security Considerations From 2304fef6255bacb87b7e13316c2ce9bc38829021 Mon Sep 17 00:00:00 2001 From: Francisco Giordano Date: Thu, 19 Feb 2026 13:49:18 -0300 Subject: [PATCH 4/4] improve end-of-code tests --- EIPS/eip-8024.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/EIPS/eip-8024.md b/EIPS/eip-8024.md index 5727b497dfa405..8f1302d6b69eed 100644 --- a/EIPS/eip-8024.md +++ b/EIPS/eip-8024.md @@ -176,9 +176,8 @@ This has no effect on contracts that would never attempt to execute the opcodes ### Execution - `60016000808080808080808080808080808080e680` results in 18 stack items, the top of the stack valued 1, the bottom of the stack valued 1, the rest valued 0 - - `60016000808080808080808080808080808080e6` at the end of the code results in exceptional halt (`code[pc + 1] = 0`, so `DUPN 145`) - `600160008080808080808080808080808080806002e780` results in 18 stack items, the top of the stack valued 1, the bottom of the stack valued 2, the rest valued 0 - - `600160008080808080808080808080808080806002e7` at the end of the code results in exceptional halt (`code[pc + 1] = 0`, so `SWAPN 145`) +- `600260008080808080600160008080808080808080e8` at the end of code results in 17 stack items, the bottom of the stack valued 1, the 10th stack item from the top valued 2, the rest valued 0 - `600060016002e88e` results in 3 stack items, from top to bottom: [2, 0, 1] - `600080808080808080808080808080808080808080808080808080808060016002e88f` results in 30 stack items, the top of the stack valued 2, the bottom of the stack valued 1, the rest valued 0 - `e75b` reverts