UNPKG

@unruggable/gateways

Version:

Trustless Ethereum Multichain CCIP-Read Gateway

126 lines (125 loc) 5.29 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EthProver = void 0; const types_js_1 = require("./types.cjs"); const vm_js_1 = require("../vm.cjs"); const constants_1 = require("ethers/constants"); const utils_js_1 = require("../utils.cjs"); class EthProver extends vm_js_1.BlockProver { static encodeProof = types_js_1.encodeProof; static isContract = types_js_1.isContract; static latest = this._createLatest(); async isContract(target) { target = target.toLowerCase(); if (this.fast) { return this.cache.get(target, async (a) => { // note: this actually reverts when the block is bad // eg. {"code": -32602, "message": "Unknown block number"} const code = await this.provider.getCode(a, this.block); return code.length > 2; }); } return (0, types_js_1.isContract)(await this.getProofs(target)); } async getStorage(target, slot, fast = this.fast) { target = target.toLowerCase(); // check to see if we know this target isn't a contract without invoking provider // this is almost equivalent to: await isContract(target) const accountProof = await this.proofLRU.touch(target); if (accountProof && !(0, types_js_1.isContract)(accountProof)) { return constants_1.ZeroHash; } // check to see if we've already have a proof for this value const storageKey = (0, vm_js_1.makeStorageKey)(target, slot); const storageProof = await this.proofLRU.touch(storageKey); if (storageProof) { return (0, utils_js_1.toPaddedHex)(storageProof.value); } if (fast) { return this.cache.get(storageKey, () => { return (0, utils_js_1.fetchStorage)(this.provider, target, slot, this.block); }); } const proofs = await this.getProofs(target, [slot]); return (0, types_js_1.isContract)(proofs) ? (0, utils_js_1.toPaddedHex)(proofs.storageProof[0].value) : constants_1.ZeroHash; } async _proveNeed(need, accountRef, slotRefs) { const m = [...slotRefs]; const accountProof = await this.proofLRU.peek(need.target); if (accountProof && !(0, types_js_1.isContract)(accountProof)) m.length = 0; const proofs = await this.getProofs(need.target, m.map(([slot]) => slot)); accountRef.proof = (0, types_js_1.encodeProof)(proofs.accountProof); if ((0, types_js_1.isContract)(proofs)) { m.forEach(([, ref], i) => (ref.proof = (0, types_js_1.encodeProof)(proofs.storageProof[i].proof))); } } async getProofs(target, slots = []) { target = target.toLowerCase(); const missing = []; // indices of slots we don't have proofs for const { promise, resolve, reject } = (0, utils_js_1.withResolvers)(); // create a blocker // 20240708: must setup blocks before await let accountProof = this.proofLRU.touch(target); if (!accountProof) { // missing account proof, so block it this.proofLRU.setFuture(target, promise.then(() => accountProof)); } // check if we're missing any slots const storageProofs = slots.map((slot, i) => { const key = (0, vm_js_1.makeStorageKey)(target, slot); const p = this.proofLRU.touch(key); if (!p) { // missing storage proof, so block it this.proofLRU.setFuture(key, promise.then(() => storageProofs[i])); missing.push(i); } return p; }); // check if we need something if (!accountProof || missing.length) { try { const { storageProof: v, ...a } = await this.fetchProofs(target, missing.map((x) => slots[x])); // update cache accountProof = a; missing.forEach((x, i) => (storageProofs[x] = v[i])); resolve(); // unblock } catch (err) { reject(err); throw err; } } // reassemble const [a, v] = await Promise.all([ accountProof, Promise.all(storageProofs), ]); this.checkStorageProofs((0, types_js_1.isContract)(a), slots, v); return { storageProof: v, ...a }; } async fetchProofs(target, slots = []) { const ps = []; for (let i = 0;;) { ps.push(this.provider.send('eth_getProof', [ target, slots .slice(i, (i += this.proofBatchSize)) .map((slot) => (0, utils_js_1.toPaddedHex)(slot)), this.block, ])); if (i >= slots.length) break; } const vs = await Promise.all(ps); // note: this returns null when block is bad if (!vs[0]) throw new Error(`unprovable block: ${this.block}`); for (let i = 1; i < vs.length; i++) { vs[0].storageProof.push(...vs[i].storageProof); } return vs[0]; } } exports.EthProver = EthProver;