In my ongoing attempt to understand Solana program development better, I’m trying to do unexpected things to see what happens.
For example, with Solana’s example Hello World program, what happens when you try to have the program modify an account it does not own? After all, the client can provide any account(s) to the program. Normally this would be an account the program owns, but what if it isn’t?
In theory the program should be confirming ownership and indeed the example program does:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The runtime grants the program write access to the account if its id matches the owner… If an account is not owned by a program, the program is only permitted to read its data and credit the account.
I’d argue the wording of the error message could use improvement: “instruction modified data” implies the it successfully modified the data, but in reality it attempted to and the network rejected it.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Given that the network will reject attempts to modify accounts that the program doesn’t own, it doesn’t seem 100% necessary to have the account ownership check in this Hello World program, but it’s still good practice to check before attempting to modify the data. The reason is that other programs may read the data from the account and do something with that data, so if you don’t verify ownership those programs could wind up doing something unintentional because it’s using data from an account it does not own. Again the docs explain:
This is because a malicious user could create accounts with arbitrary data and then pass these accounts to the program in place of valid accounts. The arbitrary data could be crafted in a way that leads to unexpected or harmful program behavior.
Therefore, leave the ownership check in place because you don’t want to forget to include it in a program where it actually matters.
I ran into a few odd errors when compiling a Solana program:
$ npm run build
> hello-world@1.0.0 build
> cargo build-bpf --manifest-path=./src/program/Cargo.toml --bpf-out-dir=./dist/program
BPF SDK: /Users/matt/.local/share/solana/install/releases/1.8.6/solana-release/bin/sdk/bpf
cargo-build-bpf child: rustup toolchain list -v
cargo-build-bpf child: cargo +bpf build --target bpfel-unknown-unknown --release
Compiling program v0.0.1 (/Users/matt/code/solana/hello-world/src/program)
error[E0433]: failed to resolve: maybe a missing crate `solana_program`?
--> src/lib.rs:3:5
|
3 | use solana_program::{
| ^^^^^^^^^^^^^^ maybe a missing crate `solana_program`?
error[E0432]: unresolved import `borsh`
--> src/lib.rs:1:5
|
1 | use borsh::{BorshDeserialize, BorshSerialize};
| ^^^^^ maybe a missing crate `borsh`?
error[E0432]: unresolved import `solana_program`
--> src/lib.rs:3:5
|
3 | use solana_program::{
| ^^^^^^^^^^^^^^ maybe a missing crate `solana_program`?
Took a while to figure out, but in the end the reason was that I was missing an edition value in Cargo.toml.
I had:
[package]
name = "program"
version = "0.0.1"
But needed:
[package]
name = "program"
version = "0.0.1"
edition = "2021"
Note that the name and package can be whatever you want here.
The edition is automatically set when you use cargo new, but at some point I had deleted it when cleaning up the manifest. Without it set, Rust assumes the edition is 2015 for backwards compatibility, but this causes problems during the Solana program compilation.
In this guide we’re going to dive into how to take a Solana keypair and break it its corresponding public and private keys.
Lets go ahead and use the Solana CLI to generate a new wallet with the keypair stored in a File System Wallet:
$ solana-keygen new --outfile ~/my-solana-wallet/demo.json
Generating a new keypair
For added security, enter a BIP39 passphrase
NOTE! This passphrase improves security of the recovery seed phrase NOT the
keypair file itself, which is stored as insecure plain text
BIP39 Passphrase (empty for none):
Wrote new keypair to /Users/matt/my-solana-wallet/demo.json
=============================================================================
pubkey: HAE1oNnc3XBmPudphRcHhyCvGShtgDYtZVzx2MocKEr1
=============================================================================
Save this seed phrase and your BIP39 passphrase to recover your new keypair:
favorite maid bind piece social father neutral over unusual tank brother code
=============================================================================
This command generates a JSON file containing the keypair:
While this isthe public key, it’s not in the same format as the public key displayed by the Solana CLI above. In order to format it that way, we need to convert this array of integer values into base 58.
In Ruby we’ll first convert the array to hex (base 16):
And then we can use a base 58 script to convert the hex into the base 58:
public_key = Base58.encode(public_key_hex)
And that gives us the same public key aka wallet address that we saw earlier in the Solana CLI output:
HAE1oNnc3XBmPudphRcHhyCvGShtgDYtZVzx2MocKEr1
We can follow the same process to get the base 58 private key:
KQ3cGFBdjJuRsB7U1K4to6cTGBPhgukqPgsi5pryr8v
And also the base 58 keypair (which is the same thing you would get if you clicked the Export Private Key button in a Phantom Wallet, but note that it’s not actually the private key, it’s the keypair with both the private and public key – I suspect they call it that to make it clear to users that it includes the private key):
2. Create a file called base58.rb and copy the base 58 Ruby script from this tutorial
3. Create another file named like keypair.rb and paste in the following script:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4. Replace the content of keypair_bytes with the value from your File System Wallet (you can use solana config get to get the path to your keypair, then use the cat command like above to get the content).
5. Then from the command line simply run ruby keypair.rb to run the script:
You can follow a similar process to take a base 58 keypair (like you would get from Export Private Key in a Phantom wallet) to derive the public and private keys:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters