Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: getting latest tx-effects #348

Merged
merged 3 commits into from
Mar 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions k8s/local/ethereum-listener/skaffold.sp_testnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ deploy:
apply: ["--force"]
manifests:
rawYaml:
- ./sp_testnet/postgres-config.yaml
- ./sp_testnet/deployment.yaml
21 changes: 19 additions & 2 deletions k8s/local/ethereum-listener/sp_testnet/deployment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,37 @@ spec:
labels:
app: ethereum-listener-sp-testnet-label
spec:
initContainers:
- name: run-migrations
image: ethereum-listener:latest
command: ["yarn", run, "migrate"]
envFrom:
- configMapRef:
name: postgres-config-global
- configMapRef:
name: postgres-config-ethereum-listener-sp-testnet
env:
- name: TOTAL_DB_RESET
value: "false"
containers:
- image: ethereum-listener:latest
resources:
limits:
memory: 150Mi
cpu: 500m
name: ethereum-listener
envFrom:
- configMapRef:
name: postgres-config-global
- configMapRef:
name: postgres-config-ethereum-listener-sp-testnet
env:
- name: INSTANCE_NAME
value: "sp_testnet_ethereum-listener"
- name: L2_NETWORK_ID
value: "SP_TESTNET"
- name: BLOCK_POLL_INTERVAL_MS
value: "5000"
- name: LISTENER_DISABLED
value: "false"
- name: GENESIS_CATCHUP
value: "true"
- name: LISTEN_FOR_BLOCKS
Expand Down
7 changes: 7 additions & 0 deletions k8s/local/ethereum-listener/sp_testnet/postgres-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: postgres-config-ethereum-listener-sp-testnet
namespace: chicmoz
data:
POSTGRES_DB_NAME: "ethereum_listener_sp_testnet"
2 changes: 2 additions & 0 deletions k8s/local/postgres/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ primary:
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'aztec_listener_remote_devnet')\gexec
select 'CREATE DATABASE ethereum_listener_sandbox'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'ethereum_listener_sandbox')\gexec
select 'CREATE DATABASE ethereum_listener_sp_testnet'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'ethereum_listener_sp_testnet')\gexec
SELECT 'CREATE DATABASE explorer_api_sandbox'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'explorer_api_sandbox')\gexec
SELECT 'CREATE DATABASE explorer_api_sp_testnet'
Expand Down
3 changes: 1 addition & 2 deletions k8s/local/skaffold.sp_testnet.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ metadata:
name: sp_testnet
requires:
- path: ./common/skaffold.infra.yaml
- path: ./common/skaffold.aztec_sanbox_nodes.yaml
- path: ./aztec-listener/skaffold.sp_testnet.yaml
- path: ./explorer-ui/skaffold.sp_testnet.yaml
- path: ./ethereum-listener/skaffold.sp_testnet.yaml
- path: ./explorer-api/skaffold.sp_testnet.yaml
- path: ./auth/skaffold.yaml
- path: ./websocket-event-publisher/skaffold.sp_testnet.yaml
- path: ./aztec-listener/skaffold.sp_testnet.yaml
3 changes: 3 additions & 0 deletions packages/types/src/aztec/l2Block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ export enum ChicmozL2BlockFinalizationStatus {
L1_MINED_PROVEN = 5,
}

export const FIRST_FINALIZATION_STATUS =
ChicmozL2BlockFinalizationStatus.L2_NODE_SEEN_PROPOSED;

export const LAST_FINALIZATION_STATUS =
ChicmozL2BlockFinalizationStatus.L1_MINED_PROVEN;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
HexString,
chicmozL2TxEffectDeluxeSchema,
} from "@chicmoz-pkg/types";
import { SQL, and, asc, eq, getTableColumns } from "drizzle-orm";
import assert from "assert";
import { SQL, and, asc, desc, eq, getTableColumns, gte, lt } from "drizzle-orm";
import { z } from "zod";
import { DB_MAX_TX_EFFECTS } from "../../../../environment.js";
import {
Expand All @@ -18,23 +19,24 @@ import {
} from "../../../database/schema/l2block/index.js";

enum GetTypes {
BlockHeight,
BlockHeightRange,
BlockHeightAndIndex,
}

type GetTxEffectByBlockHeightAndIndex = {
blockHeight: bigint;
txEffectIndex: number
txEffectIndex: number;
getType: GetTypes.BlockHeightAndIndex;
};

type GetTxEffectsByBlockHeight = {
blockHeight: bigint;
getType: GetTypes.BlockHeight;
type GetTxEffectsByBlockHeightRange = {
from?: bigint;
to?: bigint;
getType: GetTypes.BlockHeightRange;
};

export const getTxEffectNestedByHash = async (
txEffectHash: string
txEffectHash: string,
): Promise<Pick<ChicmozL2TxEffect, "publicDataWrites">> => {
const publicDataWrites = await db()
.select({
Expand All @@ -52,27 +54,52 @@ export const getTxEffectNestedByHash = async (

export const getTxEffectByBlockHeightAndIndex = async (
blockHeight: bigint,
txEffectIndex: number
txEffectIndex: number,
): Promise<ChicmozL2TxEffectDeluxe | null> => {
const res = await _getTxEffects({
blockHeight,
txEffectIndex,
getType: GetTypes.BlockHeightAndIndex,
});

if (res.length === 0) return null;
if (res.length === 0) {return null;}

return res[0];
};

export const getTxEffectsByBlockHeight = async (
height: bigint
height: bigint,
): Promise<ChicmozL2TxEffectDeluxe[]> => {
return _getTxEffects({ blockHeight: height, getType: GetTypes.BlockHeight });
return _getTxEffects({
from: height,
to: height,
getType: GetTypes.BlockHeightRange,
});
};

export const getLatestTxEffects = async (): Promise<
ChicmozL2TxEffectDeluxe[]
> => {
return _getTxEffects({
getType: GetTypes.BlockHeightRange,
});
};

const generateWhereQuery = (from?: bigint, to?: bigint) => {
if (from && !to) {
return gte(l2Block.height, from);
} else if (!from && to) {
return lt(l2Block.height, to);
}
assert(
from && to,
"FATAL: cannot have both from and to undefined when generating where query",
);
return and(gte(l2Block.height, from), lt(l2Block.height, to));
};

const _getTxEffects = async (
args: GetTxEffectByBlockHeightAndIndex | GetTxEffectsByBlockHeight
args: GetTxEffectByBlockHeightAndIndex | GetTxEffectsByBlockHeightRange,
): Promise<ChicmozL2TxEffectDeluxe[]> => {
const joinQuery = db()
.select({
Expand All @@ -89,19 +116,25 @@ const _getTxEffects = async (
let whereQuery;

switch (args.getType) {
case GetTypes.BlockHeight:
whereQuery = joinQuery
.where(eq(l2Block.height, args.blockHeight))
.orderBy(asc(txEffect.index))
.limit(DB_MAX_TX_EFFECTS);
case GetTypes.BlockHeightRange:
if (args.from ?? args.to) {
whereQuery = joinQuery
.where(generateWhereQuery(args.from, args.to))
.orderBy(desc(txEffect.index), desc(l2Block.height))
.limit(DB_MAX_TX_EFFECTS);
} else {
whereQuery = joinQuery
.orderBy(desc(txEffect.index), desc(l2Block.height))
.limit(DB_MAX_TX_EFFECTS);
}
break;
case GetTypes.BlockHeightAndIndex:
whereQuery = joinQuery
.where(
and(
eq(l2Block.height, args.blockHeight),
eq(txEffect.index, args.txEffectIndex)
)
eq(txEffect.index, args.txEffectIndex),
),
)
.limit(1);
break;
Expand All @@ -117,26 +150,26 @@ const _getTxEffects = async (
txBirthTimestamp: txEffect.txBirthTimestamp.valueOf(),
...nestedData,
};
})
}),
);

return z.array(chicmozL2TxEffectDeluxeSchema).parse(txEffects);
};

export const getTxEffectByTxHash = async (
txHash: HexString
txHash: HexString,
): Promise<ChicmozL2TxEffectDeluxe | null> => {
return getTxEffectDynamicWhere(eq(txEffect.txHash, txHash));
};

export const getTxEffectByHash = async (
hash: HexString
hash: HexString,
): Promise<ChicmozL2TxEffectDeluxe | null> => {
return getTxEffectDynamicWhere(eq(txEffect.txHash, hash));
};

export const getTxEffectDynamicWhere = async (
whereMatcher: SQL<unknown>
whereMatcher: SQL<unknown>,
): Promise<ChicmozL2TxEffectDeluxe | null> => {
const dbRes = await db()
.select({
Expand All @@ -153,7 +186,7 @@ export const getTxEffectDynamicWhere = async (
.limit(1)
.execute();

if (dbRes.length === 0) return null;
if (dbRes.length === 0) {return null;}

const nestedData = await getTxEffectNestedByHash(dbRes[0].txHash);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getDb as db } from "@chicmoz-pkg/postgres-helper";
import {
ChicmozL2BlockLight,
FIRST_FINALIZATION_STATUS,
HexString,
chicmozL2BlockLightSchema,
} from "@chicmoz-pkg/types";
Expand All @@ -27,6 +28,7 @@ import {
} from "../../../database/schema/l2block/index.js";
import { l2BlockFinalizationStatusTable } from "../../schema/l2block/finalization-status.js";
import { getBlocksWhereRange, getTableColumnsWithoutId } from "../utils.js";
import {logger} from "../../../../logger.js";

enum GetTypes {
BlockHeight,
Expand Down Expand Up @@ -61,27 +63,27 @@ export const getBlocks = async ({
};

export const getBlock = async (
heightOrHash: bigint | HexString
heightOrHash: bigint | HexString,
): Promise<ChicmozL2BlockLight | null> => {
const res = await _getBlocks(
typeof heightOrHash === "bigint"
? { height: heightOrHash, getType: GetTypes.BlockHeight }
: { hash: heightOrHash, getType: GetTypes.BlockHash }
: { hash: heightOrHash, getType: GetTypes.BlockHash },
);
if (res.length === 0) return null;
if (res.length === 0) {return null;}
return res[0];
};

type GetBlocksArgs = GetBlocksByHeight | GetBlocksByHash | GetBlocksByRange;

const _getBlocks = async (
args: GetBlocksArgs
args: GetBlocksArgs,
): Promise<ChicmozL2BlockLight[]> => {
const whereRange =
args.getType === GetTypes.Range ? getBlocksWhereRange(args) : undefined;

if (args.getType === GetTypes.BlockHeight)
if (args.height < -1) throw new Error("Invalid height");
{if (args.height < -1) {throw new Error("Invalid height");}}

const joinQuery = db()
.select({
Expand Down Expand Up @@ -122,12 +124,12 @@ const _getBlocks = async (
l1L2BlockProposedTable,
and(
eq(l2Block.height, l1L2BlockProposedTable.l2BlockNumber),
eq(archive.root, l1L2BlockProposedTable.archive)
)
eq(archive.root, l1L2BlockProposedTable.archive),
),
)
.leftJoin(
l1L2ProofVerifiedTable,
eq(l2Block.height, l1L2ProofVerifiedTable.l2BlockNumber)
eq(l2Block.height, l1L2ProofVerifiedTable.l2BlockNumber),
);

let whereQuery;
Expand Down Expand Up @@ -168,14 +170,20 @@ const _getBlocks = async (
.where(eq(l2BlockFinalizationStatusTable.l2BlockHash, result.hash))
.orderBy(
desc(l2BlockFinalizationStatusTable.status),
desc(l2BlockFinalizationStatusTable.l2BlockNumber)
desc(l2BlockFinalizationStatusTable.l2BlockNumber),
)
.limit(1);

let finalizationStatusValue = finalizationStatus[0]?.status;
if (finalizationStatusValue === undefined) {
finalizationStatusValue = FIRST_FINALIZATION_STATUS;
logger.warn(`Finalization status not found for block ${result.hash}`);
}

const blockData = {
hash: result.hash,
height: result.height,
finalizationStatus: finalizationStatus[0].status,
finalizationStatus: finalizationStatusValue,
archive: result.archive,
proposedOnL1: result.l1L2BlockProposed?.l1BlockTimestamp
? result.l1L2BlockProposed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,23 @@ import {
txEffectResponseArray,
} from "./utils/index.js";

export const openapi_GET_L2_TX_EFFECTS = {
"/l2/txEffects": {
get: {
summary: "Get all transaction effects",
responses: txEffectResponseArray,
},
},
};

export const GET_L2_TX_EFFECTS = asyncHandler(async (_req, res) => {
// TODO: this should be extended to enable querying for block-height ranges
const txEffectsData = await dbWrapper.getLatest(["l2", "txEffects"], () =>
db.l2TxEffect.getLatestTxEffects()
);
res.status(200).send(txEffectsData);
});

export const openapi_GET_L2_TX_EFFECTS_BY_BLOCK_HEIGHT = {
"/l2/blocks/{blockHeight}/txEffects": {
get: {
Expand Down
Loading