Tämä pikakäynnistysopas käyttää
Anchor-sovelluskehyksen aloitusprojektia,
jonka anchor init luo. Luot projektin paikallisesti, suoritat sen testit,
rakennat ohjelman ja käyt läpi ohjelmakoodin, jonka Anchor generoi.
Edellytykset
Ennen kuin aloitat, asenna Solana-kehitystyökalut. Asennus sisältää Rustin, Solana CLI:n ja Anchor CLI:n.
Käytä tähän malliin Anchor CLI -versiota 1.1.2 tai uudempaa. Tarkista asennettu versio:
$anchor --version
Luo projekti
Suorita seuraavat komennot terminaalissasi:
$anchor init my-program$cd my-program
Aloitusprojekti sisältää yhden Solana-ohjelman hakemistossa
programs/my-program. Ohjelma sisältää kaksi käskyä: yhden laskuritilin
alustamiseen ja yhden laskurin kasvattamiseen.
Osa mallin osioista havainnollistaa yleisiä Solana-ohjelmointimalleja: PDA-tilin osoitteiden johtaminen, Cross Program Invocation (CPI) SOL:n siirtämiseksi sekä mukautettujen virheentarkistusten käyttö käskyn pysäyttämiseksi, kun ehto ei täyty.
Rakenna ohjelma
Suorita anchor build kääntääksesi aloitusohjelman:
$anchor build
Käännetty ohjelma kirjoitetaan kohteeseen target/deploy/my_program.so. Kun
ohjelma otetaan käyttöön, tämän .so-tiedoston sisältö tallennetaan ketjussa
olevalle tilille.
Suorita testi
Suorita oletustesti:
$anchor test
Tämän mallin Anchor.toml käyttää Rust-testikomentoa:
skip_local_validator = true[scripts]test = "cargo test"
Testi lataa käännetyn ohjelman LiteSVM-ympäristöön,
luo maksajan, lähettää initialize- ja increment-käskyt, jonka
jälkeen tarkistaa laskuritilin tilan.
anchor test-komennon suorittaminen kääntää myös ohjelman, joten sinun ei
tarvitse ajaa anchor build-komentoa ensin paikallista testausta varten.
Ota ohjelma käyttöön
Paikalliset testit tarjoavat nopeimman palautesilmukan. Kun olet valmis ottamaan käyttöön verkossa, esimerkiksi devnetissä, käännä ensin ja ota sitten käyttöön klusterissa.
Solana-ohjelman käyttöönottovaatimuksena on SOL, koska ohjelma tallennetaan program account -tilille, jonka täytyy maksaa käyttämästään tilasta. Devnetissä voit pyytää ilmaista devnet-SOL:ia Solana Faucetista tai Solana CLI:llä:
$solana airdrop 2 --url devnet
$anchor build$anchor deploy --provider.cluster devnet
Lähdetiedostot
src-hakemisto sisältää Solana-ohjelman. Anchor'in
Ohjelmarakenne
-dokumentaatio selittää tässä käytetyt keskeiset makrot, mukaan lukien
declare_id!, #[program], #[derive(Accounts)] ja
#[account]. Tässä osiossa käydään läpi mallin tiedostot.
lib.rs
lib.rs on ohjelman sisääntulopiste. Se yhdistää lähdetiedostot, määrittelee
ohjelman osoitteen ja määrittelee ohjelmakomennot, joita käyttäjät voivat
kutsua.
pub mod constants;pub mod error;pub mod instructions;pub mod state;use anchor_lang::prelude::*;pub use constants::*;pub use instructions::*;pub use state::*;declare_id!("82sFkffP9wxwpyfZyeaKHH2chQoJPUGsJZSPi9mrUuXd");#[program]pub mod my_program {use super::*;pub fn initialize(ctx: Context<Initialize>) -> Result<()> {crate::instructions::initialize::handle_initialize(ctx)}pub fn increment(ctx: Context<Increment>) -> Result<()> {crate::instructions::increment::handle_increment(ctx)}}
[programs.localnet]my_program = "82sFkffP9wxwpyfZyeaKHH2chQoJPUGsJZSPi9mrUuXd"
declare_id!("82sFkffP9wxwpyfZyeaKHH2chQoJPUGsJZSPi9mrUuXd");
Sama ohjelma-osoite esiintyy sekä konfiguraatiossa että koodissa. Anchor.toml
kertoo Anchorille, mihin osoitteeseen ohjelma otetaan käyttöön tai kutsutaan
klusterissa. declare_id! määrittää ohjelma-osoitteen ohjelmassa
turvatarkistuksia varten.
constants.rs
constants.rs pitää jaetut arvot yhdessä paikassa. Tässä mallissa
COUNTER_SEED johtaa laskurin PDA:n, HELLO_WORLD_LAMPORTS siirretään
alustuksen aikana ja MAX_COUNT tarkistetaan ennen kasvattamista.
use anchor_lang::prelude::*;#[constant]pub const COUNTER_SEED: &[u8] = b"counter";#[constant]pub const HELLO_WORLD_LAMPORTS: u64 = 1;#[constant]pub const MAX_COUNT: u64 = 10;
#[derive(Accounts)]pub struct Initialize<'info> {// ...#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,// ...}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {// ...anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;Ok(())}
initialize.rs käyttää kahta vakiota:
COUNTER_SEEDjohtaa laskurin PDA-osoitteen.HELLO_WORLD_LAMPORTSmäärittää summan, joka siirretään maksajalta laskuritilille.
pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {// ...require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;Ok(())}
increment.rs käyttää MAX_COUNT laskurin ylänä
rajana. Jos nykyinen arvo on jo maksimissa, require! palauttaa
CounterOverflow eikä tilin tietoja muuteta.
state.rs
state.rs määrittää mukautetut tietotyypit tileille, jotka ohjelma luo ja
omistaa. Ohjelma määrittää ohjeet tietojen luomiseen, alustamiseen ja
päivittämiseen, mutta laskuridata ei ole tallennettu itse ohjelmaan. Se on
tallennettu erilliselle tilille, jolla on oma osoitteensa.
use anchor_lang::prelude::*;#[account]#[derive(InitSpace)]pub struct Counter {pub count: u64,pub authority: Pubkey,}
#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,// ...pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();Ok(())}
pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {// ...ctx.accounts.counter.count += 1;Ok(())}
state.rs määrittelee Counter-tilin tiedot. Ohjetiedostot käyttävät tätä
tyyppiä luodessaan ja päivittäessään tilin:
Counter::INIT_SPACEmäärittää tilin koonstate.rs:ssä määriteltyjen kenttien mukaan.countjaauthorityovat kenttien arvoja, jotka kirjoitetaan tilin alustuksen yhteydessä.count += 1päivittää tallennetun laskuriarvon validoinnin läpäisemisen jälkeen.
error.rs
error.rs määrittelee ohjelman mukautetut virheet. Tässä mallissa virheet
havainnollistavat, kuinka ohjetoiminnot pysähtyvät, kun kutsujalla ei ole
oikeutta päivittää laskuria tai laskuri on jo saavuttanut MAX_COUNT-arvon.
use anchor_lang::prelude::*;#[error_code]pub enum ErrorCode {#[msg("Only the counter authority can update this counter")]Unauthorized,#[msg("Counter has reached the maximum value")]CounterOverflow,}
pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
error.rs nimeää virheet, joita ohje voi palauttaa:
ErrorCode::Unauthorizedpalautetaan, kun allekirjoittaja ei ole laskuritiliin tallennettu auktoriteetti.ErrorCode::CounterOverflowpalautetaan, kun laskuri on jo saavuttanutMAX_COUNT-arvon.
instructions.rs
instructions.rs yhdistää ohjetiedostot ohjelmapaketille, jotta lib.rs voi
käyttää initialize- ja increment-ohjeistuksen koodia. Jokainen
ohjetiedosto määrittelee kyseisen ohjeen vaatimat tilit sekä
käsittelijälogiikan, joka suoritetaan sen jälkeen, kun Anchor on vahvistanut
nämä tilit.
pub mod initialize;pub mod increment;pub use initialize::*;pub use increment::*;
initialize.rs
initialize.rs määrittelee laskuritilin luomiseen tarvittavat tilit ja
kirjoittaa sitten tilin ensimmäiset arvot. #[derive(Accounts)]-rakenne
käyttää Anchorin
tiliehtojen
määrityksiä ilmaisemaan, mitkä tilit vaaditaan ja miten uusi laskuritili
luodaan.
use anchor_lang::prelude::*;use crate::{constants::*, state::Counter};#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
Account Context
Initialize-rakenne määrittelee tilit, jotka on sisällytettävä, kun
käyttäjä kutsuu initialize-ohjetta. Anchor tarkistaa nämä tilit ennen
käsittelijän suorittamista.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
Payer Account
payer-tili maksaa laskuritilin luomisesta. Signer<'info>-tyyppi
tarkoittaa, että maksajan on allekirjoitettava transaktio, ja
#[account(mut)] tarkoittaa, että maksajatiliä voidaan muuttaa, koska
lamport-yksiköitä vähennetään siitä.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
Counter Account
counter-tili tallentaa state.rs:n Counter-datan. init käskee
Anchoria luomaan tämän tilin ennen käsittelijän suorittamista, ja
payer = payer kertoo Anchorille, mikä tili maksaa luomisesta.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
Tilin koko
space -rajoite kertoo Anchorille, kuinka paljon tilidataa varataan. Anchor
tallentaa ensin 8-tavuisen erottelijan, sitten Counter -kenttien
tarvitsemat tavut. Erottelija antaa Anchorille mahdollisuuden tunnistaa tämä
tili Counter -tiliksi ennen kuin tilin data deserialisoidaan.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
Laskurin osoite
seeds ja bump -rajoitteet määrittävät odotetun PDA-osoitteen
laskuritilille. Anchor varmistaa, että annettu counter -tili vastaa
kyseistä osoitetta. Malli käyttää PDA:ta, jotta käyttäjät voivat johtaa laskurin
osoitteen ohjelmatunnuksesta ja seed-arvosta, mikä tekee laskurin osoitteesta
deterministisen.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
System Program
system_program -tili vaaditaan, koska uuden tilin luominen käyttää System
Program -ohjelmaa.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
Käsittelijäfunktio
handle_initialize -funktio suoritetaan sen jälkeen, kun Anchor on
validoinut tilit Initialize:ssa. ctx -arvo antaa käsittelijälle
pääsyn näihin tarkistettuihin tileihin.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
Alkudata
Käsittelijä kirjoittaa ensimmäiset arvot uudelle laskuritilille. Laskuri alkaa
arvosta 0, ja maksajasta tulee valtuutettu, jolla on oikeus kasvattaa
laskuria myöhemmin.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
Siirtotilit
Tämä siirto-CPI on sisällytetty vain havainnollistamaan, kuinka CPI välittää
tilejä toiselle ohjelmalle. Transfer -rakenne luettelee System Program
-siirron käyttämät tilit.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
CPI-konteksti
CpiContext::new yhdistää kutsuttavan ohjelman ja sille välitetyt tilit.
Tämä on CPI:n perusrakenne: valitaan kutsuttava ohjelma, kerätään kyseisen
ohjelman odottamat tilit ja välitetään molemmat kutsulle. Tässä kutsuttava
ohjelma on System Program.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
Invoke Transfer
anchor_lang::system_program::transfer kutsuu System Program
-siirto-ohjetta. Tässä mallissa siirto on pieni esimerkki toisen ohjelman
kutsumisesta omasta ohjelmastasi. Jos siirto-CPI epäonnistuu, myös
initialize-ohje epäonnistuu.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
Log Message
msg! kirjoittaa viestin ohjelman lokeihin. Ok(()) ilmaisee, että
ohje palautui onnistuneesti.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}pub fn handle_initialize(ctx: Context<Initialize>) -> Result<()> {ctx.accounts.counter.count = 0;ctx.accounts.counter.authority = ctx.accounts.payer.key();let cpi_accounts = anchor_lang::system_program::Transfer {from: ctx.accounts.payer.to_account_info(),to: ctx.accounts.counter.to_account_info(),};let cpi_ctx = CpiContext::new(anchor_lang::system_program::ID, cpi_accounts);anchor_lang::system_program::transfer(cpi_ctx, HELLO_WORLD_LAMPORTS)?;msg!("Hello, world! Counter initialized");Ok(())}
increment.rs
increment.rs määrittelee tilit, joita tarvitaan olemassa olevan laskuritilin
päivittämiseen. Käsittelijä tarkistaa, että allekirjoittaja on tallennettu
auktoriteetti, tarkistaa, että laskuri ei ole saavuttanut määritettyä
MAX_COUNT-arvoa, ja kasvattaa sitten laskuria.
use anchor_lang::prelude::*;use crate::{constants::*, error::ErrorCode, state::Counter};#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Tilikonteksti
Increment -rakenne määrittelee tilit, jotka on sisällytettävä, kun
käyttäjä kutsuu increment -instruktiota. Anchor tarkistaa nämä tilit ennen
kuin käsittelijä suoritetaan.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}
Laskuritili
counter -tili tallentaa Counter -datan. mut -rajoite sallii
käsittelijän päivittää tallennetun laskurin, ja seeds sekä bump
-rajoitteet varmistavat laskurin PDA-osoitteen.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}
Auktoriteettiallekirjoittaja
authority -tilin on allekirjoitettava transaktio. Käsittelijä tarkistaa
myöhemmin, että tämä allekirjoittaja vastaa laskuritilille tallennettua
auktoriteettia.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}
Käsittelijäfunktio
Funktio handle_increment suoritetaan sen jälkeen, kun Anchor on validoinut
tilit Increment-rakenteessa. Arvo ctx antaa käsittelijälle pääsyn
näihin tarkistettuihin tileihin.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Valtuutustarkistus
Ensimmäinen tarkistus varmistaa, että allekirjoittajalla on oikeus päivittää
tätä laskuria. Jos allekirjoittajan osoite ei vastaa arvoa
counter.authority, ohje pysähtyy virheeseen ErrorCode::Unauthorized.
Tämä havainnollistaa sovellustason valtuutusta: ohjelma omistaa laskurin tiedot,
mutta se toteuttaa säännön siitä, kuka allekirjoittaja saa muuttaa kyseisiä
tietoja.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Maksimilukeman tarkistus
Toinen tarkistus estää laskuria ylittämästä arvoa MAX_COUNT. Jos laskuri
on jo saavuttanut rajan, ohje pysähtyy virheeseen
ErrorCode::CounterOverflow. Tämä raja on mallipohjaan lisätty
keinotekoinen sääntö, jotta näet, miten mukautetut virheet pysäyttävät ohjeen
ennen kuin tilitiedot muuttuvat.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Lukeman päivitys
Vasta kun molemmat tarkistukset läpäistään, käsittelijä päivittää tilitiedot. Tämä rivi lisää yhden tallennettuun laskurin arvoon.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Lokiviesti
msg! kirjoittaa päivitetyn lukeman ohjelmalokiin. Ok(()) ilmaisee,
että ohje palautui onnistuneesti.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}pub fn handle_increment(ctx: Context<Increment>) -> Result<()> {require_keys_eq!(ctx.accounts.counter.authority,ctx.accounts.authority.key(),ErrorCode::Unauthorized,);require!(ctx.accounts.counter.count < MAX_COUNT,ErrorCode::CounterOverflow,);ctx.accounts.counter.count += 1;msg!("Hello, world! Counter is now {}", ctx.accounts.counter.count);Ok(())}
Testitiedosto
programs/my-program/tests/test_initialize.rs on Rustin integraatiotesti. Se ei
käynnistä paikallista validator:ia. Sen sijaan se lataa käännetyn
.so-tiedoston LiteSVM:ään, rakentaa transaktioita, jotka kutsuvat ohjelmaa, ja
lukee laskuritilin jokaisen transaktion jälkeen. Testi rakentaa ohjeet
Solana-transaktiota varten määrittelemällä kutsuttavan ohjelman tunnuksen,
antamalla instruction data ja välittämällä vaaditut tilit.
#[derive(Accounts)]pub struct Initialize<'info> {#[account(mut)]pub payer: Signer<'info>,#[account(init,payer = payer,space = 8 + Counter::INIT_SPACE,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub system_program: Program<'info, System>,}
let instruction = Instruction::new_with_bytes(program_id,&my_program::instruction::Initialize {}.data(),my_program::accounts::Initialize {payer: payer.pubkey(),counter,system_program: system_program::ID,}.to_account_metas(None),);
Initialize-tilirakenne määrittelee tilit, joita
initialize-instruktio vaatii. Testi välittää samat tilit generoidulle
my_program::accounts::Initialize-apufunktiolle:
payervälitetäänpayer: payer.pubkey()-parametrina.countervälitetääncounter-parametrina.system_programvälitetäänsystem_program::ID-parametrina.
my_program::instruction::Initialize {}.data()
luo instruction data. Tänne koodattaisiin instruktion argumentit, mutta tämä
initialize-instruktio ei vaadi yhtään argumenttia.
#[derive(Accounts)]pub struct Increment<'info> {#[account(mut,seeds = [COUNTER_SEED],bump)]pub counter: Account<'info, Counter>,pub authority: Signer<'info>,}
let instruction = Instruction::new_with_bytes(program_id,&my_program::instruction::Increment {}.data(),my_program::accounts::Increment {counter,authority: payer.pubkey(),}.to_account_metas(None),);
Increment-tilirakenne määrittelee tilit, joita increment-instruktio
vaatii. Testi välittää samat tilit generoidulle
my_program::accounts::Increment-apufunktiolle:
countervälitetääncounter-parametrina.authorityvälitetäänauthority: payer.pubkey()-parametrina.
my_program::instruction::Increment {}.data()
luo instruction data. Tänne koodattaisiin instruktion argumentit, mutta tämä
increment-instruktio ei vaadi yhtään argumenttia.
use {anchor_lang::{prelude::Pubkey,solana_program::{instruction::Instruction, system_program},AccountDeserialize, InstructionData, ToAccountMetas,},litesvm::LiteSVM,solana_keypair::Keypair,solana_message::{Message, VersionedMessage},solana_signer::Signer,solana_transaction::versioned::VersionedTransaction,};#[test]fn test_initialize() {let program_id = my_program::id();let payer = Keypair::new();let counter = Pubkey::find_program_address(&[my_program::constants::COUNTER_SEED],&program_id,).0;let mut svm = LiteSVM::new();let bytes = include_bytes!(concat!(env!("CARGO_TARGET_TMPDIR"),"/../deploy/my_program.so"));svm.add_program(program_id, bytes).unwrap();svm.airdrop(&payer.pubkey(), 1_000_000_000).unwrap();let instruction = Instruction::new_with_bytes(program_id,&my_program::instruction::Initialize {}.data(),my_program::accounts::Initialize {payer: payer.pubkey(),counter,system_program: system_program::ID,}.to_account_metas(None),);let blockhash = svm.latest_blockhash();let msg = Message::new_with_blockhash(&[instruction], Some(&payer.pubkey()), &blockhash);let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[&payer]).unwrap();let res = svm.send_transaction(tx);assert!(res.is_ok());let counter_account = svm.get_account(&counter).unwrap();let mut data: &[u8] = &counter_account.data;let counter_state = my_program::state::Counter::try_deserialize(&mut data).unwrap();assert_eq!(counter_state.count, 0);assert_eq!(counter_state.authority, payer.pubkey());let instruction = Instruction::new_with_bytes(program_id,&my_program::instruction::Increment {}.data(),my_program::accounts::Increment {counter,authority: payer.pubkey(),}.to_account_metas(None),);let blockhash = svm.latest_blockhash();let msg = Message::new_with_blockhash(&[instruction], Some(&payer.pubkey()), &blockhash);let tx = VersionedTransaction::try_new(VersionedMessage::Legacy(msg), &[&payer]).unwrap();let res = svm.send_transaction(tx);assert!(res.is_ok());let counter_account = svm.get_account(&counter).unwrap();let mut data: &[u8] = &counter_account.data;let counter_state = my_program::state::Counter::try_deserialize(&mut data).unwrap();assert_eq!(counter_state.count, 1);assert_eq!(counter_state.authority, payer.pubkey());}
Projektin konfigurointi
Projektin juuritiedostot kertovat Anchorille ja Cargolle, miten ohjelma rakennetaan, testataan ja otetaan käyttöön. Täydellinen viite löytyy Anchorin dokumentaatiosta: Anchor.toml-konfigurointi ja Anchor CLI.
skip_local_validator = true[toolchain][features]resolution = trueskip-lint = false[programs.localnet]my_program = "82sFkffP9wxwpyfZyeaKHH2chQoJPUGsJZSPi9mrUuXd"[provider]cluster = "localnet"wallet = "~/.config/solana/id.json"[scripts]test = "cargo test"[hooks]
Is this page helpful?