Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: could this theoretically deliver a proof of eth_call result? #16

Open
stskeeps opened this issue Jul 5, 2019 · 5 comments
Open

Comments

@stskeeps
Copy link

stskeeps commented Jul 5, 2019

Been playing with verifying ENS resolver results against a known good block hash, but that often takes a bit of poking around the contract to find right getStorageAt

Could this theoretically use ethereumjs-vm or the likes + storageAgainstBlockHash for SLOAD queries?

@stskeeps
Copy link
Author

stskeeps commented Jul 6, 2019

Answering my own question, this is a sample tx eth_call style done against a ERC20 token asking for balance -- running the tx locally instead of remote node:

This uses ethereumjs-vm 2.6.0. Bits and pieces liberally borrowed from https://github.com/MetaMask/eth-json-rpc-middleware/blob/master/vm.js


async function run() {
  const createVm = require('ethereumjs-vm/dist/hooked')
  const blockFromRpc = require('ethereumjs-block/from-rpc')
  const FakeTransaction = require('ethereumjs-tx').FakeTransaction
  const ethers = require('ethers');
  
  var provider = new ethers.providers.JsonRpcProvider('https://foundation.query.zippie.org/rpc')
  const { GetAndVerify, GetProof, VerifyProof } = require('eth-proof')
  let blockNumber = await provider.getBlockNumber()
  let block = await provider.getBlock(blockNumber)
  let knownHash = block.hash
  let getAndVerify = new GetAndVerify('https://foundation.query.zippie.org/rpc')
  
  let vm = createVm({homestead: true}, {
    fetchAccountBalance: function(addressHex, cb) {
      getAndVerify.accountAgainstBlockHash(addressHex, knownHash).then((account) => {
        console.log('fetchAccountBalance: ' + addressHex)
        cb(null, account.balance)
      })
    },  
    fetchAccountNonce: function(addressHex, cb) {
      console.log('fetchAccountNonce: '  + addressHex)
      getAndVerify.accountAgainstBlockHash(addressHex, knownHash).then((account) => {
        cb(null, account.nonce)
      })
    },
    fetchAccountCode: function(addressHex, cb) {
      console.log('fetchAccountCode: ' + addressHex)
      getAndVerify.accountAgainstBlockHash(addressHex, knownHash).then((account) => {
        provider.getBlock(knownHash).then((block) => {
         provider.getCode(addressHex, block.number).then((code) => {           
           let providerCodeHash = ethers.utils.keccak256(code)
           console.log('providerCodeHash: ' + providerCodeHash)
           console.log('account.codeHash: ' + account.codeHash.toString('hex'))
           
           if ('0x' + account.codeHash.toString('hex') !== providerCodeHash) {
             throw "Provider code hash failed verification"
           }
           cb(null, code)
         })
       })
      })
    },
    fetchAccountStorage: function(addressHex, keyHex, cb) {
      console.log('fetchAccountStorage: ' + addressHex + ' - ' + keyHex)
      getAndVerify.storageAgainstBlockHash(addressHex, keyHex, knownHash).then((result) => {
        cb(null, result)
      })
    }
  }) 
  var txParams = {}  
  txParams.from = '0xf4eb9bb30a8f61991220cb31762bf2f456bc7fee'
  txParams.gasLimit = '0x' + Number('32175').toString(16) 
  txParams.to = '0xedd7c94fd7b4971b916d15067bc454b9e1bad980'
  txParams.value = '0x00'
  txParams.nonce = '0x2d'
  txParams.data = '0x70a082310000000000000000000000002a0c0dbecc7e4d658f48e01e3fa353f44050c208'
  const tx = new FakeTransaction(txParams)
  vm.runTx({
    tx: tx,
//    block: block, XXX fixme?
    skipNonce: true,
      skipBalance: true
    }, function (err, results) {
      if (err) throw err
      if (results.error) {
        throw new Error('VM error: ' + results.error)
      }
      if (results.vm && results.vm.exception !== 1 && results.vm.exceptionError !== 'invalid opcode') {
        return new Error('VM Exception while executing ' + req.method + ': ' + results.vm.exceptionError)
      }

      console.log(results)
      console.log(results.vm.runState.returnValue.toString('hex'))
    })
}

run().then(() => {}).catch((err) => { console.log(err) })

@zmitton
Copy link
Owner

zmitton commented Jul 9, 2019

holy shit

@zmitton
Copy link
Owner

zmitton commented Jul 9, 2019

this is what I wanted someone to do. actually this would return a single proof for each value looked up - resulting in lots or repeated data. I've been hoping someone would do this though: In theory, you could combine all the data that must be "touched" by a tx into a special MPT that has only the proof nodes needed. Then the VM could use that as if it were the normal MPT.

This code is cool. It's a great start.

@stskeeps
Copy link
Author

stskeeps commented Jul 9, 2019

Yeah, I think there's a lot of room for making centralised nodes more trustable (really needed for UX)

I'm currently working on next proof of concept in line -- running eth_call against just a known good block hash -- and no actual Ehereum node to talk to.

.. where the block hash would be downloaded through IPFS, state trie navigated with IPLD and then storage trie same way (https://github.com/ipld/js-ipld-ethereum )

There's some partial support in Parity to act as such a source of IPFS objects but working to get storage trie available too.

@zmitton
Copy link
Owner

zmitton commented Jul 19, 2019

Yeah, I think there's a lot of room for making centralised nodes more trustable (really needed for UX)

agreed. If you find anything that fits this repo feel free to make PR.

If you are communicating with an eth-node we can maybe add some sort of eth_callWithProof functionality to geth/parity that returns the proofNodes that got touched during the ethcall (that way you can rerun the eth_call locally to verify).

If you are use IPFS you cant do that, but you could hack the MPT software to request from IPFS durring each _lookupNode operation. Of course this is lots if slow remote lookups! just an idea

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants