Skip to content

Commit

Permalink
Add verify command to verified build tab (#396)
Browse files Browse the repository at this point in the history
- Deriving the PDA from the upgrade authority and the program id, then
getting the parameter from the PDA and composing it into a solana-verify
comand

Like this people can easily copy the command from the explorer and
verify for themselfs.

Related Issue: 
#393 

Looks like this: 
<img width="1168" alt="image"
src="https://github.com/user-attachments/assets/708a886a-dc9b-4ab1-ac69-1465210c7611">

And like this when there is no Verify PDA:
<img width="1154" alt="image"
src="https://github.com/user-attachments/assets/d4952dd4-f996-4764-a7d8-3492bd24f396">

---------

Co-authored-by: Noah Gundotra <[email protected]>
  • Loading branch information
Woody4618 and ngundotra authored Oct 30, 2024
1 parent d5568ef commit c10c8ff
Show file tree
Hide file tree
Showing 3 changed files with 146 additions and 11 deletions.
53 changes: 51 additions & 2 deletions app/components/account/VerifiedBuildCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import { ExternalLink } from 'react-feather';

import { OsecRegistryInfo, useVerifiedProgramRegistry } from '@/app/utils/verified-builds';

import { Copyable } from '../common/Copyable';
import { LoadingCard } from '../common/LoadingCard';

export function VerifiedBuildCard({ data, pubkey }: { data: UpgradeableLoaderAccountData; pubkey: PublicKey }) {
const { data: registryInfo, isLoading } = useVerifiedProgramRegistry({
options: { suspense: true },
programAuthority: data.programData?.authority ? new PublicKey(data.programData.authority) : null,
programId: pubkey,
});
if (!data.programData) {
Expand All @@ -31,6 +33,18 @@ export function VerifiedBuildCard({ data, pubkey }: { data: UpgradeableLoaderAcc
<h3 className="card-header-title mb-0 d-flex align-items-center">Verified Build</h3>
<small>Information provided by osec.io</small>
</div>
<div className="alert mt-2 mb-2">
Verified builds indicate that the onchain build was built from the source code that is publicly
available, but this does not imply a security audit. For more details, refer to the{' '}
<a
href="https://solana.com/developers/guides/advanced/verified-builds"
target="_blank"
rel="noopener noreferrer"
>
Verified Builds Docs <ExternalLink className="align-text-top ms-1" size={13} />
</a>
.
</div>
<TableCardBody>
{ROWS.filter(x => x.key in registryInfo).map((x, idx) => {
return (
Expand All @@ -50,6 +64,7 @@ enum DisplayType {
String,
URL,
Date,
LongString,
}

type TableRow = {
Expand Down Expand Up @@ -84,6 +99,11 @@ const ROWS: TableRow[] = [
key: 'last_verified_at',
type: DisplayType.Date,
},
{
display: 'Verify Command',
key: 'verify_command',
type: DisplayType.LongString,
},
{
display: 'Repository URL',
key: 'repo_url',
Expand All @@ -100,7 +120,32 @@ function RenderEntry({ value, type }: { value: OsecRegistryInfo[keyof OsecRegist
</td>
);
case DisplayType.String:
return <td className="text-lg-end font-monospace" style={{whiteSpace: 'pre'}}>{value && (value as string).length > 1 ? value : '-'}</td>;
return (
<td className="text-lg-end font-monospace" style={{ whiteSpace: 'pre' }}>
{value && (value as string).length > 1 ? value : '-'}
</td>
);
case DisplayType.LongString:
return (
<td
className="text-lg-end font-monospace"
style={{
overflowWrap: 'break-word',
position: 'relative',
whiteSpace: 'pre-wrap',
wordWrap: 'break-word',
}}
>
{value && (value as string).length > 1 ? (
<>
<Copyable text={value as string}> </Copyable>
<span>{value}</span>
</>
) : (
'-'
)}
</td>
);
case DisplayType.URL:
if (isValidLink(value as string)) {
return (
Expand All @@ -120,7 +165,11 @@ function RenderEntry({ value, type }: { value: OsecRegistryInfo[keyof OsecRegist
</td>
);
case DisplayType.Date:
return <td className="text-lg-end font-monospace">{value && (value as string).length > 1 ? new Date(value as string).toUTCString() : '-'}</td>;
return (
<td className="text-lg-end font-monospace">
{value && (value as string).length > 1 ? new Date(value as string).toUTCString() : '-'}
</td>
);
default:
break;
}
Expand Down
2 changes: 1 addition & 1 deletion app/components/common/VerifiedProgramBadge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function VerifiedProgramBadge({
programData: ProgramDataAccountInfo;
pubkey: PublicKey;
}) {
const { isLoading, data: registryInfo } = useVerifiedProgramRegistry({ programId: pubkey });
const { isLoading, data: registryInfo } = useVerifiedProgramRegistry({ programAuthority: programData.authority ? new PublicKey(programData.authority) : null, programId: pubkey });
const verifiedBuildTabPath = useClusterPath({ pathname: `/address/${pubkey.toBase58()}/verified-build` });

const hash = hashProgramData(programData);
Expand Down
102 changes: 94 additions & 8 deletions app/utils/verified-builds.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { sha256 } from '@noble/hashes/sha256';
import { PublicKey } from '@solana/web3.js';
import { Connection, PublicKey } from '@solana/web3.js';
import useSWRImmutable from 'swr/immutable';

import { useAnchorProgram } from '../providers/anchor';
import { useCluster } from '../providers/cluster';
import { ProgramDataAccountInfo } from '../validators/accounts/upgradeable-program';
import { Cluster } from './cluster';

const OSEC_REGISTRY_URL = 'https://verify.osec.io';
const VERIFY_PROGRAM_ID = 'verifycLy8mB96wd9wqq3WDXQwM4oU6r42Th37Db9fC';

export type OsecRegistryInfo = {
is_verified: boolean;
Expand All @@ -13,29 +17,111 @@ export type OsecRegistryInfo = {
executable_hash: string;
last_verified_at: string | null;
repo_url: string;
};

export type CheckedOsecRegistryInfo = {
explorer_hash: string;
verify_command: string;
};

export function useVerifiedProgramRegistry({
programId,
programAuthority,
options,
}: {
programId: PublicKey;
programAuthority: PublicKey | null;
options?: { suspense: boolean };
}) {
const { data, error, isLoading } = useSWRImmutable(
const { url: clusterUrl, cluster: cluster } = useCluster();
const connection = new Connection(clusterUrl);

const {
data: registryData,
error: registryError,
isLoading: isRegistryLoading,
} = useSWRImmutable(
`${programId.toBase58()}`,
async (programId: string) => {
return fetch(`${OSEC_REGISTRY_URL}/status/${programId}`).then(response => response.json());
const response = await fetch(`${OSEC_REGISTRY_URL}/status/${programId}`);
return response.json();
},
{ suspense: options?.suspense }
);

const { program: accountAnchorProgram } = useAnchorProgram(VERIFY_PROGRAM_ID, connection.rpcEndpoint);

// Fetch the PDA derived from the program upgrade authority
// TODO: Add getting verifier pubkey from the security.txt as second option once implemented
const {
data: pdaData,
error: pdaError,
isLoading: isPdaLoading,
} = useSWRImmutable(
programAuthority && accountAnchorProgram ? `pda-${programId.toBase58()}` : null,
async () => {
const [pda] = PublicKey.findProgramAddressSync(
[Buffer.from('otter_verify'), programAuthority!.toBuffer(), programId.toBuffer()],
new PublicKey(VERIFY_PROGRAM_ID)
);
const pdaAccountInfo = await connection.getAccountInfo(pda);
if (!pdaAccountInfo || !pdaAccountInfo.data) {
console.log('PDA account info not found');
return null;
}
return accountAnchorProgram?.coder.accounts.decode('buildParams', pdaAccountInfo.data);
},
{ suspense: options?.suspense }
);
return { data: error ? null : (data as OsecRegistryInfo), isLoading };

const isLoading = isRegistryLoading || isPdaLoading;

if (registryError || pdaError) {
return { data: null, error: registryError || pdaError, isLoading };
}

// Create command from the args of the verify PDA
if (registryData && pdaData && !isLoading) {
const verifiedData = registryData as OsecRegistryInfo;
verifiedData.verify_command = `solana-verify verify-from-repo -um --program-id ${programId.toBase58()} ${
pdaData.gitUrl
} --commit-hash ${pdaData.commit}`;

// Add additional args if available, for example mount-path and --library-name
if (pdaData.args && pdaData.args.length > 0) {
const filteredArgs = [];

for (let i = 0; i < pdaData.args.length; i++) {
const arg = pdaData.args[i];

if (arg === '-b' || arg === '--base-image') {
i++; // Also skip the parameter
continue;
}
filteredArgs.push(arg);
}

if (filteredArgs.length > 0) {
const argsString = filteredArgs.join(' ');
verifiedData.verify_command += ` ${argsString}`;
}
}

return { data: verifiedData, isLoading };
}
if (registryData && pdaData == null && !isLoading) {
const verifiedData = registryData as OsecRegistryInfo;

verifiedData.verify_command = isMainnet(cluster)
? 'Program does not have a verify PDA uploaded.'
: 'Verify command only available on mainnet.';
return { data: verifiedData, isLoading };
}

return { data: null, isLoading };
}

function isMainnet(currentCluster: Cluster): boolean {
return currentCluster == Cluster.MainnetBeta;
}

// Helper function to hash program data
export function hashProgramData(programData: ProgramDataAccountInfo): string {
const buffer = Buffer.from(programData.data[0], 'base64');
// Truncate null bytes at the end of the buffer
Expand Down

0 comments on commit c10c8ff

Please sign in to comment.