@unruggable/gateways
Version:
Trustless Ethereum Multichain CCIP-Read Gateway
126 lines (125 loc) • 5.29 kB
JavaScript
;
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;