bluealloy / revm

Ethereum Virtual Machine written in rust that is fast and simple to use
https://bluealloy.github.io/revm/
MIT License
1.5k stars 482 forks source link

Self-destruct contract still have storage #1543

Closed rllola closed 1 month ago

rllola commented 1 month ago

I am trying to replay all the blocks of mainnet using revm. I have been stuck at blocks 49157 as their state root calculated is different from the one in the block header.

After some digging I noticed a difference in the storage trie for the smart contract created in the only transaction in this block.

With revm, we have 1 storage slot used

    0xf9c2a99482823c30062ded531e049163034273c2: Account {
        info: AccountInfo {
            balance: 0,
            nonce: 0,
            code_hash: 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470,
            code: Some(
                LegacyAnalyzed(
                    LegacyAnalyzedBytecode {
                        bytecode: 0x000000000000000000000000000000000000000000000000000000000000000000,
                        original_len: 0,
                        jump_table: JumpTable {
                            map: "0000000000",
                        },
                    },
                ),
            ),
        },
        storage: {
            1: StorageSlot {
                previous_or_original_value: 0,
                present_value: 0,
            },
        },
        status: AccountStatus(
            Created | Touched | LoadedAsNotExisting,
        ),
    },

using a JSON-RPC endpoint, it indicates it have none.

    "address": "0xf9c2a99482823c30062ded531e049163034273c2",
    "balance": "0x0",
    "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470",
    "nonce": "0x0",
    "storageHash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
    "storageProof": [
      {
        "key": "0x0",
        "proof": [],
        "value": "0x0"
      }
    ]

The smart contract has apparently self-destructed. My understanding is that it remove all the stored values when it happens. I know someone already tried to use revm to replay all the blocks so could it be that I am missing something ?

I have instanciated the evm like this

    let mut evm = Evm::builder()
                    .with_db(db)
                    .with_external_context(tracer)
                    .append_handler_register(inspector_handle_register)
                    .with_spec_id(revm::primitives::SpecId::FRONTIER)
                    .modify_cfg_env(|c| {
                        c.chain_id = 1;
                    })
                    .build();

Cheers

rllola commented 1 month ago

I might have solve it... I realized that the account status for this specific contract is StorageCleared so I guess it indicates me that when calculating the storage root I should just take null...

I ended up having the same state root when doing this so I guess I can close this issue.

rllola commented 1 month ago

Reopening because it didn't work actually... Other account with StorageCleared status return the wrong storage hash...

rakita commented 1 month ago

Revm is part of Reth so you can assume it is working correctly.

SELFDESTRUCT just marks an account for destruction, selfdestruction is applied AFTER transaction finishes its execution. You can check State that is used in Reth: https://github.com/bluealloy/revm/tree/main/crates/revm/src/db/states Or simple example in CacheDb: https://github.com/bluealloy/revm/blob/c2a04cbb2ba5862e56212caa6ceca2c11bcac3d0/crates/revm/src/db/in_memory_db.rs#L136-L142

rllola commented 1 month ago

Revm is part of Reth so you can assume it is working correctly. That was my thought too.

I am trying to calculate the the storage root after I did the commit indeed. The only difference I get is the storage for 0xf9c2a99482823c30062ded531e049163034273c2 contract which appears with this transaction (https://etherscan.io/tx/0xe9b2d3e8a2bc996a1c7742de825fdae2466ae783ce53484304efffe304ff232d#statechange)

Using JSON-RPC or Etherscan it is telling me that there is nothing in the storage trie for this smart contract. But using revm it is telling me there is. I have no doubt I am doing something wrong but I still don't know what I am missing.

When calculating the state root for this block I just ignore the storage for 0xf9c2a99482823c30062ded531e049163034273c2 and I have the correct root. I just don't know why.

Could it be some early weird rule in Ethereum that I need to consider ?

rllola commented 1 month ago

I have looked into reth code base and I have found this filter in case the value is 0.

https://github.com/paradigmxyz/reth/blob/cc502523ca6f7a0856dc09a6282257c53f87c4f7/crates/trie/common/src/account.rs#L38

Could it be this ?

rllola commented 1 month ago

Adding this weird rule when calculating storage root is working. I am no sure why this is being isgnored when the value is 0 specifically ?

Thanks @rakita for your time. Closing this for good now.