cli.ts 4.34 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
import fs from 'fs'

import { Command } from 'commander'
import { ethers } from 'ethers'
import { getContractInterface } from '@eth-optimism/contracts'

import { version } from '../package.json'
import { advancedQueryFilter } from '../src/advanced-query'

const program = new Command()

program
  .name('migration-data-query')
  .description('CLI for querying Bedrock migration data')
  .version(version)

17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
program
  .command('parse-state-dump')
  .description('parses state dump to json')
  .option('--file <file>', 'path to state dump file')
  .action(async (options) => {
    const iface = getContractInterface('OVM_L2ToL1MessagePasser')
    const dump = fs.readFileSync(options.file, 'utf-8')

    const addrs: string[] = []
    const msgs: any[] = []
    for (const line of dump.split('\n')) {
      if (line.startsWith('ETH')) {
        addrs.push(line.split('|')[1].replace('\r', ''))
      } else if (line.startsWith('MSG')) {
        const msg = '0x' + line.split('|')[2].replace('\r', '')
        const parsed = iface.decodeFunctionData('passMessageToL1', msg)
        msgs.push({
          who: line.split('|')[1],
          msg: parsed._message,
        })
      }
    }

40 41 42 43
    fs.writeFileSync(
      './data/evm-addresses.json',
      JSON.stringify(addrs, null, 2)
    )
44 45 46
    fs.writeFileSync('./data/evm-messages.json', JSON.stringify(msgs, null, 2))
  })

47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
program
  .command('evm-sent-messages')
  .description('queries messages sent after the EVM upgrade')
  .option('--rpc <rpc>', 'rpc url to use')
  .action(async (options) => {
    const provider = new ethers.providers.JsonRpcProvider(options.rpc)

    const xdm = new ethers.Contract(
      '0x4200000000000000000000000000000000000007',
      getContractInterface('L2CrossDomainMessenger'),
      provider
    )

    const sent: any[] = await advancedQueryFilter(xdm, {
      queryFilter: xdm.filters.SentMessage(),
    })

    const messages: any[] = []
    for (const s of sent) {
      messages.push({
        who: '0x4200000000000000000000000000000000000007',
        msg: xdm.interface.encodeFunctionData('relayMessage', [
          s.args.target,
          s.args.sender,
          s.args.message,
          s.args.messageNonce,
        ]),
      })
    }

    fs.writeFileSync(
      './data/evm-messages.json',
      JSON.stringify(messages, null, 2)
    )
  })

program
  .command('sent-slots')
  .description('queries storage slots in the message passer')
  .option('--rpc <rpc>', 'rpc url to use')
  .action(async (options) => {
    const provider = new ethers.providers.JsonRpcProvider(options.rpc)

    let nextKey = '0x'
    let slots: any[] = []
    while (nextKey) {
      const latestBlock = await provider.getBlock('latest')
      const ret = await provider.send('debug_storageRangeAt', [
        latestBlock.hash,
        0,
        '0x4200000000000000000000000000000000000000',
        nextKey,
        10000,
      ])

      slots = slots.concat(
        Object.values(ret.storage).map((s: any) => {
          return s.key
        })
      )

      // Update next key and potentially try again
      nextKey = ret.nextKey
    }

    fs.writeFileSync('./data/slots.json', JSON.stringify(slots, null, 2))
  })

program
  .command('accounting')
  .description('verifies that we have sufficient slot data')
  .action(async () => {
    const parseMessageFile = (
      path: string
    ): Array<{
      message: string
      slot: string
    }> => {
      const messages: any[] = JSON.parse(fs.readFileSync(path, 'utf8'))
      return messages.map((message) => {
        return {
          message,
          slot: ethers.utils.keccak256(
            ethers.utils.hexConcat([
              ethers.utils.keccak256(
                ethers.utils.hexConcat([message.msg, message.who])
              ),
              ethers.constants.HashZero,
            ])
          ),
        }
      })
    }

    const ovmMessages = parseMessageFile('./data/ovm-messages.json')
    const evmMessages = parseMessageFile('./data/evm-messages.json')
    const slotList: string[] = JSON.parse(
      fs.readFileSync('./data/slots.json', 'utf8')
    )

    const unaccounted = slotList.filter((slot) => {
      return (
        !ovmMessages.some((m) => m.slot === slot) &&
        !evmMessages.some((m) => m.slot === slot)
      )
    })

    console.log(`Total slots: ${slotList.length}`)
    console.log(`Unaccounted slots: ${unaccounted.length}`)
  })

program.parse(process.argv)