program-rust/src/lib.rs是链上程序的核心代码,如代码清单1-1所示,实现了将程序被调用次数存储在链上账户中。
第1行代码将borsh::BorshDeserialize和borsh::BorshSerialize引入本地作用域,用于序列化和反序列化数据。第2~9行代码将SolanaRustSDK的模块引入本地作用域,使用Rust编写程序都需要这个SDK。
第13~16行代码定义了GreetingAccount结构体作为存储在账户中的状态类型,里面有一个u32类型的字段counter,用于记录程序被有效调用的次数。
第19行代码entrypoint声明了process_instruction函数是程序入口,每个程序都有一个唯一的入口。第22~26行代码是process_instruction函数签名,它要接收3个参数:
process_instruction函数的返回值类型是ProgramResult,ProgramResult类型的定义如下所示。
pubtypeProgramResult=Result<(),ProgramError>;
当程序的逻辑执行成功时返回Ok(()),否则将ProgramError错误返回。ProgramError是自定义错误的枚举类型,其中包含程序可能失败的各种原因。
第27行代码使用msg!宏将字符串输出到日志中,方便观察业务的执行逻辑和调试信息。第30行代码通过iter方法将账户列表转换为迭代器,以安全的方式获取账户地址。第33行代码使用了?操作符,如果迭代器中有账户地址,会将账户地址与变量account绑定。如果迭代器中没有账户地址,?操作符会让程序执行失败。
第36~39行代码判断存储状态的账户所有者是否是当前程序。只有账户所有者才能修改数据,否则输出日志并返回。
第42~44行代码先对账户中的数据进行反序列化操作,再将counter加一,最后将其序列化后存储到账户中。
代码清单1-1helloworld链上程序
useborsh::{BorshDeserialize,BorshSerialize};
usesolana_program::{
account_info::{next_account_info,AccountInfo},
entrypoint,
entrypoint::ProgramResult,
msg,
program_error::ProgramError,
pubkey::Pubkey,
};
///Definethetypeofstatestoredinaccounts
#[derive(BorshSerialize,BorshDeserialize,Debug)]
pubstructGreetingAccount{
///numberofgreetings
pubcounter:u32,
}
//Declareandexporttheprogramsentrypoint
entrypoint!(process_instruction);
//Programentrypointsimplementation
pubfnprocess_instruction(
program_id:&Pubkey,//Publickeyoftheaccountthehelloworldprogramwasloadedinto
accounts:&[AccountInfo],//Theaccounttosayhelloto
_instruction_data:&[u8],//Ignored,allhelloworldinstructionsarehellos
)->ProgramResult{
msg!("HelloWorldRustprogramentrypoint");
//Iteratingaccountsissaferthenindexing
letaccounts_iter=&mutaccounts.iter();
//Gettheaccounttosayhelloto
letaccount=next_account_info(accounts_iter)?;
//Theaccountmustbeownedbytheprograminordertomodifyitsdata
ifaccount.owner!=program_id{
msg!("Greetedaccountdoesnothavethecorrectprogramid");
returnErr(ProgramError::IncorrectProgramId);
}
//Incrementandstorethenumberoftimestheaccounthasbeengreeted
letmutgreeting_account=GreetingAccount::try_from_slice(&account.data.borrow())?;
greeting_account.counter+=1;
greeting_account.serialize(&mut&mutaccount.data.borrow_mut()[..])?;
msg!("Greeted{}time(s)!",greeting_account.counter);
Ok(())
}
要想测试链上程序,我们必须通过SolanaJSONRPCAPI去和链上程序进行交互。example-helloworld项目提供的客户端用Typescript编写,使用了web3.js库这个SolanaJavaScriptSDK。
在client目录下,客户端执行的入口是main.ts文件,它按特定的顺序执行任务,每个任务的业务逻辑代码在hello_world.ts文件。
首先,客户端调用establishConnection函数与集群建立连接。
exportasyncfunctionestablishConnection():Promise<void>{
constrpcUrl=awaitgetRpcUrl();
connection=newConnection(rpcUrl,confirmed);
constversion=awaitconnection.getVersion();
console.log(Connectiontoclusterestablished:,rpcUrl,version);
}
接着,客户端调用establishPayer函数来确保有一个有支付能力的账户。
exportasyncfunctionestablishPayer():Promise<void>{
letfees=0;
if(!payer){
const{feeCalculator}=awaitconnection.getRecentBlockhash();
//Calculatethecosttofundthegreeteraccount
fees+=awaitconnection.getMinimumBalanceForRentExemption(GREETING_SIZE);
//Calculatethecostofsendingtransactions
fees+=feeCalculator.lamportsPerSignature*100;//wag
try{
//Getpayerfromcliconfig
payer=awaitgetPayer();
}catch(err){
//Fundanewpayerviaairdrop
payer=awaitnewAccountWithLamports(connection,fees);
}
}
constlamports=awaitconnection.getBalance(payer.publicKey);
if(lamports<fees){
//Thisshouldonlyhappenwhenusingcliconfigkeypair
constsig=awaitconnection.requestAirdrop(
payer.publicKey,
fees-lamports,
);
awaitconnection.confirmTransaction(sig);
}
console.log(
Usingaccount,
payer.publicKey.toBase58(),
containing,
lamports/LAMPORTS_PER_SOL,
SOLtopayforfees,
);
}
然后,客户端调用checkProgram函数从src/program-rust/target/deploy/helloworld-keypair.json中加载已部署程序的密钥对(此操作前需先构建链上程序,详见1.3.2节),并使用密钥对的公钥来获取程序账户。如果程序不存在,客户端会报错并停止执行。如果程序存在,将创建一个新账户来存储状态,并以该程序作为新账户所有者。这里新账户存储的状态,就是程序被调用的次数。
exportasyncfunctioncheckProgram():Promise<void>{
//Readprogramidfromkeypairfile
try{
constprogramKeypair=awaitcreateKeypairFromFile(PROGRAM_KEYPAIR_PATH);
programId=programKeypair.publicKey;
}catch(err){
consterrMsg=(errasError).message;
thrownewError(
`Failedtoreadprogramkeypairat${PROGRAM_KEYPAIR_PATH}duetoerror:${errMsg}.`,
);
}
//Checkiftheprogramhasbeendeployed
constprogramInfo=awaitconnection.getAccountInfo(programId);
if(programInfo===null){
if(fs.existsSync(PROGRAM_SO_PATH)){
thrownewError(
Programneedstobedeployedwith`solanaprogramdeploydist/program/helloworld.so`,
);
}else{
thrownewError(Programneedstobebuiltanddeployed);
}
}elseif(!programInfo.executable){
thrownewError(`Programisnotexecutable`);
}
console.log(`Usingprogram${programId.toBase58()}`);
//Derivetheaddress(publickey)ofagreetingaccountfromtheprogramsothatitseasytofindlater.
constGREETING_SEED=hello;
greetedPubkey=awaitPublicKey.createWithSeed(
payer.publicKey,
GREETING_SEED,
programId,
);
//Checkifthegreetingaccounthasalreadybeencreated
constgreetedAccount=awaitconnection.getAccountInfo(greetedPubkey);
if(greetedAccount===null){
console.log(
Creatingaccount,
greetedPubkey.toBase58(),
tosayhelloto,
);
constlamports=awaitconnection.getMinimumBalanceForRentExemption(
GREETING_SIZE,
以上就是Solana开发学习笔记(一)——从Hello World出发的全部内容,望能这篇Solana开发学习笔记(一)——从Hello World出发可以帮助您解决问题,能够解决大家的实际问题是非常好学习网一直努力的方向和目标。