Skip to content

Commit

Permalink
Fix some issues in the prover
Browse files Browse the repository at this point in the history
  • Loading branch information
nazarhussain committed Jul 31, 2023
1 parent 0563661 commit cf67f92
Show file tree
Hide file tree
Showing 7 changed files with 633 additions and 17 deletions.
12 changes: 6 additions & 6 deletions packages/prover/src/proof_provider/ordered_map.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
export class OrderedMap<T> extends Map<number, T> {
private _min = 0;
private _max = 0;
private _min?: number;
private _max?: number;

get min(): number {
get min(): number | undefined {
return this._min;
}

get max(): number {
get max(): number | undefined {
return this._max;
}

set(key: number, value: T): this {
if (key < this._min) {
if (this._min === undefined || key < this._min) {
this._min = key;
}

if (key > this._max) {
if (this._max === undefined || key > this._max) {
this._max = key;
}

Expand Down
41 changes: 31 additions & 10 deletions packages/prover/src/proof_provider/payload_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ type BlockCLRoot = string;
* The in-memory store for the execution payloads to be used to verify the proofs
*/
export class PayloadStore {
// We store the block numbers only for finalized blocks
// We store the block root from execution for finalized blocks
// As these blocks are finalized, so not to be worried about conflicting roots
private finalizedRoots = new OrderedMap<BlockELRoot>();

// Unfinalized blocks are stored by the roots of the beacon chain
// Unfinalized blocks may change over time and may have conflicting roots
// We can receive multiple light-client headers for the same block of execution
// So we why store unfinalized payloads by their CL root, which is only used
// in processing the light-client headers
private unfinalizedRoots = new Map<BlockCLRoot, BlockELRoot>();

// Payloads store with BlockELRoot as key
Expand All @@ -27,7 +31,13 @@ export class PayloadStore {
constructor(private opts: {api: Api; logger: Logger}) {}

get finalized(): allForks.ExecutionPayload | undefined {
const finalizedMaxRoot = this.finalizedRoots.get(this.finalizedRoots.max);
const maxBlockNumberForFinalized = this.finalizedRoots.max;

if (maxBlockNumberForFinalized === undefined) {
return;
}

const finalizedMaxRoot = this.finalizedRoots.get(maxBlockNumberForFinalized);
if (finalizedMaxRoot) {
return this.payloads.get(finalizedMaxRoot);
}
Expand Down Expand Up @@ -67,8 +77,15 @@ export class PayloadStore {
return undefined;
}

async getOrFetchFinalizedPayload(blockNumber: number): Promise<allForks.ExecutionPayload | undefined> {
if (blockNumber > this.finalizedRoots.max) {
protected async getOrFetchFinalizedPayload(blockNumber: number): Promise<allForks.ExecutionPayload | undefined> {
const maxBlockNumberForFinalized = this.finalizedRoots.max;
const minBlockNumberForFinalized = this.finalizedRoots.min;

if (maxBlockNumberForFinalized === undefined || minBlockNumberForFinalized === undefined) {
return;
}

if (blockNumber > maxBlockNumberForFinalized) {
throw new Error(
`Block number ${blockNumber} is higher than the latest finalized block number. We recommend to use block hash for unfinalized blocks.`
);
Expand All @@ -77,7 +94,7 @@ export class PayloadStore {
let blockELRoot = this.finalizedRoots.get(blockNumber);
// check if we have payload cached locally else fetch from api
if (!blockELRoot) {
const payloads = await getExecutionPayloadForBlockNumber(this.opts.api, this.finalizedRoots.min, blockNumber);
const payloads = await getExecutionPayloadForBlockNumber(this.opts.api, minBlockNumberForFinalized, blockNumber);
for (const payload of Object.values(payloads)) {
this.set(payload, true);
}
Expand Down Expand Up @@ -129,7 +146,7 @@ export class PayloadStore {

// If the block is finalized and we do not have the payload
// We need to fetch and set the payload
else if (finalized && !existingELRoot) {
else {
this.payloads.set(
bufferToHex(header.execution.blockHash),
(
Expand All @@ -155,9 +172,11 @@ export class PayloadStore {
// Re-org happened, we need to update the payload
if (existingELRoot && existingELRoot !== blockELRoot) {
this.payloads.delete(existingELRoot);
this.unfinalizedRoots.set(blockCLRoot, blockELRoot);
}

// This is unfinalized header we need to store it's root related to cl root
this.unfinalizedRoots.set(blockCLRoot, blockELRoot);

// We do not have the payload for this block, we need to fetch it
const payload = (
await getExecutionPayloads({
Expand All @@ -171,12 +190,14 @@ export class PayloadStore {
this.prune();
}

private prune(): void {
prune(): void {
if (this.finalizedRoots.size <= MAX_PAYLOAD_HISTORY) return;
// Store doe not have any finalized blocks means it's recently initialized
if (this.finalizedRoots.max === undefined || this.finalizedRoots.min === undefined) return;

for (
let blockNumber = this.finalizedRoots.max - MAX_PAYLOAD_HISTORY;
blockNumber > this.finalizedRoots.min;
blockNumber >= this.finalizedRoots.min;
blockNumber--
) {
const blockELRoot = this.finalizedRoots.get(blockNumber);
Expand Down
153 changes: 153 additions & 0 deletions packages/prover/src/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import Web3 from "web3";
import {Logger} from "@lodestar/logger";
import {createVerifiedExecutionProvider} from "./web3_provider.js";
import {LCTransport} from "./interfaces.js";

export const erc20Abi = [
{
inputs: [
{internalType: "string", name: "name_", type: "string"},
{internalType: "string", name: "symbol_", type: "string"},
{internalType: "uint8", name: "decimals_", type: "uint8"},
{internalType: "uint256", name: "initialBalance_", type: "uint256"},
{internalType: "address payable", name: "feeReceiver_", type: "address"},
],
stateMutability: "payable",
type: "constructor",
},
{
anonymous: false,
inputs: [
{indexed: true, internalType: "address", name: "owner", type: "address"},
{indexed: true, internalType: "address", name: "spender", type: "address"},
{indexed: false, internalType: "uint256", name: "value", type: "uint256"},
],
name: "Approval",
type: "event",
},
{
anonymous: false,
inputs: [
{indexed: true, internalType: "address", name: "from", type: "address"},
{indexed: true, internalType: "address", name: "to", type: "address"},
{indexed: false, internalType: "uint256", name: "value", type: "uint256"},
],
name: "Transfer",
type: "event",
},
{
inputs: [
{internalType: "address", name: "owner", type: "address"},
{internalType: "address", name: "spender", type: "address"},
],
name: "allowance",
outputs: [{internalType: "uint256", name: "", type: "uint256"}],
stateMutability: "view",
type: "function",
},
{
inputs: [
{internalType: "address", name: "spender", type: "address"},
{internalType: "uint256", name: "amount", type: "uint256"},
],
name: "approve",
outputs: [{internalType: "bool", name: "", type: "bool"}],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [{internalType: "address", name: "account", type: "address"}],
name: "balanceOf",
outputs: [{internalType: "uint256", name: "", type: "uint256"}],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "decimals",
outputs: [{internalType: "uint8", name: "", type: "uint8"}],
stateMutability: "view",
type: "function",
},
{
inputs: [
{internalType: "address", name: "spender", type: "address"},
{internalType: "uint256", name: "subtractedValue", type: "uint256"},
],
name: "decreaseAllowance",
outputs: [{internalType: "bool", name: "", type: "bool"}],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{internalType: "address", name: "spender", type: "address"},
{internalType: "uint256", name: "addedValue", type: "uint256"},
],
name: "increaseAllowance",
outputs: [{internalType: "bool", name: "", type: "bool"}],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [],
name: "name",
outputs: [{internalType: "string", name: "", type: "string"}],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "symbol",
outputs: [{internalType: "string", name: "", type: "string"}],
stateMutability: "view",
type: "function",
},
{
inputs: [],
name: "totalSupply",
outputs: [{internalType: "uint256", name: "", type: "uint256"}],
stateMutability: "view",
type: "function",
},
{
inputs: [
{internalType: "address", name: "recipient", type: "address"},
{internalType: "uint256", name: "amount", type: "uint256"},
],
name: "transfer",
outputs: [{internalType: "bool", name: "", type: "bool"}],
stateMutability: "nonpayable",
type: "function",
},
{
inputs: [
{internalType: "address", name: "sender", type: "address"},
{internalType: "address", name: "recipient", type: "address"},
{internalType: "uint256", name: "amount", type: "uint256"},
],
name: "transferFrom",
outputs: [{internalType: "bool", name: "", type: "bool"}],
stateMutability: "nonpayable",
type: "function",
},
] as const;

const {provider} = createVerifiedExecutionProvider(
new Web3.providers.HttpProvider("https://lodestar-mainnetrpc.chainsafe.io"),
{
transport: LCTransport.Rest,
urls: ["https://lodestar-mainnet.chainsafe.io"],
network: "mainnet",
logger: console as unknown as Logger,
}
);

const address = "0x8EB8a3b98659Cce290402893d0123abb75E3ab28";
const contractAddress = "0x6b175474e89094c44da98b954eedeac495271d0f";
const web3 = new Web3(provider);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const contract = new web3.eth.Contract(erc20Abi as any, contractAddress);

console.log("account balance : ", await web3.eth.getBalance(address));
console.log("contract balance : ", await contract.methods.balanceOf(address).call());
2 changes: 2 additions & 0 deletions packages/prover/src/utils/consensus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export async function getExecutionPayloads({
let slot = endSlot;
let block = await fetchNearestBlock(api, slot, "down");
payloads[block.message.slot] = block.message.body.executionPayload;
slot = block.message.slot - 1;

while (slot >= startSlot) {
const previousBlock = await fetchNearestBlock(api, block.message.slot - 1, "down");
Expand Down Expand Up @@ -84,6 +85,7 @@ export async function getExecutionPayloadForBlockNumber(
while (payloads[block.message.slot].blockNumber !== blockNumber) {
const previousBlock = await fetchNearestBlock(api, block.message.slot - 1, "down");
block = previousBlock;
payloads[block.message.slot] = block.message.body.executionPayload;
}

return payloads;
Expand Down
2 changes: 1 addition & 1 deletion packages/prover/src/utils/validation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export async function isValidStorageKeys({

const isStorageValid =
(!expectedStorageRLP && sp.value === "0x0") ||
(!!expectedStorageRLP && expectedStorageRLP.equals(RLP.encode(sp.value)));
(!!expectedStorageRLP && expectedStorageRLP.equals(Buffer.from(RLP.encode(sp.value))));
if (!isStorageValid) return false;
} catch (err) {
logger.error("Error verifying storage keys", undefined, err as Error);
Expand Down
40 changes: 40 additions & 0 deletions packages/prover/test/unit/proof_provider/orderd_map.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {expect} from "chai";
import {OrderedMap} from "../../../src/proof_provider/ordered_map.js";

describe("proof_provider/ordered_map", () => {
it("should initialize the min with undefined", () => {
const omap = new OrderedMap<string>();

expect(omap.min).to.undefined;
});

it("should initialize the max with undefined", () => {
const omap = new OrderedMap<string>();

expect(omap.max).to.undefined;
});

it("should set the min and max to the first value ", () => {
const omap = new OrderedMap<string>();
omap.set(11, "value");

expect(omap.min).eql(11);
expect(omap.max).eql(11);
});

it("should set the max value", () => {
const omap = new OrderedMap<string>();
omap.set(10, "value");
omap.set(11, "value");

expect(omap.max).eql(11);
});

it("should set the min value", () => {
const omap = new OrderedMap<string>();
omap.set(10, "value");
omap.set(11, "value");

expect(omap.min).eql(10);
});
});
Loading

0 comments on commit cf67f92

Please sign in to comment.