SOL rs/ts 代码 nonce-account 签名
初始化 nonce account
w@w:~/.config/solana$ solana-keygen new -o nonce-account.json --no-bip39-passphrase
w@w:~/.config/solana$ p solana create-nonce-account nonce-account.json 0.0015
ProxyChains-3.1 (http://proxychains.sf.net)
Signature: 3L2J5xYt8z8WpNw6ajJXMq7VbTS4ANYCv7V5NqcabDQn6owsW57kzNzs9NxQLZRfgCA4o8FGBTdmcb9xhmPExfCA
w@w:~/.config/solana$ p solana nonce nonce-account.json
ProxyChains-3.1 (http://proxychains.sf.net)
C87QyTSxGy8CTEdM3PX2aNJLDsNQQiEZ7yaRudApuuYa
solana create-nonce-account
对应的 ts API 是 SystemProgram.nonceInitialize
对应的 rs API 是 solana_sdk::system_instruction::create_nonce_account
nonce account 的好处很多,除了类似 eth nonce 自增防重入和双花攻击,例如一些耗时的操作,例如有100人多签,可能2分钟内lastestBlockHash就失效了
创建 nonce account 的 tx 会在 solflare 钱包中标记为 App Interaction
ts 使用 nonce account
import {
Connection,
PublicKey,
Keypair,
NonceAccount,
Transaction,
sendAndConfirmTransaction,
TransactionInstruction,
SystemProgram
} from '@solana/web3.js';
import * as fs from 'fs';
import * as dotenv from 'dotenv';
dotenv.config();
function readKeypair(file: string): Keypair {
const keypairPath = `${process.env.HOME}/.config/solana/${file}`;
const keypairData = JSON.parse(fs.readFileSync(keypairPath, 'utf8'));
return Keypair.fromSecretKey(new Uint8Array(keypairData));
}
(async () => {
const connection = new Connection('https://api.devnet.solana.com', 'confirmed');
const payer = readKeypair("id.json");
const nonceAccountKey = readKeypair("nonce-account.json")
const accountInfo = await connection.getAccountInfo(nonceAccountKey.publicKey);
if (accountInfo === null) throw new Error("")
const nonceAccount = NonceAccount.fromAccountData(accountInfo.data);
const programId = new PublicKey(process.env.PROGRAM_ID as string);
// make a nonce advance instruction
const advanceIX = SystemProgram.nonceAdvance({
authorizedPubkey: payer.publicKey,
noncePubkey: nonceAccountKey.publicKey,
});
const ix = new TransactionInstruction({
// our program only logs hello world and doesn't need any accounts.
keys: [{pubkey: payer.publicKey, isSigner: true, isWritable: true}],
programId,
data: Buffer.alloc(0)
});
const tx = new Transaction();
tx.add(advanceIX);
tx.add(ix);
tx.recentBlockhash = nonceAccount.nonce;
tx.feePayer = payer.publicKey;
const signature = await sendAndConfirmTransaction(
connection,
tx,
[payer]
);
console.log('signature/txhash:', signature);
})();
rs 使用 nonce account
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
commitment_config::CommitmentConfig,
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
signature::Signer,
transaction::Transaction,
};
fn main() {
dotenv::dotenv().unwrap();
let client = RpcClient::new_with_commitment(
"https://api.devnet.solana.com".to_string(),
CommitmentConfig::confirmed(),
);
let payer = solana_client_example::keypair();
let nonce_account = solana_client_example::nonce_account();
let nonce_account_data = client.get_account(&nonce_account.pubkey()).unwrap();
let nonce_data = solana_rpc_client_nonce_utils::data_from_account(&nonce_account_data).unwrap();
let block_hash = nonce_data.blockhash();
// below code get wrong nonce_account block_hash
// let nonce_state: nonce::State = bincode::deserialize(&nonce_account_data).unwrap();
// let block_hash = match nonce_state {
// nonce::State::Initialized(data) => data.blockhash(),
// nonce::State::Uninitialized => {
// unimplemented!()
// }
// };
let program_id: Pubkey = std::env::var("PROGRAM_ID").unwrap().parse().unwrap();
let advance_ix = solana_sdk::system_instruction::advance_nonce_account(
&nonce_account.pubkey(),
&payer.pubkey(),
);
let instruction = Instruction {
program_id,
accounts: vec![AccountMeta::new(payer.pubkey(), true)],
data: Vec::new(),
};
let transaction = Transaction::new_signed_with_payer(
&[advance_ix, instruction],
Some(&payer.pubkey()),
&[&payer],
block_hash,
);
match client.send_and_confirm_transaction(&transaction) {
Ok(signature) => println!("Transaction sent with signature: {}", signature),
Err(err) => eprintln!("Error sending transaction: {:?}", err),
}
}
注意尝试用 bincode::deserialize 解析 nonceAccount 会得到错误的 hash