Solana teardown: Walkthrough of the example helloworld program
Tags: solana, rust, beginners, web3
Published: 2021-10-22 14:34:39

In this article, we'll walk through the [helloworld][1] Rust program line-by-line and begin to unpack how programs on Solana work.

## Who this article is for

This walkthrough assumes that you've written code in any programming language or have a basic understanding of Rust. You don't need prior experience writing programs (smart contracts) on Solana or any other blockchain. I won't spend much time explaining the Rust specific code or idioms but rather focus on the Solana program.

Let's get started.

## Use declaration

```rust
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
    account_info::{next_account_info, AccountInfo},
    entrypoint,
    entrypoint::ProgramResult,
    msg,
    program_error::ProgramError,
    pubkey::Pubkey,
};
```

We bring libraries and traits into scope with the `use` declaration. It allows us to bind a full path to a new name for easier access.

## Greeting Account

```rust
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct GreetingAccount {
    /// number of greetings
    pub counter: u32,
}
```

Programs are stateless. If you need to store state between transactions, you can do so using Accounts. In Solana, **EVERYTHING** is an account. The Solana blockchain only sees a list of accounts of varying sizes. If an account is marked "executable" in its metadata, then it is considered a program. We'll talk more on accounts later, but for now, the understanding we need is that; our Greeting Account can hold data, and this data is in the shape of our `GreetingAccount` struct. Our accounts `data` property will have a `counter` property which we'll use to store a count of all the greetings. This data will persist beyond the lifetime of our program.

## Entrypoint

```rust
entrypoint!(process_instruction);
```

We register the `process_instruction` as the entrypoint symbol which the Solana runtime looks up and calls when invoking the program.

## process_instruction

```rust
pub fn process_instruction(
    program_id: &Pubkey,
    accounts: &[AccountInfo],
    _instruction_data: &[u8],
) -> ProgramResult {
// --snip--
```

All programs have the same signature. Let's take an in-depth look at the parameters.

### program_id

The `program_id` is the public key (the address) of the currently executing program. In our case, this will be the public key of our HelloWorld program.

### accounts

Accounts really deserve their own post. For now, I'll keep it brief. The `accounts` is an **ordered** list of the needed accounts for this transaction. The only properties that an account owner can modify are `lamports` and `data`.

### instruction_data

The `_instruction_data` is specific to the program and is usually used to specify what operations the program should perform. In this helloworld example, we only have one instruction, "greet", so the `instruction_data` is unused, hence the preceding `_`. That's a Rust idiom to keep the compiler happy.

## Logging

```Rust
msg!("Hello World Rust program entrypoint");
```

We can output logs from our program using the `msg!` macro. The caution here is that these messages are visible by viewing the program logs on the [solana explorer][2].

## Accounts

```rust
let accounts_iter = &mut accounts.iter();
let account = next_account_info(accounts_iter)?;
```

We take the list of `accounts` we received via our entrypoint and convert it into an iterable that we can iterate over. The [Solana program library][4] provides the `next_account_info` function  that returns the next account in the list.

It's important to note that programming in Solana requires you to think about your "data model" or "program model", as it were. Since we have to specify all the accounts a transaction will need, we need to think about; what accounts our program needs, the purpose of the account, and the relationships between them. For example, when transferring lamports, an instruction may define the first account as the source and the second as the destination.

The instructions our program can perform are essentially the API of our program. Therefore, we must know how to call these APIs and what order  we need to provide the accounts. We specify this in an `instructions.rs` file. An example of this can be found in the [token program][3].

## Validation and Security

```rust
if account.owner != program_id {
    msg!("Greeted account does not have the correct program id");
    return Err(ProgramError::IncorrectProgramId);
}
```

It's always good practice to validate the accounts you receive and assert that they have the correct permissions and are the accounts you are expecting.

Since our program needs to modify the `data` property, we need to check that the program is the owner of the account we are about to change. If not, we error out of the program.

Have a look at programs written in the [Solana Program Library][4]. You'll always find an exhaustive list of checks validating the inputs to the program.

## Serialization / Deserialization

```rust
let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
greeting_account.counter += 1;
```

Hidden in here are a lot of rust idioms, but we'll focus on the deserializing part.

In Solana, an account can store ANY binary data. The Solana blockchain doesn't know what that data is. It's the responsibility of the program or client to understand it. For you to store data, you incur a storage cost called _rent_. Rent is another pretty big topic, so we'll gloss over it for now and do an in-depth look when we do a teardown of the client app in a future post.

Back to the data. Each account can store its own type of data. In our case, the data we are storing is of type `GreetingAccount`. Since we store the data in binary, we need to deserialize it when reading it and serialize it when saving it.

Let's retake a look at our struct.

```rust
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct GreetingAccount {
    /// number of greetings
    pub counter: u32,
}
```

To increment the counter, we need to deserialize the binary data into a `GreetingAccount`. For this, we use the [borsh][5] library. Solana doesn't enforce how we serialize or deserialize our data, so the choice of library is up to us.

```rust
let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?;
```

We take the binary data in `account.data` and **try** to deserialize it into a `GreetingAccount`. If that fails, we'll get an error.

You can read more on the `.borrow()` syntax from the [official Solana docs][6].

```rust
greeting_account.counter += 1;
```

Once we have successfully deserialized the data, we can now increment the counter.

```rust
greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?;
```

We call the serialize method on the `greeting_acocunt`, which serializes the `GreetingAccount` struct and updates the `data` property.


```rust
msg!("Greeted {} time(s)!", greeting_account.counter);

Ok(())
```

We then log the new counter and return the result of our program to signal that this transaction was successful.

## Resources

* [helloworld][1] Rust program
* [Solana programming model](https://docs.solana.com/developing/programming-model/overview)
* [Solana](https://www.youtube.com/c/Solanalabs/videos) YouTube channel
* [Solana Program Library][4]

## Footnotes

Thank you for the guidance, support and review of this article.

[@therealchaseeb](https://twitter.com/therealchaseeb), [@jamesflorentino](https://twitter.com/jamesflorentino) and [@fastfrank](https://github.com/FrankC01).

---

Follow me on [twitter](https://twitter.com/C_Ogoo) to read more about developing on Solana.

[1]:https://github.com/solana-labs/example-helloworld/blob/master/src/program-rust/src/lib.rs
[2]:https://explorer.solana.com/tx/2hnw1ugqChuGrVq1GdBk63a3rHk3GvcoyuXLS9MyUneza9M4hYcqGdBUSCr2Hp6djEvzFPv34dV4iMXkGCQJjFft
[3]:https://github.com/solana-labs/solana-program-library/blob/master/token/program/src/instruction.rs#L32
[4]:https://github.com/solana-labs/solana-program-library
[5]:https://crates.io/crates/borsh
[6]:https://docs.solana.com/developing/on-chain-programs/developing-rust#data-types