-
-
Save keitaj/c6bce4dbd7f4f23bbadfc339e45691ca to your computer and use it in GitHub Desktop.
use borsh::{BorshDeserialize, BorshSerialize}; | |
use solana_program::{ | |
account_info::{next_account_info, AccountInfo}, | |
entrypoint, | |
entrypoint::ProgramResult, | |
msg, | |
program_error::ProgramError, | |
pubkey::Pubkey, | |
}; | |
/// Define the type of state stored in accounts | |
#[derive(BorshSerialize, BorshDeserialize, Debug)] | |
pub struct GreetingAccount { | |
/// number of greetings | |
pub counter: u32, | |
} | |
// Declare and export the program's entrypoint | |
entrypoint!(process_instruction); | |
// Program entrypoint's implementation | |
pub fn process_instruction( | |
program_id: &Pubkey, // Public key of the account the hello world program was loaded into | |
accounts: &[AccountInfo], // The account to say hello to | |
_instruction_data: &[u8], // Ignored, all helloworld instructions are hellos | |
) -> ProgramResult { | |
msg!("Hello World Rust program entrypoint"); | |
// Iterating accounts is safer than indexing | |
let accounts_iter = &mut accounts.iter(); | |
// Get the account to say hello to | |
let account = next_account_info(accounts_iter)?; | |
// The account must be owned by the program in order to modify its data | |
if account.owner != program_id { | |
msg!("Greeted account does not have the correct program id"); | |
return Err(ProgramError::IncorrectProgramId); | |
} | |
// Increment and store the number of times the account has been greeted | |
let mut greeting_account = GreetingAccount::try_from_slice(&account.data.borrow())?; | |
greeting_account.counter += 1; | |
greeting_account.serialize(&mut &mut account.data.borrow_mut()[..])?; | |
msg!("Greeted {} time(s)!", greeting_account.counter); | |
Ok(()) | |
} | |
// Sanity tests | |
#[cfg(test)] | |
mod test { | |
use super::*; | |
use solana_program::clock::Epoch; | |
use std::mem; | |
#[test] | |
fn test_sanity() { | |
let program_id = Pubkey::default(); | |
let key = Pubkey::default(); | |
let mut lamports = 0; | |
let mut data = vec![0; mem::size_of::<u32>()]; | |
let owner = Pubkey::default(); | |
let account = AccountInfo::new( | |
&key, | |
false, | |
true, | |
&mut lamports, | |
&mut data, | |
&owner, | |
false, | |
Epoch::default(), | |
); | |
let instruction_data: Vec<u8> = Vec::new(); | |
let accounts = vec![account]; | |
assert_eq!( | |
GreetingAccount::try_from_slice(&accounts[0].data.borrow()) | |
.unwrap() | |
.counter, | |
0 | |
); | |
process_instruction(&program_id, &accounts, &instruction_data).unwrap(); | |
assert_eq!( | |
GreetingAccount::try_from_slice(&accounts[0].data.borrow()) | |
.unwrap() | |
.counter, | |
1 | |
); | |
process_instruction(&program_id, &accounts, &instruction_data).unwrap(); | |
assert_eq!( | |
GreetingAccount::try_from_slice(&accounts[0].data.borrow()) | |
.unwrap() | |
.counter, | |
2 | |
); | |
} | |
} |
Set up Solana CLI
Solana Programをデプロイする前に、Solanaクラスターを設定し、アカウントを作成し、エアドロップを要求し、すべてが正しく機能していることを確認する必要があります。
CLI の設定 URL を devnet クラスターに設定します。
solana config set --url https://api.devnet.solana.com
次に、CLIを使用して新しいキーペアを生成します。ターミナルで以下のコマンドを実行します。
mkdir solana-wallet
solana-keygen new --outfile solana-wallet/keypair.json
デプロイには、アカウントで利用できるSOLが必要ですので、AirDrop withを入手してください。
solana airdrop 1 $(solana-keygen pubkey solana-wallet/keypair.json)
すべての設定が完了し、アドレスに1SOLの資金が投入されたことを確認します。
solana config get
solana account $(solana-keygen pubkey solana-wallet/keypair.json)
Solana programデプロイする前にRustのインストールが必要
https://doc.rust-jp.rs/book-ja/ch01-01-installation.html
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
Filesize limit exceededのエラー出たらとりあえずこれで対応
ulimit -f unlimited
インストールしたらPATH通す
export PATH="$HOME/.cargo/bin:$PATH"
rustを最新版にアップデート
rustup update
rustcのバージョン確認
rustc --version
Deploy a Solana program
これからデプロイするプログラムは、あるアカウントが挨拶の指示を送った回数を記録しています。これは、Solana上でストレージがどのように機能するかを効果的に示すものです。
Building the program
yarn run solana:build:program
成功すると、helloworld.soというコンパイル済みのプログラムへのパスを指定してdeployコマンドを実行するように指示されます。このままでもいいのですが、この目的のためだけに生成したキーペアを指定したいので、読み進めてください。
To deploy this program:
$ solana program deploy /home/zu/project/figment/learn-web3-dapp/dist/solana/program/helloworld.so
Done in 1.39s.
Deploying the program
CLIのsolana deployを使って、プログラムをdevnetクラスタにデプロイする
solana deploy -v --keypair solana-wallet/keypair.json dist/solana/program/helloworld.so
vフラグはオプションですが、RPC URLやデフォルトの署名者鍵ペアのパス、予想されるコミットメントレベルなどの関連情報が表示されます。プロセスが完了すると、プログラムIDが表示されます。
成功した場合、CLIはデプロイされたコントラクトのprogramIdを表示します。
RPC URL: https://api.devnet.solana.com
Default Signer Path: solana-wallet/keypair.json
Commitment: confirmed
Program Id: 8LwWMLn37RKFLFz84HwaigLfpeoXCaXSFhcXooeDQBpW
Deploying the program to a test validator inside Gitpod
クラスタの変更
solana config set --url http://127.0.0.1:8899
テストバリデーターの実行
solana-test-validator
Gitpodで新しいターミナルを開きます (あるいはテストバリデータを実行しているターミナルを分割します)。この新しいターミナルで、Solana CLI の場所をコマンドで PATH に追加する必要があります。
export PATH="/home/gitpod/.local/share/solana/install/active_release/bin:$PATH"
/solana-wallet/keypair.json にあるキーペアに SOL の残高があることを確認し、SOL をいくらかエアドロップすることでデプロイの代金を支払います (テストバリデータ上なので、もっと高額な SOL を指定することも可能です)。
solana airdrop 100 $(solana-keygen pubkey solana-wallet/keypair.json)
これで、プログラムをテストバリデータにデプロイするコマンドを実行することができます。
solana deploy -v --keypair solana-wallet/keypair.json dist/solana/program/helloworld.so
Refs
% solana --version
solana-cli 1.10.5 (src:5eb085fc; feat:3235626988)
% rustc --version
rustc 1.60.0 (7737e0b5c 2022-04-04)
次に、エントリポイントであるprocess_instruction関数を宣言します。
Result は std クレートに由来し、エラーの可能性を表現するために使用されます。
アカウントの所有者が許可されているかどうか、セキュリティチェックを行います。もし account.owner の公開鍵が program_id と等しくない場合は、エラーを返します。
最後に、既存のアカウントデータを「借りて」、カウンターの値を1つ増やし、ストレージに書き戻すという、いいとこ取りの処理をします。
つまり、アカウントデータを借りてきて、それをデシリアライズする関数に渡し、エラーが発生した場合はエラーを返すということです。?はエラー伝搬のためのものであることを思い出してください。
次に、カウンターの値を1つ増やすのは、加算代入演算子+=を使えば簡単です。
BorshSerializeのserialize()関数により、新しいカウンタ値が正しいフォーマットでSolanaに送り返されます。このメカニズムは、std::ioクレートのWrite traitによって実現されています。
そして、そのカウントが何回インクリメントされたかは、msg!を使って見ることができる。