Solana ts/rs 代码 nonce-account 签名

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

全部评论(0)
推荐文章
Pectra 升级的核心:EIP-7702的原理分析和实操
来 The Web3, 学习史上最全面的区块链教程,挑战高薪
TON钱包签名、私钥导入与发送交易
Rust 实战:构建高效的异步 P2P 网络节点
深入理解solana-keygen
solana账户总结
以太坊POS工作原理详解:Epoch、Slot 与信标区块
以太坊发币 - 超简单发行 ERC-20 代币并上线到 holesky 上
NFT发行 - 超简单发行 NFT 到 holesky 上(包含 ERC165、ERC721Receiver 的含义)
wrapped SOL 与 naive SOL 互相转换
The Web3 社区--区块链运维课程大纲
更安全的签名 - EIP712 结构化签名
带你手搓一个预言机 - 极简版的 ChainLink VRF 随机数生成
The Web3 区块链钱包教程大纲
以太坊代理模式的天花板 - 信标代理
DeFi 项目的基石 - ERC4626 代币金库协议的实现
SOL合约部署调用与销毁
Uniswap价格批量查询与ws订阅行情
智能合约的身份保证 - 数字签名
Solana USDC 转账交易的细节
ERC20授权的更优方案 - ERC20Permit 签名授权
The Web3 社区 Move 共学招募
abigen 工具和 sol! 宏生成智能合约 ABI 数据结构
The Web3 社区第三期区块链技术培训课程火热招生中--四个月高强度挑战,成为区块链技术高手
MPC托管系统工作原理
事件监听 - 合约事件监听的方案汇总
监听合约事件 -- 手把手带你在线、离线部署 The Graph
代币集大成者 - 手搓一个ERC1155合约并上线 holesky
如何成为一名专业的 Web3 产品经理 ——Web3 产品经理课程招募!
Solana ts/rs 代码 nonce-account 签名