Skip to content
Draft
Show file tree
Hide file tree
Changes from 5 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
13 changes: 13 additions & 0 deletions src/features/deliveryStatus/fetchDeliveryStatus.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ export async function fetchDeliveryStatus(
};
return result;
} else {
// Tip/raw rows may lack full finalized fields (e.g. message body), so avoid
// deep debug that can generate false negatives.
if (message.isProvisional) {
const result: MessageDeliveryPendingResult = {
status: MessageStatus.Pending,
debugResult: {
status: MessageDebugStatus.NoErrorsFound,
description: 'Provisional tip-stage message; debug deferred until finalized.',
},
};
return result;
}

const debugResult = await debugMessage(multiProvider, registry, overrideChainMetadata, message);
const messageStatus =
debugResult.status === MessageDebugStatus.NoErrorsFound
Expand Down
12 changes: 11 additions & 1 deletion src/features/messages/MessageTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,17 @@ export function MessageSummaryRow({
{shortenAddress(formattedTxHash)}
</LinkCell>
<LinkCell id={msgId} base64={base64} aClasses={styles.valueTruncated}>
{getHumanReadableTimeString(origin.timestamp)}
<span className="flex items-center gap-1">
<span>{getHumanReadableTimeString(origin.timestamp)}</span>
{message.isProvisional && (
<span
className="rounded bg-blue-100 px-1 py-0.5 text-[10px] font-medium uppercase text-blue-600"
title="Tip-stage message, not finalized"
>
tip
</span>
)}
</span>
</LinkCell>
<LinkCell
id={msgId}
Expand Down
157 changes: 156 additions & 1 deletion src/features/messages/queries/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import { MessageStatusFilter } from '../../../types';
import { adjustToUtcTime } from '../../../utils/time';

import { isPotentiallyTransactionHash, searchValueToPostgresBytea } from './encoding';
import { messageDetailsFragment, messageStubFragment } from './fragments';
import {
messageDetailsFragment,
messageStubFragment,
rawMessageDispatchFragment,
} from './fragments';

/**
* ========================
Expand Down Expand Up @@ -63,6 +67,39 @@ export function buildMessageQuery(
return { query, variables };
}

export function buildRawMessageQuery(
idType: MessageIdentifierType,
idValue: string,
limit: number,
) {
let whereClause: string;
if (idType === MessageIdentifierType.Id) {
whereClause = 'msg_id: {_eq: $identifier}';
} else if (idType === MessageIdentifierType.Sender) {
whereClause = 'sender: {_eq: $identifier}';
} else if (idType === MessageIdentifierType.Recipient) {
whereClause = 'recipient: {_eq: $identifier}';
} else if (idType === MessageIdentifierType.OriginTxHash) {
whereClause = 'origin_tx_hash: {_eq: $identifier}';
} else {
return null;
}
const variables = { identifier: searchValueToPostgresBytea(idValue) };

const query = `
query ($identifier: bytea!) @cached(ttl: 5) {
raw_message_dispatch(
where: {${whereClause}},
order_by: [{time_updated: desc}, {id: desc}],
limit: ${limit}
) {
${rawMessageDispatchFragment}
}
}
`;
return { query, variables };
}

export function buildMessageSearchQuery(
searchInput: string,
originDomainIdFilter: number | null,
Expand Down Expand Up @@ -170,6 +207,97 @@ export function buildMessageSearchQuery(
return { query, variables };
}

export function buildRawMessageSearchQuery(
searchInput: string,
originDomainIdFilter: number | null,
destDomainIdFilter: number | null,
startTimeFilter: number | null,
endTimeFilter: number | null,
limit: number,
mainnetDomainIds?: number[],
warpRouteAddresses: string[] = [],
) {
const originChains = originDomainIdFilter ? [originDomainIdFilter] : undefined;
const destinationChains = destDomainIdFilter ? [destDomainIdFilter] : undefined;
const startTime = startTimeFilter ? adjustToUtcTime(startTimeFilter) : undefined;
const endTime = endTimeFilter ? adjustToUtcTime(endTimeFilter) : undefined;

const warpAddressesBytea = warpRouteAddresses
.map((addr) => searchValueToPostgresBytea(addr))
.filter((addr): addr is string => !!addr);

const variables: Record<string, unknown> = {
search: searchValueToPostgresBytea(searchInput),
originChains,
destinationChains,
startTime,
endTime,
};

if (warpAddressesBytea.length > 0) {
variables.warpAddresses = warpAddressesBytea;
}

const hasFilters = !!(
originDomainIdFilter ||
destDomainIdFilter ||
startTimeFilter ||
endTimeFilter ||
searchInput ||
warpAddressesBytea.length > 0
);
const whereClauses = buildRawSearchWhereClauses(searchInput);
const originDomainWhereClause = buildRawDomainIdWhereClause(
originDomainIdFilter,
hasFilters,
'origin',
mainnetDomainIds,
);
const destinationDomainWhereClause = buildRawDomainIdWhereClause(
destDomainIdFilter,
hasFilters,
'destination',
mainnetDomainIds,
);
const warpRouteWhereClause = buildWarpRouteWhereClause(warpAddressesBytea);

const queries = whereClauses.map(
(whereClause, i) =>
`q${i}: raw_message_dispatch(
where: {
_and: [
${originDomainWhereClause}
${destinationDomainWhereClause}
${startTimeFilter ? '{time_updated: {_gte: $startTime}},' : ''}
${endTimeFilter ? '{time_updated: {_lte: $endTime}},' : ''}
${warpRouteWhereClause}
${whereClause}
]
},
order_by: [{time_updated: desc}, {id: desc}],
limit: ${limit}
) {
${rawMessageDispatchFragment}
}`,
);

const variableDeclarations = [
'$search: bytea',
'$originChains: [Int!]',
'$destinationChains: [Int!]',
'$startTime: timestamp',
'$endTime: timestamp',
];
if (warpAddressesBytea.length > 0) {
variableDeclarations.push('$warpAddresses: [bytea!]');
}

const query = `query (${variableDeclarations.join(', ')}) @cached(ttl: 5) {
${queries.join('\n')}
}`;
return { query, variables };
}

// Note: Only 'delivered' filter is applied at DB level. 'pending' uses client-side
// filtering (see useMessageQuery.ts) because DB query for is_delivered=false is slow.
function buildStatusWhereClause(statusFilter: MessageStatusFilter): string {
Expand Down Expand Up @@ -205,6 +333,20 @@ function buildSearchWhereClauses(searchInput: string) {
return clauses;
}

function buildRawSearchWhereClauses(searchInput: string) {
if (!searchInput) return [''];

const clauses: string[] = [];
if (isAddress(searchInput)) {
clauses.push(`{sender: {_eq: $search}}`, `{recipient: {_eq: $search}}`);
}
if (isPotentiallyTransactionHash(searchInput)) {
clauses.push(`{origin_tx_hash: {_eq: $search}}`);
}
clauses.push(`{msg_id: {_eq: $search}}`);
return clauses;
}

function buildDomainIdWhereClause(
domainId: number | null,
hasFilters: boolean,
Expand All @@ -220,3 +362,16 @@ function buildDomainIdWhereClause(
// if domainId is not set but there are other filters, remove condition of filtering by mainnet chains
return '';
}

function buildRawDomainIdWhereClause(
domainId: number | null,
hasFilters: boolean,
fieldName: 'origin' | 'destination',
mainnetDomainIds: number[] = [],
) {
const dbField = `${fieldName}_domain`;

if (!hasFilters) return `{${dbField}: {_in: [${mainnetDomainIds}]}},`;
if (domainId) return `{${dbField}: {_in: $${fieldName}Chains}},`;
return '';
}
33 changes: 33 additions & 0 deletions src/features/messages/queries/fragments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,22 @@ ${messageStubFragment}
num_payments
`;

export const rawMessageDispatchFragment = `
id
msg_id
nonce
origin_domain
destination_domain
sender
recipient
origin_tx_hash
origin_block_hash
origin_block_height
origin_mailbox
time_created
time_updated
`;

/**
* ===================================
* FRAGMENT TYPES
Expand Down Expand Up @@ -121,5 +137,22 @@ export interface MessageEntry extends MessageStubEntry {
num_payments: number;
}

export interface RawMessageDispatchEntry {
id: number;
msg_id: string; // binary e.g. \\x123
nonce: number;
origin_domain: number;
destination_domain: number;
sender: string; // binary e.g. \\x123
recipient: string; // binary e.g. \\x123
origin_tx_hash: string; // binary e.g. \\x123
origin_block_hash: string; // binary e.g. \\x123
origin_block_height: number;
origin_mailbox: string; // binary e.g. \\x123
time_created: string | null; // e.g. "2022-08-28T17:30:15"
time_updated: string | null; // e.g. "2022-08-28T17:30:15"
}

export type MessagesStubQueryResult = Record<string, MessageStubEntry[]>;
export type MessagesQueryResult = Record<string, MessageEntry[]>;
export type RawMessagesQueryResult = Record<string, RawMessageDispatchEntry[]>;
Loading