今天来开始学习solana
从上周四21号开始到今天已经学了5天了,
但是真的是一点都没学会,脑子里完全是一片混乱
从来没有学一个东西学得这么痛苦
所以我准备静下心来好好学一学,不要浮躁
之前觉得evm的开发体验真的是非常垃圾,
搞了solana之后才发现
evm真的是天堂
我们先来看一下官方的hello world示例项目
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(())
}
首先是use,use,use
都是导包
然后是struct
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct GreetingAccount {
/// number of greetings
pub counter: u32,
}
derive相当于是继承,所以这个GreetingAccount继承了
BorshSerialize, BorshDeserialize, Debug
然后GreetingAcount这个结构体里面有一个属性
pub就是public
counter是变量名
这个属性变量是u32的
i32是有符号32位整型,,,u32是无符号32位整型
还有比如i8和u8就是有符号和无符号的8位整型
然后是
// Declare and export the program's entrypoint
entrypoint!(process_instruction);
entrypoint!()
就是我们的程序入口
然后我们看process_instruction方法
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(())
}
这里的三个参数
1.program_id 程序id(程序地址)
2.accounts 账户
3.instruction_data 指令数据
这三个参数对process_instruction方法来说是固定的
然后看代码
首先是把accounts这个账户数组用iter()这个方法遍历了一下
拿到account对象
然后如果account的owner不是program_id的话就报错
然后让greeting_account里面的counter加1
说实话这个例子很弱智
没有体现很多的特点和功能
看了一遍这个例子也学不到任何东西
我感觉很无语
Accounts
现在来说说Accounts
If the program needs to store state between transactions, it does so using accounts. Accounts are similar to files in operating systems such as Linux in that they may hold arbitrary data that persists beyond the lifetime of a program. Also like a file, an account includes metadata that tells the runtime who is allowed to access the data and how.
Unlike a file, the account includes metadata for the lifetime of the file. That lifetime is expressed by a number of fractional native tokens called lamports. Accounts are held in validator memory and pay "rent" to stay there. Each validator periodically scans all accounts and collects rent. Any account that drops to zero lamports is purged. Accounts can also be marked rent-exempt if they contain a sufficient number of lamports.
In the same way that a Linux user uses a path to look up a file, a Solana client uses an address to look up an account. The address is a 256-bit public key.
现在我来部署一下这个helloworld程序
BTo3KCTWZ7S1FSQgKNcr32RAcMhgLGF7toxqAw35BnBc
然后来调用一下
1.
我们先通过公钥来创建这个程序的公钥
const payer = web3.Keypair.fromSeed(derivedSeed);
console.log(payer.publicKey.toBase58())
let connection = new web3.Connection(web3.clusterApiUrl('devnet'));
const programId = new PublicKey("BTo3KCTWZ7S1FSQgKNcr32RAcMhgLGF7toxqAw35BnBc");
console.log(programId.toBase58());
const greetedPubkey = await PublicKey.createWithSeed(
payer.publicKey,
'hello',
programId,
);
这里的seed,可以是任意的东西
所以我们可以通过不同的seed创建出无数个不同的公钥
2.
我们要计算一下space
class GreetingAccount {
counter = 0;
constructor(fields) {
if (fields) {
this.counter = fields.counter;
}
}
}
const GreetingSchema = new Map([
[GreetingAccount, {kind: 'struct', fields: [['counter', 'u32']]}],
]);
const GREETING_SIZE = borsh.serialize(
GreetingSchema,
new GreetingAccount(),
).length;
console.log(GREETING_SIZE);
3.
然后我们用公钥来创建账户
同理,不同的seed可以创建出无数不同的公钥
然后创建出无数不同的账户
const accountInfo = await connection.getAccountInfo(greetedPubkey);
console.log(accountInfo)
if (accountInfo == null) {
const lamports = await connection.getMinimumBalanceForRentExemption(GREETING_SIZE);
const transaction = new Transaction().add(
SystemProgram.createAccountWithSeed({
fromPubkey: payer.publicKey,
basePubkey: payer.publicKey,
seed: 'hello',
newAccountPubkey: greetedPubkey,
lamports,
space: GREETING_SIZE,
programId,
}),
);
const result = await sendAndConfirmTransaction(connection, transaction, [payer]);
console.log(result)
}
4.
然后我们发个交易
const instruction = new TransactionInstruction({ keys: [{pubkey: greetedPubkey, isSigner: false, isWritable: true}], programId, data: Buffer.alloc(0), }); const result = await sendAndConfirmTransaction( connection, new Transaction().add(instruction), [payer], ); console.log(result)
5.
然后查询一下
const accountInfo = await connection.getAccountInfo(greetedPubkey);
console.log(accountInfo)
if (accountInfo === null) {
throw 'Error: cannot find the greeted account';
}
const GreetingSchema = new Map([
[GreetingAccount, {kind: 'struct', fields: [['counter', 'u32']]}],
]);
const greeting = borsh.deserialize(
GreetingSchema,
GreetingAccount,
accountInfo.data,
);
console.log(greeting)