Add verified/unverified badge to Program Account view (#22825)

* Add verified/unverified badge to Program Account view

* Generalize to any number of build verification providers
This commit is contained in:
man0s 2022-02-06 11:11:12 +01:00 committed by GitHub
parent d2c89213ff
commit 86c3990c25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 169 additions and 0 deletions

View File

@ -15,6 +15,9 @@ import { useCluster } from "providers/cluster";
import { ErrorCard } from "components/common/ErrorCard";
import { UnknownAccountCard } from "components/account/UnknownAccountCard";
import { Downloadable } from "components/common/Downloadable";
import { CheckingBadge, VerifiedBadge } from "components/common/VerifiedBadge";
import { InfoTooltip } from "components/common/InfoTooltip";
import { useVerifiableBuilds } from "utils/program-verification";
export function UpgradeableLoaderAccountSection({
account,
@ -72,6 +75,7 @@ export function UpgradeableProgramSection({
const refresh = useFetchAccountInfo();
const { cluster } = useCluster();
const label = addressLabel(account.pubkey.toBase58(), cluster);
const { loading, verifiableBuilds } = useVerifiableBuilds(account.pubkey);
return (
<div className="card">
<div className="card-header">
@ -122,6 +126,22 @@ export function UpgradeableProgramSection({
{programData.authority !== null ? "Yes" : "No"}
</td>
</tr>
<tr>
<td>
<LastVerifiedBuildLabel />
</td>
<td className="text-lg-end">
{loading ? (
<CheckingBadge />
) : (
<>
{verifiableBuilds.map((b) => (
<VerifiedBadge verifiableBuild={b} />
))}
</>
)}
</td>
</tr>
<tr>
<td>Last Deployed Slot</td>
<td className="text-lg-end">
@ -141,6 +161,14 @@ export function UpgradeableProgramSection({
);
}
function LastVerifiedBuildLabel() {
return (
<InfoTooltip text="Indicates whether the program currently deployed on-chain is verified to match the associated published source code, when it is available.">
Verifiable Build Status
</InfoTooltip>
);
}
export function UpgradeableProgramDataSection({
account,
programData,

View File

@ -0,0 +1,38 @@
import { VerifiableBuild } from "utils/program-verification";
export function VerifiedBadge({
verifiableBuild,
}: {
verifiableBuild: VerifiableBuild;
}) {
if (verifiableBuild && verifiableBuild.verified_slot) {
return (
<h3 className="mb-0">
<a
className="c-pointer badge bg-success-soft rank"
href={verifiableBuild.url}
target="_blank"
rel="noreferrer"
>
{verifiableBuild.label}: Verified
</a>
</h3>
);
} else {
return (
<h3 className="mb-0">
<span className="badge bg-warning-soft rank">
{verifiableBuild.label}: Unverified
</span>
</h3>
);
}
}
export function CheckingBadge() {
return (
<h3 className="mb-0">
<span className="badge bg-dark rank">Checking</span>
</h3>
);
}

View File

@ -0,0 +1,103 @@
import { PublicKey } from "@solana/web3.js";
import { useEffect, useState } from "react";
export type VerifiableBuild =
| {
label: string;
id: number;
verified_slot: number;
url: string;
}
| {
label: string;
verified_slot: null;
};
export function useVerifiableBuilds(programAddress: PublicKey) {
const { loading: loadingAnchor, verifiableBuild: verifiedBuildAnchor } =
useAnchorVerifiableBuild(programAddress);
return {
loading: loadingAnchor,
verifiableBuilds: [verifiedBuildAnchor],
};
}
// ANCHOR
const defaultAnchorBuild = {
label: "Anchor",
verified_slot: null,
};
export function useAnchorVerifiableBuild(programAddress: PublicKey) {
const [loading, setLoading] = useState(true);
const [verifiableBuild, setVerifiableBuild] =
useState<VerifiableBuild>(defaultAnchorBuild);
useEffect(() => {
setLoading(true);
getAnchorVerifiableBuild(programAddress)
.then(setVerifiableBuild)
.catch((error) => {
console.log(error);
setVerifiableBuild(defaultAnchorBuild);
})
.finally(() => setLoading(false));
}, [programAddress, setVerifiableBuild, setLoading]);
return {
loading,
verifiableBuild,
};
}
export interface AnchorBuild {
aborted: boolean;
address: string;
created_at: string;
updated_at: string;
descriptor: string[];
docker: string;
id: number;
name: string;
sha256: string;
upgrade_authority: string;
verified: string;
verified_slot: number;
state: string;
}
/**
* Returns a verified build from the anchor registry. null if no such
* verified build exists, e.g., if the program has been upgraded since the
* last verified build.
*/
export async function getAnchorVerifiableBuild(
programId: PublicKey,
limit: number = 5
): Promise<VerifiableBuild> {
const url = `https://anchor.projectserum.com/api/v0/program/${programId.toString()}/latest?limit=${limit}`;
const latestBuildsResp = await fetch(url);
// Filter out all non successful builds.
const latestBuilds = (await latestBuildsResp.json()).filter(
(b: AnchorBuild) =>
!b.aborted && b.state === "Built" && b.verified === "Verified"
) as AnchorBuild[];
if (latestBuilds.length === 0) {
return defaultAnchorBuild;
}
// Get the latest build.
const { verified_slot, id } = latestBuilds[0];
return {
...defaultAnchorBuild,
verified_slot,
id,
url: `https://anchor.projectserum.com/build/${id}`,
};
}
// END ANCHOR