@unruggable/gateways
Version:
Trustless Ethereum Multichain CCIP-Read Gateway
82 lines (81 loc) • 3.21 kB
JavaScript
import { ZeroHash } from 'ethers/constants';
import { BlockProver, makeStorageKey } from '../vm.mjs';
import { toPaddedHex } from '../utils.mjs';
import { isContract, encodeProof, } from './types.mjs';
export class ZKEVMProver extends BlockProver {
static isContract = isContract;
static encodeProof = encodeProof;
static latest = this._createLatest();
async isContract(target) {
target = target.toLowerCase();
if (this.fast) {
return this.cache.get(target, async () => {
const code = await this.provider.getCode(target, this.block);
return code.length > 2;
});
}
return 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 && !isContract(accountProof)) {
return ZeroHash;
}
// check to see if we've already have a proof for this value
const storageKey = makeStorageKey(target, slot);
const storageProof = await this.proofLRU.touch(storageKey);
if (storageProof) {
return toPaddedHex(storageProof.value);
}
if (fast) {
return this.cache.get(storageKey, () => this.provider.getStorage(target, slot, this.block));
}
const proofs = await this.getProofs(target, [slot]);
return isContract(proofs)
? toPaddedHex(proofs.storageProof[0].value)
: ZeroHash;
}
async _proveNeed(need, accountRef, slotRefs) {
const m = [...slotRefs];
const accountProof = await this.proofLRU.touch(need.target);
if (accountProof && !isContract(accountProof))
m.length = 0;
if (!m.length && !need.required)
return;
const proofs = await this.getProofs(need.target, m.map(([slot]) => slot));
if (need.required) {
accountRef.proof = encodeProof(proofs.codeHashProof);
}
if (isContract(proofs)) {
m.forEach(([, ref], i) => (ref.proof = encodeProof(proofs.storageProof[i].proof)));
}
}
async getProofs(target, slots = []) {
// TODO: fix me
const proofs = await this.fetchProofs(target, slots);
this.checkStorageProofs(isContract(proofs), slots, proofs.storageProof);
return proofs;
}
async fetchProofs(target, slots = []) {
const ps = [];
for (let i = 0;;) {
ps.push(this.provider.send('zkevm_getProof', [
target,
slots
.slice(i, (i += this.proofBatchSize))
.map((slot) => toPaddedHex(slot)),
this.block,
]));
if (i >= slots.length)
break;
}
const vs = await Promise.all(ps);
for (let i = 1; i < vs.length; i++) {
vs[0].storageProof.push(...vs[i].storageProof);
}
return vs[0];
}
}