ts: verified build utility (#1371)

This commit is contained in:
Armani Ferrante 2022-01-29 16:46:12 -05:00 committed by GitHub
parent e6333e3554
commit 46722953c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 150 additions and 5 deletions

View File

@ -17,6 +17,7 @@ incremented for features.
* lang: `Context` now has a new `bumps: BTree<String, u8>` argument, mapping account name to bump seed "found" by the accounts context. This allows one to access bump seeds without having to pass them in from the client or recalculate them in the handler ([#1367](calculated )).
* ts: Remove error logging in the event parser when log websocket encounters a program error ([#1313](https://github.com/project-serum/anchor/pull/1313)).
* ts: Add new `methods` namespace to the program client, introducing a more ergonomic builder API ([#1324](https://github.com/project-serum/anchor/pull/1324)).
* ts: Add registry utility for fetching the latest verified build ([#1371](https://github.com/project-serum/anchor/pull/1371)).
### Breaking

View File

@ -33,13 +33,14 @@
"test": "jest tests --detectOpenHandles"
},
"dependencies": {
"@project-serum/borsh": "^0.2.2",
"@project-serum/borsh": "^0.2.4",
"@solana/web3.js": "^1.17.0",
"base64-js": "^1.5.1",
"bn.js": "^5.1.2",
"bs58": "^4.0.1",
"buffer-layout": "^1.2.2",
"camelcase": "^5.3.1",
"cross-fetch": "^3.1.5",
"crypto-hash": "^1.3.0",
"eventemitter3": "^4.0.7",
"find": "^0.3.0",

View File

@ -4,3 +4,4 @@ export * as publicKey from "./pubkey.js";
export * as bytes from "./bytes/index.js";
export * as token from "./token.js";
export * as features from "./features.js";
export * as registry from "./registry.js";

110
ts/src/utils/registry.ts Normal file
View File

@ -0,0 +1,110 @@
import BN from "bn.js";
import fetch from "cross-fetch";
import * as borsh from "@project-serum/borsh";
import { Connection, PublicKey } from "@solana/web3.js";
/**
* 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 verifiedBuild(
connection: Connection,
programId: PublicKey,
limit: number = 5
): Promise<Build | null> {
const url = `https://anchor.projectserum.com/api/v0/program/${programId.toString()}/latest?limit=${limit}`;
const [programData, latestBuildsResp] = await Promise.all([
fetchData(connection, programId),
fetch(url),
]);
// Filter out all non successful builds.
const latestBuilds = (await latestBuildsResp.json()).filter(
(b: Build) => !b.aborted && b.state === "Built" && b.verified === "Verified"
);
if (latestBuilds.length === 0) {
return null;
}
// Get the latest build.
const build = latestBuilds[0];
// Has the program been upgraded since the last build?
if (programData.slot.toNumber() !== build.verified_slot) {
return null;
}
// Success.
return build;
}
/**
* Returns the program data account for this program, containing the
* metadata for this program, e.g., the upgrade authority.
*/
export async function fetchData(
connection: Connection,
programId: PublicKey
): Promise<ProgramData> {
const accountInfo = await connection.getAccountInfo(programId);
if (accountInfo === null) {
throw new Error("program account not found");
}
const { program } = decodeUpgradeableLoaderState(accountInfo.data);
const programdataAccountInfo = await connection.getAccountInfo(
program.programdataAddress
);
if (programdataAccountInfo === null) {
throw new Error("program data account not found");
}
const { programData } = decodeUpgradeableLoaderState(
programdataAccountInfo.data
);
return programData;
}
const UPGRADEABLE_LOADER_STATE_LAYOUT = borsh.rustEnum(
[
borsh.struct([], "uninitialized"),
borsh.struct(
[borsh.option(borsh.publicKey(), "authorityAddress")],
"buffer"
),
borsh.struct([borsh.publicKey("programdataAddress")], "program"),
borsh.struct(
[
borsh.u64("slot"),
borsh.option(borsh.publicKey(), "upgradeAuthorityAddress"),
],
"programData"
),
],
undefined,
borsh.u32()
);
export function decodeUpgradeableLoaderState(data: Buffer): any {
return UPGRADEABLE_LOADER_STATE_LAYOUT.decode(data);
}
export type ProgramData = {
slot: BN;
upgradeAuthorityAddress: PublicKey | null;
};
export type Build = {
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;
};

View File

@ -855,10 +855,10 @@
"@nodelib/fs.scandir" "2.1.4"
fastq "^1.6.0"
"@project-serum/borsh@^0.2.2":
version "0.2.2"
resolved "https://registry.npmjs.org/@project-serum/borsh/-/borsh-0.2.2.tgz"
integrity sha512-Ms+aWmGVW6bWd3b0+MWwoaYig2QD0F90h0uhr7AzY3dpCb5e2S6RsRW02vFTfa085pY2VLB7nTZNbFECQ1liTg==
"@project-serum/borsh@^0.2.4":
version "0.2.4"
resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.4.tgz#8884c3a759984a39d54bf5b7390bd1ee0b579f16"
integrity sha512-tQPc1ktAp1Jtn9D72DmObAfhAic9ivfYBOS5b+T4H7MvkQ84uML88LY1LfvGep30mCy+ua5rf+X9ocPfg6u9MA==
dependencies:
bn.js "^5.1.2"
buffer-layout "^1.2.0"
@ -1836,6 +1836,13 @@ create-require@^1.1.0:
resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-fetch@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
dependencies:
node-fetch "2.6.7"
cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz"
@ -3744,6 +3751,13 @@ node-addon-api@^2.0.0:
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz"
integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==
node-fetch@2.6.7:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"
node-fetch@^2.6.1:
version "2.6.1"
resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz"
@ -4695,6 +4709,11 @@ tr46@^2.1.0:
dependencies:
punycode "^2.1.1"
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
traverse-chain@~0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1"
@ -4929,6 +4948,11 @@ walker@^1.0.7:
dependencies:
makeerror "1.0.x"
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
webidl-conversions@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz"
@ -4951,6 +4975,14 @@ whatwg-mimetype@^2.3.0:
resolved "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz"
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
whatwg-url@^8.0.0:
version "8.4.0"
resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz"