Solana基础 - 如何创建程序派生地址
程序派生地址只是程序拥有的一个帐户,但没有私钥。相反,它的签名是通过一组种子和一个碰撞(一个
随机数,确保它“偏离曲线”,即不是有效的公钥)获得的。“生成”程序地址
不同于“创建”它。可以使用生成 PDA Pubkey::find_program_address。创建 PDA 本质上意味着用空间初始化地址并将状态设置为它。可以在我们的程序之外创建一个普通的密钥对帐户,然后输入以初始化它的状态。不幸的是,对于 PDA,它必须在链上创建,因为它不能代表自己签名。因此,我们使用invoke_signed传递 PDA 的种子,以及资金账户的签名,从而创建 PDA 账户。
use borsh::{BorshDeserialize, BorshSerialize};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint,
entrypoint::ProgramResult,
program::invoke_signed,
program_error::ProgramError,
pubkey::Pubkey,
rent::Rent,
system_instruction,
sysvar::Sysvar,
};
entrypoint!(process_instruction);
#[derive(BorshSerialize, BorshDeserialize, Debug)]
pub struct HelloState {
is_initialized: bool,
}
// Accounts required
/// 1. [signer, writable] Funding account
/// 2. [writable] PDA account
/// 3. [] System Program
pub fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
const ACCOUNT_DATA_LEN: usize = 1;
let accounts_iter = &mut accounts.iter();
// Getting required accounts
let funding_account = next_account_info(accounts_iter)?;
let pda_account = next_account_info(accounts_iter)?;
let system_program = next_account_info(accounts_iter)?;
// Getting PDA Bump from instruction data
let (pda_bump, _) = instruction_data
.split_first()
.ok_or(ProgramError::InvalidInstructionData)?;
// Checking if passed PDA and expected PDA are equal
let signers_seeds: &[&[u8]; 3] = &[
b"customaddress",
&funding_account.key.to_bytes(),
&[*pda_bump],
];
let pda = Pubkey::create_program_address(signers_seeds, program_id)?;
if pda.ne(&pda_account.key) {
return Err(ProgramError::InvalidAccountData);
}
// Assessing required lamports and creating transaction instruction
let lamports_required = Rent::get()?.minimum_balance(ACCOUNT_DATA_LEN);
let create_pda_account_ix = system_instruction::create_account(
&funding_account.key,
&pda_account.key,
lamports_required,
ACCOUNT_DATA_LEN.try_into().unwrap(),
&program_id,
);
// Invoking the instruction but with PDAs as additional signer
invoke_signed(
&create_pda_account_ix,
&[
funding_account.clone(),
pda_account.clone(),
system_program.clone(),
],
&[signers_seeds],
)?;
// Setting state for PDA
let mut pda_account_state = HelloState::try_from_slice(&pda_account.data.borrow())?;
pda_account_state.is_initialized = true;
pda_account_state.serialize(&mut &mut pda_account.data.borrow_mut()[..])?;
Ok(())
}可以通过客户端发送所需的帐户,如下所示
import {
clusterApiUrl,
Connection,
Keypair,
LAMPORTS_PER_SOL,
PublicKey,
sendAndConfirmTransaction,
SystemProgram,
Transaction,
TransactionInstruction,
} from "@solana/web3.js";
const PAYER_KEYPAIR = Keypair.generate();
(async () => {
const connection = new Connection(clusterApiUrl("devnet"), "confirmed");
const latestBlockHash = await connection.getLatestBlockhash();
const programId = new PublicKey(
"6eW5nnSosr2LpkUGCdznsjRGDhVb26tLmiM1P8RV1QQp",
);
// Airdrop to Payer
await connection.confirmTransaction(
{
blockhash: latestBlockHash.blockhash,
lastValidBlockHeight: latestBlockHash.lastValidBlockHeight,
signature: await connection.requestAirdrop(
PAYER_KEYPAIR.publicKey,
LAMPORTS_PER_SOL,
),
},
"confirmed",
);
const [pda, bump] = await PublicKey.findProgramAddress(
[Buffer.from("customaddress"), PAYER_KEYPAIR.publicKey.toBuffer()],
programId,
);
console.log(`PDA Pubkey: ${pda.toString()}`);
const createPDAIx = new TransactionInstruction({
programId: programId,
data: Buffer.from(Uint8Array.of(bump)),
keys: [
{
isSigner: true,
isWritable: true,
pubkey: PAYER_KEYPAIR.publicKey,
},
{
isSigner: false,
isWritable: true,
pubkey: pda,
},
{
isSigner: false,
isWritable: false,
pubkey: SystemProgram.programId,
},
],
});
const transaction = new Transaction();
transaction.add(createPDAIx);
const txHash = await sendAndConfirmTransaction(connection, transaction, [
PAYER_KEYPAIR,
]);
console.log(`Created PDA successfully. Tx Hash: ${txHash}`);
})();https://solana.com/zh/developers/cookbook/programs/create-pda