Nick Kolenda Reviews Preceden

Nick Kolenda is an author, teacher, and consultant who specializes in the psychology of marketing and related topics like pricing optimization, sales psychology, and website behavior. He’s also happens to be a regular in a poker game I host each week.

A few months back he mentioned that he was starting a new business to provide psychology-focused website reviews. With his large 50k+ mailing list audience and 13k+ YouTube subscribers he frequently gets asked to do these type of reviews and so turning it into a business made a lot of sense.

And so when he graciously offered for Preceden, my timeline maker software, to be his first review, I jumped at the opportunity.

Today he’s officially launching his business and with it his review of Preceden:

I highly recommend checking out the video: it’s packed with optimization insights covering the marketing copy, the design, the presentation of the example timelines, and more, all of which will help you see your own site in a new light. Even if you don’t have a site of your own, the review is worth watching anyway just to marvel at its production quality and Nick’s unique style.

If you’re interested in having Nick performa similar review for your site and having it shared with his audience, you can read more about the various services he offers on the Submit Your Business section of his site. And if you want to see more reviews like this one, you can subscribe to his mailing list.

Understanding Solana’s “instruction modified data of an account it does not own” error

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:

if account.owner != program_id {
msg!("Greeted account does not have the correct program id");
return Err(ProgramError::IncorrectProgramId);
}
view raw check.rs hosted with ❤ by GitHub

But if you leave that out, what happens?

Fortunately the Solana network rejects the attempt to modify the data in the other account:

'failed to verify account ...: instruction modified data of an account it does not own'

The docs explain:

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.

We can search for the error message above to find where it’s being used:

/// Program modified the data of an account that doesn't belong to it
#[error("instruction modified data of an account it does not own")]
ExternalAccountDataModified,

And digging in more, we can find where that error is thrown:

// For accounts unassigned to the program, the data may not change.
if *program_id != account.owner
&& !system_program::check_id(&program_id)
&& pre_data != &account.data[..]
{
return Err(InstructionError::ExternalAccountDataModified);
}

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.

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
   ...