Dealing with “failed to resolve: maybe a missing crate `solana_program`?”

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.

Dealing with “no such subcommand: `+bpf`”

When getting set up with Solana development you may run into this unhelpful error message:

$ npm run build:program-rust

> helloworld@0.0.1 build:program-rust
> cargo build-bpf --manifest-path=./src/program-rust/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
error: no such subcommand: `+bpf`

	Did you mean `b`?

The solution for me was to completely uninstall Rust:

$ rustup self uninstall

Even after doing this I still had a version of rust installed on my system, which may have been the cause of the error in the first place:

$ which rustc
/usr/local/bin/rustc

I uninstalled it with brew:

$ brew uninstall rust 
Uninstalling /usr/local/Cellar/rust/1.56.1... (30,734 files, 763.2MB)

$ which rustc
rustc not found

And then finally re-installed Rust using rustup:

$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

And then compiling the rust program worked without any issues:

$ npm run build:program-rust                                        

> helloworld@0.0.1 build:program-rust
> cargo build-bpf --manifest-path=./src/program-rust/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 proc-macro2 v1.0.27
   Compiling unicode-xid v0.2.2
   ...

Friday Updates: Crypto, Solana, Preceden Improvements, and the Madness of Crowds

Photo by Lenstravelier

Hey friends – it’s been a while since I posted an update. I’ve been heads down on a number of things and just haven’t been prioritizing blogging, but figure it’s good to do every now and then. Shout out to the ~3 of you who read these :).

Crypto

For anyone who follows me on Twitter, you may have seen me periodically retweeting or favoriting bearish crypto tweets. For a long time I have been super bearish on crypto and honestly still am. I think we’re in a massive bubble at the moment that’s due to pop in a big way in part due to the mania wearing off and in part due to the massive amount of fraud in the market right now. And blockchain technology just doesn’t seem super useful.

There’s a famous chart called the Gartner hype cycle that always comes to mind:

I’m of the opinion that we’re near the top of that Peak of Inflated Expectations curve, both in terms of crypto expectations and coin prices. But I do think there’s a chance that in the coming decade after the coming crash that we’ll see practical applications come out of all this.

Solana

In order to be more educated about crypto I’ve been investing a few hours each day into learning how to develop on the Solana blockchain. I’ve found it to be a pretty steep learning curve – I’m having to learn about blockchains, Solana, TypeScript, Rust, and how it all fits together. I don’t have any practical applications in mind yet, but am enjoying learning nonetheless.

I’m trying to keep an open mind – we’ll see what comes out of it all.

Preceden

I’ve made a ton of progress on Preceden in the last few months. Things like:

I haven’t been working on it as much the last few weeks because I’ve been putting time into learning Solana instead, but still have a lot planned for Preceden in the coming months.

Also, at the end of December 2020 I switched Preceden’s pricing to have recurring annual plans (whereas before they didn’t renew automatically), which means those renewals will start hitting in a few weeks. It should help drive a lot of revenue growth for Preceden in 2022. Fingers crossed the renewal rate is decent – we will see.

Help Scout

Mostly status quo for my contract work at Help Scout. Recent projects have included:

  • Reworking the Daily Metrics Email that goes out to the entire company
  • Setting up reporting to estimate cash payback (ie, if we spend $X acquiring a cohort of new customers, how long do we expect it to be paid back when looking solely at cash payments).
  • Updating Account Executive commission structures
  • Analyzing how customers are interacting with new pricing we introduced for Messages
  • Helping a bit with hiring: we’re currently in the process of hiring an Analytics Engineer and Product Analyst

I will be reducing my hours from 18/week to 12/week starting in January which will free up more time in my schedule for Preceden, Solana, and whatever else piques my interest in 2022.

What I’m reading

Speaking of crypto, I’ve been reading Memoirs of Extraordinary Popular Delusions and the Madness of Crowds by Charles MacKay, a fantastic book about past bubbles. It’s amazing how many parallels there are between previous bubbles and what we’re going through now in crypto. And things will probably end just as badly.

On that happy note, I’ll end this update :).

Hope y’all are doing well 👋

Splitting a Solana Keypair into Public and Private Keys

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:

$ cat ~/my-solana-wallet/demo.json

[4,182,130,247,119,117,227,207,112,73,170,126,222,197,244,99,215,107,255,202,33,43,36,17,104,111,157,246,196,192,174,95,240,23,238,206,118,215,154,238,229,96,11,37,156,123,51,223,5,231,17,117,86,136,103,14,75,95,175,132,148,54,1,46]  

This is an array containing 64 values:

The first 32 values represent the private key:

private_key_bytes = [4, 182, 130, 247, 119, 117, 227, 207, 112, 73, 170, 126, 222, 197, 244, 99, 215, 107, 255, 202, 33, 43, 36, 17, 104, 111, 157, 246, 196, 192, 174, 95]

The second 32 values represent the public key:

public_key_bytes = [240, 23, 238, 206, 118, 215, 154, 238, 229, 96, 11, 37, 156, 123, 51, 223, 5, 231, 17, 117, 86, 136, 103, 14, 75, 95, 175, 132, 148, 54, 1, 46]

While this is the 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):

public_key_hex = public_key_bytes.pack("C*").unpack("H*").first

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):

6Tyktf6mEqUMEKm2ZpLn3srEwk9zsT5jiE54EgPgToikMFYww1LGFUXgwgr6hvc9CikpaNaBH2vmkmqN3Usrxpd

Try it yourself

1. Create a new folder

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:

require_relative "base58"
def bytes_to_base58(bytes)
hex = bytes.pack("C*").unpack("H*").first
Base58.encode(hex)
end
keypair_bytes = [4,182,130,247,119,117,227,207,112,73,170,126,222,197,244,99,215,107,255,202,33,43,36,17,104,111,157,246,196,192,174,95,240,23,238,206,118,215,154,238,229,96,11,37,156,123,51,223,5,231,17,117,86,136,103,14,75,95,175,132,148,54,1,46]
private_key_bytes = keypair_bytes[0, 32]
public_key_bytes = keypair_bytes[32..-1]
puts "Keypair:", bytes_to_base58(keypair_bytes)
puts "\nPublic Key:", bytes_to_base58(public_key_bytes)
puts "\nPrivate Key:", bytes_to_base58(private_key_bytes)
view raw keypair.rb hosted with ❤ by GitHub

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:

$ ruby keypair.rb 
 
Keypair:
6Tyktf6mEqUMEKm2ZpLn3srEwk9zsT5jiE54EgPgToikMFYww1LGFUXgwgr6hvc9CikpaNaBH2vmkmqN3Usrxpd

Public Key:
HAE1oNnc3XBmPudphRcHhyCvGShtgDYtZVzx2MocKEr1

Private Key:
KQ3cGFBdjJuRsB7U1K4to6cTGBPhgukqPgsi5pryr8v

Starting with a Base 58 Keypair

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:

require_relative "base58"
def bytes_to_base58(bytes)
hex = bytes.pack("C*").unpack("H*").first
Base58.encode(hex)
end
keypair = "6Tyktf6mEqUMEKm2ZpLn3srEwk9zsT5jiE54EgPgToikMFYww1LGFUXgwgr6hvc9CikpaNaBH2vmkmqN3Usrxpd"
keypair_hex = Base58.decode(keypair).rjust(128, "0")
keypair_bytes = [keypair_hex].pack('H*').bytes.to_a
private_key_bytes = keypair_bytes[0, 32]
public_key_bytes = keypair_bytes[32..-1]
puts "Public Key:", bytes_to_base58(public_key_bytes)
puts "\nPrivate Key:", bytes_to_base58(private_key_bytes)
view raw keypair58.rb hosted with ❤ by GitHub
$ ruby keypair58.rb

Public Key:
HAE1oNnc3XBmPudphRcHhyCvGShtgDYtZVzx2MocKEr1

Private Key:
KQ3cGFBdjJuRsB7U1K4to6cTGBPhgukqPgsi5pryr8v