Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ts: verified build utility #1371

Merged
merged 3 commits into from
Jan 29, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
3 changes: 2 additions & 1 deletion ts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 1 addition & 0 deletions ts/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 110 additions & 0 deletions ts/src/utils/registry.ts
Original file line number Diff line number Diff line change
@@ -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;
};
40 changes: 36 additions & 4 deletions ts/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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"
Expand All @@ -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"
Expand Down