If you’re creating a program for the Solana blockchain using the Anchor framework,
it’s recommended to publish it as a verifiable build.
You can do this easily by running anchor build --verifiable
, and the result to
deploy to devnet.
When you deploy a verifiable build to the Solana network, you can verify that the program binary is the same
as one the deployer declares.
You can check out the source code of the program at a particular commit and run anchor verify
against it to see if the binary matches the checked-out code, and if no changes were made to the program.
To verify a deployed program, it must(!) be published as a verifiable build;
otherwise, the anchor verify
command will fail.
Note
|
This article talks about experiences at the Anchor version 0.27.0. |
A verifiable build is a program build that is created in a predefined and clean environment, in this case, in the Docker container.
By default, the docker image used is project-serum/anchor
,
configured at anchor cli in particular docker tag.
You can specify to use a specific image with the CLI arguments -d, --docker-image <DOCKER_IMAGE>
.
Or specify docker tag in Anchor.toml
file with
anchor_version
parameter
(project-serum/anchor
is the image for the tag).
When you run the anchor build --verifiable
command, the build process is executed inside the docker container.
anchor build --verifiable
The docker image is pulled from Docker Hub, and the build is executed inside it.
As it’s a clean environment, the build process requires the download of all dependencies and the compilation takes some time.
The result of the build is a standard .so
binary file, which is stored at directory ./target/verifiable/
.
The build also generates the IDLs, which are stored at ./target/idl/
and ./target/types
.
You can publish the compiled binary to the network (while I haven’t found a way to use anchor deploy
for this purpose).
Instead, you can use the solana
CLI tools.
solana program deploy -v -u devnet --keypair ~/.config/solana/devnet.json \
--program-id <PROGRAM_ID_KEYPAIR> \
--upgrade-authority <PATH_TO_KEYPAIR> \
./target/verifiable/program.so
Note
|
usually <PROGRAM_ID> is defined at Anchor.toml and for initial deployment we need to provide the keypair as the new account needs to be initialized
|
When you deploy the program, you also need to publish the Anchor IDL. Omitting to do so will result in a build verification failure. Use the IDL created by the verifiable build process.
anchor idl init --provider.cluster devnet \
--filepath ./target/idl/program.json \
<PROGRAM_ID>
Once you have uploaded the IDL, you can update the IDL account with the upgrade
subcommand using anchor idl upgrade …
.
Note that the IDL authority defines who has got the permission to upgrade the IDL.
anchor idl authority --provider.cluster devnet <PROGRAM_ID>
# one can change the idl account authority
anchor idl set-authority --provider.cluster devnet \
--program-id <PROGRAM_ID> \
--new-authority <AUTHORITY_PUBKEY>
# when a new authority is set --provider.wallet is for define what authority is used on idl upgrade
anchor idl upgrade \
--provider.cluster devnet \
--provider.wallet <AUTHORITY_KEYPAIR_PATH> \
--filepath ./target/idl/program.json \
<PROGRAM_ID>
Note
|
command anchor idl upgrade consists of two instructions which are sent to the network
and are defined within the anchor framework and are available as separate CLI commands as well.
It’s write-buffer (idl data saved at blockchain) and set-buffer (the saved data loaded into program idl account).
To combine write-buffer with set-buffer to manage IDL authority being multisig see https://gist.github.com/ochaloup/e013a7912858818ad2855e13754edf36
|
To verify the build, run the anchor verify
command. This command runs inside the docker image,
builds the program, generates the IDL, and compares the result with the one deployed to the network.
To start the verification process, check out the source code of the program at the commit declared being used for the build,
and then run the anchor verify
command.
# git checkout
git clone <GIT_REPOSITORY>
git checkout <COMMIT_HASH>
# anchor verify
anchor --provider.cluster devnet verify \
-p <PROGRAM_NAME> <PROGRAM_ID>
... Copying out the build artifacts Cleaning up the docker target directory Removing the docker container 595f4e94d9028adff8573512672adfc31a7dcce324a8b8976334acad963f1e0a Extracting the IDL Writing the IDL file Writing the .ts file Build success <PROGRAM_ID> is verified.
The <PROGRAM_NAME>`
refers to the name of the library defined in the
Cargo.toml
file.
The repository is usually in hyphenated form, while the library name is in underscore form.
When the name of the directory structure is programs/marinade-finance
,
then the <PROGRAM_NAME>
would most likely be marinade_finance
.
`<PROGRAM_ID>` refers to the address of the program that has been deployed on the blockchain, in our case, the one deployed on Devnet.
It’s worth noting that verification can be performed not only against the deployed program but also against the buffer. When you write binary to the buffer, you can verify the buffer content.
# write the program to buffer (-ud is shortcut for devnet cluster)
solana -ud program write-buffer ./target/verifiable/program.so
# buffer authority can be changed in case (not needed for verification)
solana -ud program -um set-buffer-authority \
--new-buffer-authority <AUTHORITY_PUBKEY> <BUFFER_ID>
# verify the buffer
anchor --provider.cluster devnet verify -p <PROGRAM_NAME> <BUFFER_ID>
Another point to note is that the verifiable build and verification process can also be performed with non-Anchor programs. We successfully used this with the SPL Governance and it worked well. However, since the IDL is not generated, there is no check for the IDL, and it works without it.
# get source code
git clone https://github.com/solana-labs/solana-program-library -b governance-v3.1.0
cd solana-program-library/governance/program
# do the build
anchor build --verifiable
# deploy the program (-k is keypair paying fee)
cd ../..
solana -um -k ~/.config/solana/mainnet.json program write-buffer \
--buffer-authority <SOME_AUTORITY_KEYPAIR> \
./target/verifiable/spl_governance.so
# later the program can be upgraded from buffer to <PROGRAM_ID>
solana program -um -k ~/.config/solana/mainnet.json deploy \
--program-id <PROGRAM_ID> \
--upgrade-authority <SOME_AUTORITY_KEYPAIR> \
--buffer <BUFFER_PUBKEY>
# verify the build
anchor --provider.cluster mainnet verify -p spl_governance <PROGRAM_ID>
My Docker build was failing with the anchor build --verifiable
command and returning the error message:
ERROR (5963): ENOENT: no such file or directory, open '/root/.config/solana/id.json'
To work around this issue, I used a different Docker image with a newer version of Anchor.
The projectserum/build:v0.27.0
image worked for me (use -d
switch or anchor_version
attribute).
Alternatively, you could use my own Docker image from
https://github.com/ochaloup/projectserum-build-docker (build it first locally).
anchor build --verifiable -d 'projectserum/build:v0.27.0'
Warning
|
When using a different Docker image for verification, be sure to double-check the result. The later verification is done against the IDL generated by the Docker image, and the IDL can be generated differently in dependency on anchor version. |
As I built the program first with an old version of Anchor (such as 0.18.2
)
the older version ommitted some fields, such as comments.
In comparison to the IDL built with a newer version of Anchor (such as 0.26.0
).
The anchor idl init
command creates the IDL account with a size that is
double the size of the IDL data being uploaded.
Unfortunately, when comments are included in the IDL, the data size exceeds the limit,
causing the verification of the IDL to fail (because the IDL built by the old Anchor version does not match the content with comments),
and the IDL cannot be upgraded, as the account size is too small.
I experienced the error
Idl buffer created: HhH987yt7K... Error: Error processing Instruction 0: custom program error: 0xbbc Caused by: Error processing Instruction 0: custom program error: 0xbbc
The error 0xbbc/3004
means Failed to serialize the account
,
indicating that there is not enough space.
The solution could be to initialize the IDL again,
but initialization of an already-existing account is not possible and will result in an error message (Error processing Instruction 0: custom program error: 0x0
).
What may help is Anchor version 0.27.0, which introduces the anchor idl close
command.
However, you have to build the verifiable binary compiled with Anchor 0.27.0.
Be aware that default tag for anchor build --verifiable
is 0.26.0
.
A program that was build with older Anchor version does not implement the command idl close
,
instead error 0x66/102
The program could not deserialize the given instruction
is emitted.
It is good practice for a contract to provide metadata about the program, including a link to the source code, information about audits, and how to contact the author. This is not only useful for security researchers.
Neodyme Labs has created a Rust library that defines a macro, called
solana-security-txt
inspired by security.txt standard.
To use this library, the program creator should add the Rust dependency`solana-security-txt = "1.1.0"` to
Cargo.toml
file, and then add the metadata to the library source code using
the security_txt!
macro definition. A simple example can be seen below.
/// solana-security-txt for admin contract
use solana_security_txt::security_txt;
security_txt! {
name: "Simple admin contract for testing purposes",
project_url: "https://github.com/ochaloup/simple-admin",
contacts: "twitter: @_chalda",
policy: "",
preferred_languages: "en, cz",
auditors: "None"
}
The format, required fields, and other details are described in the library’s README on GitHub.
One of the benefits of this standard is that it is integrated within the Solana explorer, allowing easy access to the metadata by anyone who wishes to check it.
Verifiable builds allow for the verification of the program binary against the source code. This is the recommended way to publish a program to the Solana blockchain.