Open stskeeps opened 5 years ago
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) })
holy shit
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.
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.
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
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?