explorer: Display correct list of block programs (#17489)

This commit is contained in:
Justin Starry 2021-05-26 08:21:07 -07:00 committed by GitHub
parent 9a5330b7eb
commit 179856c13a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 57 additions and 60 deletions

View File

@ -1,5 +1,5 @@
import React from "react"; import React from "react";
import { ConfirmedBlock, PublicKey } from "@solana/web3.js"; import { BlockResponse, PublicKey } from "@solana/web3.js";
import { Address } from "components/common/Address"; import { Address } from "components/common/Address";
type AccountStats = { type AccountStats = {
@ -9,18 +9,19 @@ type AccountStats = {
const PAGE_SIZE = 25; const PAGE_SIZE = 25;
export function BlockAccountsCard({ block }: { block: ConfirmedBlock }) { export function BlockAccountsCard({ block }: { block: BlockResponse }) {
const [numDisplayed, setNumDisplayed] = React.useState(10); const [numDisplayed, setNumDisplayed] = React.useState(10);
const totalTransactions = block.transactions.length; const totalTransactions = block.transactions.length;
const accountStats = React.useMemo(() => { const accountStats = React.useMemo(() => {
const statsMap = new Map<string, AccountStats>(); const statsMap = new Map<string, AccountStats>();
block.transactions.forEach((tx) => { block.transactions.forEach((tx) => {
const message = tx.transaction.message;
const txSet = new Map<string, boolean>(); const txSet = new Map<string, boolean>();
tx.transaction.instructions.forEach((ix) => { message.instructions.forEach((ix) => {
ix.keys.forEach((key) => { ix.accounts.forEach((index) => {
const address = key.pubkey.toBase58(); const address = message.accountKeys[index].toBase58();
txSet.set(address, key.isWritable); txSet.set(address, message.isAccountWritable(index));
}); });
}); });

View File

@ -1,12 +1,11 @@
import React from "react"; import React from "react";
import { ConfirmedBlock } from "@solana/web3.js"; import { BlockResponse } from "@solana/web3.js";
import { ErrorCard } from "components/common/ErrorCard"; import { ErrorCard } from "components/common/ErrorCard";
import { Signature } from "components/common/Signature"; import { Signature } from "components/common/Signature";
import bs58 from "bs58";
const PAGE_SIZE = 25; const PAGE_SIZE = 25;
export function BlockHistoryCard({ block }: { block: ConfirmedBlock }) { export function BlockHistoryCard({ block }: { block: BlockResponse }) {
const [numDisplayed, setNumDisplayed] = React.useState(PAGE_SIZE); const [numDisplayed, setNumDisplayed] = React.useState(PAGE_SIZE);
if (block.transactions.length === 0) { if (block.transactions.length === 0) {
@ -32,7 +31,7 @@ export function BlockHistoryCard({ block }: { block: ConfirmedBlock }) {
let statusText; let statusText;
let statusClass; let statusClass;
let signature: React.ReactNode; let signature: React.ReactNode;
if (tx.meta?.err || !tx.transaction.signature) { if (tx.meta?.err || tx.transaction.signatures.length === 0) {
statusClass = "warning"; statusClass = "warning";
statusText = "Failed"; statusText = "Failed";
} else { } else {
@ -40,12 +39,9 @@ export function BlockHistoryCard({ block }: { block: ConfirmedBlock }) {
statusText = "Success"; statusText = "Success";
} }
if (tx.transaction.signature) { if (tx.transaction.signatures.length > 0) {
signature = ( signature = (
<Signature <Signature signature={tx.transaction.signatures[0]} link />
signature={bs58.encode(tx.transaction.signature)}
link
/>
); );
} }

View File

@ -7,7 +7,7 @@ import { Slot } from "components/common/Slot";
import { ClusterStatus, useCluster } from "providers/cluster"; import { ClusterStatus, useCluster } from "providers/cluster";
import { BlockHistoryCard } from "./BlockHistoryCard"; import { BlockHistoryCard } from "./BlockHistoryCard";
import { BlockRewardsCard } from "./BlockRewardsCard"; import { BlockRewardsCard } from "./BlockRewardsCard";
import { ConfirmedBlock } from "@solana/web3.js"; import { BlockResponse } from "@solana/web3.js";
import { NavLink } from "react-router-dom"; import { NavLink } from "react-router-dom";
import { clusterPath } from "utils/url"; import { clusterPath } from "utils/url";
import { BlockProgramsCard } from "./BlockProgramsCard"; import { BlockProgramsCard } from "./BlockProgramsCard";
@ -134,7 +134,7 @@ function MoreSection({
tab, tab,
}: { }: {
slot: number; slot: number;
block: ConfirmedBlock; block: BlockResponse;
tab?: string; tab?: string;
}) { }) {
return ( return (

View File

@ -1,9 +1,9 @@
import React from "react"; import React from "react";
import { ConfirmedBlock, PublicKey } from "@solana/web3.js"; import { BlockResponse, PublicKey } from "@solana/web3.js";
import { Address } from "components/common/Address"; import { Address } from "components/common/Address";
import { TableCardBody } from "components/common/TableCardBody"; import { TableCardBody } from "components/common/TableCardBody";
export function BlockProgramsCard({ block }: { block: ConfirmedBlock }) { export function BlockProgramsCard({ block }: { block: BlockResponse }) {
const totalTransactions = block.transactions.length; const totalTransactions = block.transactions.length;
const txSuccesses = new Map<string, number>(); const txSuccesses = new Map<string, number>();
const txFrequency = new Map<string, number>(); const txFrequency = new Map<string, number>();
@ -11,25 +11,24 @@ export function BlockProgramsCard({ block }: { block: ConfirmedBlock }) {
let totalInstructions = 0; let totalInstructions = 0;
block.transactions.forEach((tx) => { block.transactions.forEach((tx) => {
totalInstructions += tx.transaction.instructions.length; const message = tx.transaction.message;
totalInstructions += message.instructions.length;
const programUsed = new Set<string>(); const programUsed = new Set<string>();
const trackProgramId = (programId: PublicKey) => { const trackProgram = (index: number) => {
if (index >= message.accountKeys.length) return;
const programId = message.accountKeys[index];
const programAddress = programId.toBase58(); const programAddress = programId.toBase58();
programUsed.add(programAddress); programUsed.add(programAddress);
const frequency = ixFrequency.get(programAddress); const frequency = ixFrequency.get(programAddress);
ixFrequency.set(programAddress, frequency ? frequency + 1 : 1); ixFrequency.set(programAddress, frequency ? frequency + 1 : 1);
}; };
tx.transaction.instructions.forEach((ix, index) => { message.instructions.forEach((ix) => trackProgram(ix.programIdIndex));
trackProgramId(ix.programId); tx.meta?.innerInstructions?.forEach((inner) => {
tx.meta?.innerInstructions?.forEach((inner) => { totalInstructions += inner.instructions.length;
if (inner.index !== index) return; inner.instructions.forEach((innerIx) =>
totalInstructions += inner.instructions.length; trackProgram(innerIx.programIdIndex)
inner.instructions.forEach((innerIx) => { );
if (innerIx.programIdIndex >= ix.keys.length) return;
trackProgramId(ix.keys[innerIx.programIdIndex].pubkey);
});
});
}); });
const successful = tx.meta?.err === null; const successful = tx.meta?.err === null;

View File

@ -1,11 +1,11 @@
import React from "react"; import React from "react";
import { lamportsToSolString } from "utils"; import { lamportsToSolString } from "utils";
import { ConfirmedBlock, PublicKey } from "@solana/web3.js"; import { BlockResponse, PublicKey } from "@solana/web3.js";
import { Address } from "components/common/Address"; import { Address } from "components/common/Address";
const PAGE_SIZE = 10; const PAGE_SIZE = 10;
export function BlockRewardsCard({ block }: { block: ConfirmedBlock }) { export function BlockRewardsCard({ block }: { block: BlockResponse }) {
const [rewardsDisplayed, setRewardsDisplayed] = React.useState(PAGE_SIZE); const [rewardsDisplayed, setRewardsDisplayed] = React.useState(PAGE_SIZE);
if (!block.rewards || block.rewards.length < 1) { if (!block.rewards || block.rewards.length < 1) {

View File

@ -38,10 +38,12 @@ export function InstructionCard({
const [showRaw, setShowRaw] = React.useState(defaultRaw || false); const [showRaw, setShowRaw] = React.useState(defaultRaw || false);
const signature = useContext(SignatureContext); const signature = useContext(SignatureContext);
const details = useTransactionDetails(signature); const details = useTransactionDetails(signature);
let raw: TransactionInstruction | undefined = undefined; let raw: TransactionInstruction | undefined = undefined;
if (details && childIndex === undefined) { if (details && childIndex === undefined) {
raw = details?.data?.raw?.transaction.instructions[index]; raw = details?.data?.raw?.instructions[index];
} }
const fetchRaw = useFetchRawTransaction(); const fetchRaw = useFetchRawTransaction();
const fetchRawTrigger = () => fetchRaw(signature); const fetchRawTrigger = () => fetchRaw(signature);

View File

@ -7,7 +7,6 @@ import {
PartiallyDecodedInstruction, PartiallyDecodedInstruction,
PublicKey, PublicKey,
SignatureResult, SignatureResult,
Transaction,
TransactionSignature, TransactionSignature,
} from "@solana/web3.js"; } from "@solana/web3.js";
import { BpfLoaderDetailsCard } from "components/instruction/bpf-loader/BpfLoaderDetailsCard"; import { BpfLoaderDetailsCard } from "components/instruction/bpf-loader/BpfLoaderDetailsCard";
@ -59,8 +58,6 @@ export function InstructionsSection({ signature }: SignatureProps) {
if (!status?.data?.info || !details?.data?.transaction) return null; if (!status?.data?.info || !details?.data?.transaction) return null;
const raw = details.data.raw?.transaction;
const { transaction } = details.data.transaction; const { transaction } = details.data.transaction;
const { meta } = details.data.transaction; const { meta } = details.data.transaction;
@ -106,7 +103,6 @@ export function InstructionsSection({ signature }: SignatureProps) {
signature, signature,
tx: transaction, tx: transaction,
childIndex, childIndex,
raw,
}); });
innerCards.push(res); innerCards.push(res);
@ -120,7 +116,6 @@ export function InstructionsSection({ signature }: SignatureProps) {
signature, signature,
tx: transaction, tx: transaction,
innerCards, innerCards,
raw,
}); });
} }
); );
@ -147,7 +142,6 @@ function renderInstructionCard({
signature, signature,
innerCards, innerCards,
childIndex, childIndex,
raw,
}: { }: {
ix: ParsedInstruction | PartiallyDecodedInstruction; ix: ParsedInstruction | PartiallyDecodedInstruction;
tx: ParsedTransaction; tx: ParsedTransaction;
@ -156,7 +150,6 @@ function renderInstructionCard({
signature: TransactionSignature; signature: TransactionSignature;
innerCards?: JSX.Element[]; innerCards?: JSX.Element[];
childIndex?: number; childIndex?: number;
raw?: Transaction;
}) { }) {
const key = `${index}-${childIndex}`; const key = `${index}-${childIndex}`;

View File

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import * as Sentry from "@sentry/react"; import * as Sentry from "@sentry/react";
import * as Cache from "providers/cache"; import * as Cache from "providers/cache";
import { Connection, ConfirmedBlock } from "@solana/web3.js"; import { Connection, BlockResponse } from "@solana/web3.js";
import { useCluster, Cluster } from "./cluster"; import { useCluster, Cluster } from "./cluster";
export enum FetchStatus { export enum FetchStatus {
@ -16,7 +16,7 @@ export enum ActionType {
} }
type Block = { type Block = {
block?: ConfirmedBlock; block?: BlockResponse;
}; };
type State = Cache.State<Block>; type State = Cache.State<Block>;
@ -72,18 +72,18 @@ export async function fetchBlock(
try { try {
const connection = new Connection(url, "finalized"); const connection = new Connection(url, "finalized");
data = { block: await connection.getConfirmedBlock(Number(key)) }; const block = await connection.getBlock(Number(key));
status = FetchStatus.Fetched; if (block === null) {
} catch (err) { data = {};
const error = err as Error;
if (error.message.includes("not found")) {
data = {} as Block;
status = FetchStatus.Fetched; status = FetchStatus.Fetched;
} else { } else {
status = FetchStatus.FetchFailed; data = { block };
if (cluster !== Cluster.Custom) { status = FetchStatus.Fetched;
Sentry.captureException(error, { tags: { url } }); }
} } catch (err) {
status = FetchStatus.FetchFailed;
if (cluster !== Cluster.Custom) {
Sentry.captureException(err, { tags: { url } });
} }
} }

View File

@ -3,7 +3,7 @@ import {
Connection, Connection,
TransactionSignature, TransactionSignature,
ParsedConfirmedTransaction, ParsedConfirmedTransaction,
ConfirmedTransaction, Transaction,
} from "@solana/web3.js"; } from "@solana/web3.js";
import { useCluster, Cluster } from "../cluster"; import { useCluster, Cluster } from "../cluster";
import * as Cache from "providers/cache"; import * as Cache from "providers/cache";
@ -12,7 +12,7 @@ import { reportError } from "utils/sentry";
export interface Details { export interface Details {
transaction?: ParsedConfirmedTransaction | null; transaction?: ParsedConfirmedTransaction | null;
raw?: ConfirmedTransaction | null; raw?: Transaction | null;
} }
type State = Cache.State<Details>; type State = Cache.State<Details>;
@ -128,17 +128,23 @@ async function fetchRawTransaction(
url: string url: string
) { ) {
let fetchStatus; let fetchStatus;
let transaction;
try { try {
transaction = await new Connection(url).getConfirmedTransaction(signature); const response = await new Connection(url).getTransaction(signature);
fetchStatus = FetchStatus.Fetched; fetchStatus = FetchStatus.Fetched;
let data: Details = { raw: null };
if (response !== null) {
const { message, signatures } = response.transaction;
data = {
raw: Transaction.populate(message, signatures),
};
}
dispatch({ dispatch({
type: ActionType.Update, type: ActionType.Update,
status: fetchStatus, status: fetchStatus,
key: signature, key: signature,
data: { data,
raw: transaction,
},
url, url,
}); });
} catch (error) { } catch (error) {