ts: verified build utility (#1371)
This commit is contained in:
parent
e6333e3554
commit
46722953c1
|
@ -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
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
ts/yarn.lock
40
ts/yarn.lock
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue