How to extract state and storage for a contract from the Ethereum database

For this task we will use Node.Js, the offical Ethereum client geth and the EthereumJS-VM library. It can open and process the locally stored Ethereum database which is a LevelDB. A LevelDB is an efficient key-value storage created by Google.

Syncing the blockchain with geth Ethereum client

Download the current geth version here. After extracting start it with the following flags: ./geth --datadir "/media/ren/2nd/geth/ethereum" --testnet --syncmode "full" --gcmode "archive"

The first option --datadir "path_to_ethereum_folder" specifies where the Ethereum blockchain information should be stored. The second option --testnet means that we want to sync the ropsten testnet. This is so we can begin quickly - the sync process would take way longer for the mainnet. The third and fourth option --syncmode "full" and --gcmode "archive" are set to make sure the whole blockchain with all the necessary information is downloaded. Otherwise geth would be pruning block and state information to allow a faster sync of the blockchain.

Extracting state information from the Ethereum blockchain

Now we need to install all necessary libraries: Run npm install level ethereumjs-vm in the terminal or use the package.json in my git repository.

Warning: The following code should only be executed if you have stopped the geth sync or made a copy of the geth database otherwise your Ethereum database could get corrupted.

First we define all the objects:

//imports are left out for brevity
const db = level('/media/ren/2nd/geth/ethereum/geth/chaindata') //open ethereum leveldb stored in path
const bc = new Blockchain({ db: db }) //takes db and makes it parsable

const address = '0x7ac337474ca82e0f324fbbe8493f175e0f681188' // random ropsten contract
const blockNumber = 19693 //block number which has a stored state of account

Now we are ready to extract the state and storage information for an Ethereum account:

start()

async function start () {
  const block = await getBlock(blockNumber) //get block information
  const trie = new Trie(db, block.header.stateRoot) //open state trie at this block
  const state = await getState(trie, address) //get state from state trie for address

  if (state != null) {
    const acc = new Account(state)
    const storageRoot = acc.stateRoot //get current root of storage trie

    console.log('Account fields: ')
    console.log('nonce: ' + ethUtils.bufferToInt(acc.nonce))
    console.log('balance: ' + new BN(acc.balance)) //get balance in wei
    console.log('storageRoot: ' + ethUtils.bufferToHex(storageRoot))
    console.log('codeHash: ' + ethUtils.bufferToHex(acc.codeHash))
    console.log()

    console.log('Storage trie contents for account: ')
    const storageTrie = trie.copy()
    storageTrie.root = storageRoot

    storageTrie.createReadStream()
      .on('data', function (data) {
        console.log('key: ' + ethUtils.bufferToHex(data.key))
        console.log('value: ' + ethUtils.bufferToHex(rlp.decode(data.value)))
      })
      .on('end', function () {
        console.log('Done reading storage trie.')
      })
  }
}

The function getBlock and getState are promisified versions of blockchain.getBlock(number, callback) and trie.get(address, callback), respectively. If you want to know how to promisify callback functions you can read this blogpost: How to convert a callback function to a Promise in Node.js.

Finally, the whole source file is available here.

Sources:

  1. https://github.com/ethereumjs/merkle-patricia-tree/issues/32#issuecomment-363654149
  2. https://medium.com/cybermiles/diving-into-ethereums-world-state-c893102030ed
  3. https://web.archive.org/web/20181028024327/http://wanderer.github.io/ethereum/nodejs/code/2014/08/12/running-contracts-with-vm/
Published 31 Dec 2019

Remote Developer | Vegan | Pro Wrestling geek | Gamer | Scared of his kids becoming teenagers
Remote Ren on Twitter