-
Notifications
You must be signed in to change notification settings - Fork 97
Description
Problem
Right now, when we use prestateTracer, we return the full storage of the smart contract at that timestamp.
In geth (besu and erigon works similarly) docs:
https://geth.ethereum.org/docs/developers/evm-tracing/built-in-tracers
prestateTracer:
- Reexecutes the transaction
- Tracks every part of state that was touched
- In prestate mode: slots accessed via SLOAD or SSTORE
- In diff mode: slots changed via SSTORE
For example, in Hardhat network (ethereumjs-vm underneath), in the account's section it lists:
- All smart contracts it queried (getCodeAt)
- All storage slots it accessed (getHtsStorageAt)
And it does this lazily, only accessing slots it actually needs.
They execute the transactions and report only the storage slots touched in that particular block/transaction.
Currently we always return full storage, which means the storage field cannot be used to determine which slots were accessed.
Tests performed
- Install geth: https://geth.ethereum.org/docs/getting-started/installing-geth
- Start geth:
geth --dev --http --http.addr 0.0.0.0 --http.port 8545 \
--http.api eth,net,web3,debug \
--http.corsdomain "*" --http.vhosts "*" \
--allow-insecure-unlock --verbosity 3You'll see an output like:
WARN [02-16|09:21:29.548] Account
WARN [02-16|09:21:29.548] ------------------
WARN [02-16|09:21:29.548] 0x71562b71999873db5b286df957af199ec94617f7 (10^49 ETH)
WARN [02-16|09:21:29.548]
WARN [02-16|09:21:29.548] Private Key
WARN [02-16|09:21:29.548] ------------------
WARN [02-16|09:21:29.548] 0xb71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291copy private key (most probably it will be the one posted here...)
- Test it on your own!
contracts/Slots.ts
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract Slots {
uint256 public SLOT_0 = 1;
uint256 public SLOT_1 = 2;
function setSlot0(uint256 x) external {
SLOT_0 = x;
}
}hardhat.config.js
// SPDX-License-Identifier: Apache-2.0
import hardhatToolboxMochaEthers from '@nomicfoundation/hardhat-toolbox-mocha-ethers';
import { defineConfig } from 'hardhat/config';
export default defineConfig({
plugins: [hardhatToolboxMochaEthers],
test: {
mocha: {
timeout: 3600000
},
},
solidity: {
version: '0.8.24',
settings: {
optimizer: {
enabled: true,
runs: 500,
},
evmVersion: 'cancun',
},
},
abiExporter: {
path: './contracts-abi',
runOnCompile: true,
},
defaultNetwork: 'local',
networks: {
local: {
type: 'http',
url: 'http://localhost:8545',
accounts: ['0xb71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291'], // Replace with the PK you've seen when starting geth (most probably it will be this one...)
chainId: 1337
}
}
});test/prestateTracer.spec.ts
import { expect } from 'chai';
import { network } from 'hardhat';
const { ethers } = await network.connect();
describe('prestateTracer storage touched slots', function () {
it('returns only touched slot(s) in storage map (slot 0 touched, slot 1 not)', async () => {
const [sender] = await ethers.getSigners();
const Slots = await ethers.getContractFactory('Slots');
const contract = await Slots.deploy();
await contract.waitForDeployment();
const tx = await contract.setSlot0(123);
const receipt = await tx.wait();
const txHash = receipt!.hash;
const trace: any = await sender.provider.send('debug_traceTransaction', [
txHash,
{ tracer: 'prestateTracer' },
]);
const contractAddress = (await contract.getAddress()).toLowerCase();
const storage = trace[contractAddress].storage ?? {};
const SLOT_0 = '0x' + '0'.repeat(63) + '0';
const SLOT_1 = '0x' + '0'.repeat(63) + '1';
// We expect slot 0 to be present, slot 1 to be absent
expect(storage).to.have.property(SLOT_0);
expect(storage).to.not.have.property(SLOT_1);
// Even though THERE is something written to the storage slot 1:
const valueInSlot2 = await sender.provider.send('eth_getStorageAt', [contractAddress, SLOT_1, 'latest']);
expect(Number(valueInSlot2)).to.not.be.equal(0);
});
});Run:
npx hardhat test --network localSee the output:
❯ npx hardhat test --network local
Compiled 1 Solidity file with solc 0.8.24 (evm target: cancun)
No Solidity tests to compile
Running Solidity tests
Running Mocha tests
prestateTracer storage touched slots
✔ returns only touched slot(s) in storage map (slot 0 touched, slot 1 not) (66ms)
1 passing (67ms)
1 passing (1 mocha)If you'll run the same tests against hiero-json-rpc-relay they will fail.
Solution
Instead of returning full storage Prestate Tracer Response object's storage field we should:
- Inspect all of the slots listed in the structLogs when using opcodeLogger tracer
- Return only the slots accessed during execution
Alternatives
Change field description to make it clear we are always returning full contract state there.