From 1a21f86ac743d6b3006f8cf5bcaa90bf77e1a083 Mon Sep 17 00:00:00 2001
From: Jochem Brouwer <jochembrouwer96@gmail.com>
Date: Mon, 19 Aug 2024 04:22:02 +0200
Subject: [PATCH] vm: 7702: correctly handle self-sponsored txs [no ci]

---
 packages/vm/src/runTx.ts | 31 +++++++++++++++++++++++++++----
 1 file changed, 27 insertions(+), 4 deletions(-)

diff --git a/packages/vm/src/runTx.ts b/packages/vm/src/runTx.ts
index 1fce1d8b6a..c9f5f8a3a1 100644
--- a/packages/vm/src/runTx.ts
+++ b/packages/vm/src/runTx.ts
@@ -7,6 +7,7 @@ import {
   Account,
   Address,
   BIGINT_0,
+  BIGINT_1,
   KECCAK256_NULL,
   bytesToBigInt,
   bytesToHex,
@@ -292,8 +293,21 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise<RunTxResult> {
   }
   // EIP-3607: Reject transactions from senders with deployed code
   if (vm.common.isActivatedEIP(3607) && !equalsBytes(fromAccount.codeHash, KECCAK256_NULL)) {
-    const msg = _errorMsg('invalid sender address, address is not EOA (EIP-3607)', vm, block, tx)
-    throw new Error(msg)
+    if (vm.common.isActivatedEIP(7702)) {
+      const code = await state.getCode(caller)
+      if (!equalsBytes(code.slice(0, 3), new Uint8Array([0xef, 0x01, 0x00]))) {
+        const msg = _errorMsg(
+          'invalid sender address, address is not EOA (EIP-3607)',
+          vm,
+          block,
+          tx,
+        )
+        throw new Error(msg)
+      }
+    } else {
+      const msg = _errorMsg('invalid sender address, address is not EOA (EIP-3607)', vm, block, tx)
+      throw new Error(msg)
+    }
   }
 
   // Check balance against upfront tx cost
@@ -464,14 +478,23 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise<RunTxResult> {
           continue
         }
       }
-      if (account.nonce !== bytesToBigInt(nonce)) {
+
+      // Nonce check
+      if (caller.toString() === authority.toString()) {
+        if (account.nonce + BIGINT_1 !== bytesToBigInt(nonce)) {
+          // Edge case: caller is the authority, so is self-signing the delegation
+          // In this case, we "virtually" bump the account nonce by one
+          // We CANNOT put this updated nonce into the account trie, because then
+          // the EVM will bump the nonce once again, thus resulting in a wrong nonce
+          continue
+        }
+      } else if (account.nonce !== bytesToBigInt(nonce)) {
         continue
       }
 
       if (accountExists) {
         const refund = tx.common.param('perEmptyAccountCost') - tx.common.param('perAuthBaseGas')
         gasRefund += refund
-        await vm.evm.journal.putAccount(caller, fromAccount)
       }
 
       account.nonce++