Skip to content
Open
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 .env.example.compose
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ PGURI=postgres://postgres:5432/archive
PG_CONN=postgres://${PGUSER}:${PGPASSWORD}@postgres:5432/${PGDATABASE}
PG_DUMP="archive.sql"
PGDATA=/var/lib/postgresql/data
PGPORT=5432 # Local port for postgres - it will always be 5432 inside the container

# Fields for Jaeger (Required)
JAEGER=jaegertracing/all-in-one:latest
Expand Down
3 changes: 2 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,15 @@ services:
image: ${POSTGRES}
container_name: postgres
ports:
- '5432:5432'
- '${PGPORT}:5432'
env_file:
- ./.env
environment:
POSTGRES_USER: ${PGUSER}
POSTGRES_PASSWORD: ${PGPASSWORD}
POSTGRES_DB: ${PGDATABASE}
PGDATA: ${PGDATA}
PGPORT: 5432
volumes:
- './data/${PG_DUMP}:/data/${PG_DUMP}'
- './scripts/init_docker_compose.sh:/docker-entrypoint-initdb.d/init.sh'
Expand Down
48 changes: 48 additions & 0 deletions src/db/sql/events-actions/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -440,3 +440,51 @@ export const USED_TABLES = [
'zkapp_accounts',
'zkapp_action_states',
] as const;

export function getZkappsWithPendingEventsQuery(db_client: postgres.Sql) {
return db_client`
WITH RECURSIVE pending_chain(id, parent_id, chain_status) AS (
-- start at the tip
SELECT id, parent_id, chain_status
FROM blocks
WHERE height = (SELECT MAX(height) FROM blocks)

UNION ALL

-- walk back until we re-join the canonical branch
SELECT b.id, b.parent_id, b.chain_status
FROM blocks b
JOIN pending_chain pc
ON b.id = pc.parent_id
WHERE pc.id <> pc.parent_id
AND pc.chain_status <> 'canonical'
)
SELECT DISTINCT
pk.value AS public_key
FROM
pending_chain pc

-- only commands in this block that didn’t fail
JOIN blocks_zkapp_commands bzc
ON bzc.block_id = pc.id
AND bzc.status <> 'failed'

-- get the command, then unwind its array of account-update IDs
JOIN zkapp_commands zkc
ON zkc.id = bzc.zkapp_command_id
JOIN LATERAL UNNEST(zkc.zkapp_account_updates_ids) AS upd(id) ON TRUE

-- pick up the body, but only if it actually emitted events
JOIN zkapp_account_update au
ON au.id = upd.id
JOIN zkapp_account_update_body aub
ON aub.id = au.body_id
AND aub.events_id IS NOT NULL

-- map back to the account’s public key
JOIN account_identifiers ai
ON ai.id = aub.account_identifier_id
JOIN public_keys pk
ON pk.id = ai.public_key_id;
`;
}
4 changes: 4 additions & 0 deletions tests/live-api/actions.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { describe } from 'node:test';
describe('Actions', () => {
// TODO: Find mainnet ZkApps with actions
});
106 changes: 106 additions & 0 deletions tests/live-api/events.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { gql } from 'graphql-tag';
import { GraphQLClient } from 'graphql-request';
import postgres from 'postgres';
import { getZkappsWithPendingEventsQuery } from '../../src/db/sql/events-actions/queries.js';
import { after, describe, it } from 'node:test';
import { EventOutput } from 'src/resolvers-types.js';
import assert from 'node:assert';

// Fixtures
import J1fu_65_66 from './fixtures/B62qpHtWX41NstxzzUe8xooKogqomDwgJ4CN8J3V2274v5B9dnfJ1fu_65_66.json' with { type: "json" };
import J1fu_84_83 from './fixtures/B62qpHtWX41NstxzzUe8xooKogqomDwgJ4CN8J3V2274v5B9dnfJ1fu_84_83.json' with { type: "json" };

const db_client = postgres(process.env.PG_CONN);
/**
* This gets all the public keys of accounts with events.
*
* TODO: This returns ZkApps and validators for some reason. Ideally it would only return ZkApps.
*/
const zkappsWithPendingEvents = (
await getZkappsWithPendingEventsQuery(db_client).execute()
).map((x) => x.public_key);

const endpoint =
process.env.STAGING_GRAPHQL_ENDPOINT ||
'http://archive-node-api.gcp.o1test.net/';
const client = new GraphQLClient(endpoint);

const getEventsQuery = gql`
query getEvents($input: EventFilterOptionsInput!) {
events(input: $input) {
blockInfo {
stateHash
height
parentHash
}
eventData {
data
transactionInfo {
status
hash
memo
}
}
}
}
`;

after(async () => {
await db_client.end();
});

describe('Events', () => {
// Skipping until we configure a DB connection for CI
describe.skip('Pending Chain', () => {
// Only some, because some of the addresses are not ZkApps, which should be fixed long term.
it('Some of the zkapps with pending actions should be returned', async () => {
const totalEvents: any[] = [];
for (const publicKey of zkappsWithPendingEvents) {
const input = {
address: publicKey,
status: 'PENDING',
};
const data: [EventOutput] = await client.request(getEventsQuery, {
input,
});
totalEvents.concat(data);
}
console.log(totalEvents);
assert(
totalEvents.length > 0,
'No events found for zkapps with pending actions'
);
});
});
describe('Canonical Chain', () => {
it('Block Filter', async () => {
const totalEvents: any[] = [];
const input = {
address: 'B62qpHtWX41NstxzzUe8xooKogqomDwgJ4CN8J3V2274v5B9dnfJ1fu',
from: 433465,
to: 433466,
};
const data: [EventOutput] = await client.request(getEventsQuery, {
input,
});
totalEvents.concat(data);
assert.deepStrictEqual(data, J1fu_65_66);
});
});
describe('Canonical Chain', () => {
it('Block Filter with several blocks', async () => {
const totalEvents: any[] = [];
const input = {
address: 'B62qpHtWX41NstxzzUe8xooKogqomDwgJ4CN8J3V2274v5B9dnfJ1fu',
to: 439983,
from: 429984,
};
const data: [EventOutput] = await client.request(getEventsQuery, {
input,
});
totalEvents.concat(data);
console.log(totalEvents);
assert.deepStrictEqual(data, J1fu_84_83);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"events": [
{
"blockInfo": {
"stateHash": "3NLrHxJXQbR3AHRjUMfLz1iy96xt1CDouJLFarkQfUKvodybLhJ5",
"height": 433465,
"parentHash": "3NLQFTnTGmHNnbiEWzeyuw1aaCCBQmB1knxQVPZ83U3gL5nckZmP"
},
"eventData": [
{
"data": [
"54",
"21418505143439845619563268510404671261197295135579508033453569377868703098897",
"1",
"5209626664266657802284133885362614683421727910483159025584204599078514592235",
"1"
],
"transactionInfo": {
"status": "applied",
"hash": "5JtYELUSVwo8HFC3rbNSdFyTHWorAUPSfJ1VgnJ8GNZACLh2yZ4S",
"memo": "E4ZANcKkkEtax1nEzsSw3o23y3vLHjF2ZB6eMqZV4ik52mSq1yHYn"
}
}
]
}
]
}
Loading
Loading