<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>kl (kl)</title>
    <link>https://soldev.cn/kl</link>
    <description/>
    <language>en-us</language>
    <item>
      <title>使用 rust lib 开发 solana 移动客户端</title>
      <description>&lt;p&gt;最近在研究 RUST FFI（Foreign Function Interface），刚好又比较熟悉 iOS 开发。
&lt;br&gt;
&lt;br&gt;
为什么要用 rust：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;在 solana 生态中 rust 是第一语言，使用同一种语言就可以开发公链、program(智能合约)、client。&lt;/li&gt;
&lt;li&gt;移动应用开发中使用 rust 可以无缝衔接，使用 solana 官方的工具库。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;使用 rust lib 开发 solana 移动客户端，主要分为两部分：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rust lib 开发&lt;/li&gt;
&lt;li&gt;集成到 App（以 iOS 为例，Android 同样可以使用 rust lib）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;代码已上传到 github： &lt;a href="https://github.com/kouliang/solana-ios.git" rel="nofollow" target="_blank"&gt;https://github.com/kouliang/solana-ios.git&lt;/a&gt;&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="rust lib 开发"&gt;rust lib 开发&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;创建 rust lib 项目
cargo 命令： &lt;code&gt;cargo new solana-core --lib&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;添加依赖库&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;solana-sdk  solana 开发中最基础的 SDK&lt;/li&gt;
&lt;li&gt;solana-client 与 Solana 节点交互&lt;/li&gt;
&lt;li&gt;rust-client  我自己封装的一个工具库&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[lib]
name = "solana_core"
crate-type = ["staticlib"]

[dependencies]
libc = "0.2.169"
solana-client = "2.1.7"
solana-sdk = "2.1.7"
rust-client = { git = "https://github.com/kouliang/solana-client.git" }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3.方法实现
&lt;br&gt;
要遵循 rust 中 FFI 标准：  &lt;a href="https://doc.rust-lang.org/nomicon/ffi.html#rust-side" rel="nofollow" target="_blank"&gt;https://doc.rust-lang.org/nomicon/ffi.html#rust-side&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;test_key_pair 函数的功能是：接收外部传递过来的字符串，并尝试转换为 keypair 类型。如果转换成功则返回此 keypair 对应的账户地址，如果失败返回错误信息。&lt;/p&gt;

&lt;p&gt;#[no_mangle] 是必要的，告诉 Rust 编译器：不要乱改函数的名称。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#[no_mangle]
pub extern "C" fn test_key_pair(content: *const libc::c_char) -&amp;gt; *const libc::c_char {
    let c_str = unsafe { std::ffi::CStr::from_ptr(content) };
    let content = c_str.to_str().unwrap();

    let mut reader = Cursor::new(content.as_bytes());
    let keypair = keypair::read_keypair(&amp;amp;mut reader);

    match keypair {
        Ok(keypair) =&amp;gt; {
            let pubkey = keypair.pubkey();
            let pubkey = pubkey.to_string();
            return std::ffi::CString::new(format!("Address: {:?}", pubkey)).unwrap().into_raw();
        },
        Err(e) =&amp;gt; {
            return std::ffi::CString::new(format!("Error: {:?}", e)).unwrap().into_raw();
        }
    }
}

#[no_mangle]
pub extern "C" fn test_rpc(content: *const libc::c_char) -&amp;gt; *const libc::c_char {
    let c_str = unsafe { std::ffi::CStr::from_ptr(content) };
    let content = c_str.to_str().unwrap();

    let lowercase = content.to_lowercase();

    let url = match lowercase.as_str() {
        "localhost" =&amp;gt; "http://localhost:8899".to_string(),
        "testnet" =&amp;gt; "https://api.testnet.solana.com".to_string(),
        "devnet" =&amp;gt; "https://api.devnet.solana.com".to_string(),
        "mainnet" =&amp;gt; "https://api.mainnet-beta.solana.com".to_string(),
        other =&amp;gt; other.to_string()
    };

    let client = RpcClient::new_with_commitment(url, CommitmentConfig::confirmed());

    let block_height = client.get_block_height();
    match block_height {
        Ok(block_height) =&amp;gt; {
            return std::ffi::CString::new(format!("block_height: {:?}", block_height)).unwrap().into_raw();
        },
        Err(e) =&amp;gt; {
            return std::ffi::CString::new(format!("Error: {:?}", e)).unwrap().into_raw();
        }

    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4.交叉编译&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;查看已安装和可用 targets 列表： &lt;code&gt;rustup target list&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;添加工具链： &lt;code&gt;rustup target add &amp;lt;your_target&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;编译： &lt;code&gt;cargo build --target=&amp;lt;your_target&amp;gt; --release&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;target triple 格式是 {arch}-{vendor}-{sys}-{abi}
  分别代表：编译程序的主机系统、供应商、操作系统、ABI 接口，最后 abi 有时候可以省略&lt;/p&gt;

&lt;p&gt;一定要根据开发环境选择对应的 target 才能编译出可用的库。我本地是 Appel M2 要在 模拟器上执行，所以选择的是 aarch64-apple-ios-sim&lt;/p&gt;

&lt;p&gt;最终的编译产物是 target/aarch64-apple-ios-sim/release/libsolana_core.a&lt;/p&gt;

&lt;hr&gt;
&lt;h3 id="集成到 App"&gt;集成到 App&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;将 libsolana_core.a 添加到 iOS 项目中。具体操作不赘述，可以搜索 iOS 添加静态库。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;创建桥接头文件&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;在本项目中为 solana-Bridging-Header.h&lt;/li&gt;
&lt;li&gt;在 build settings 中链接该头文件：
targets -&amp;gt; build settings -&amp;gt; swift compiler - general -&amp;gt; Objective-C Bridging Header -&amp;gt; YourProject-Bridging-Header.h&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;#ifndef Bridging_Header_h
#define Bridging_Header_h

#include &amp;lt;stdint.h&amp;gt;

const char* test_rpc(const char* content);
const char* test_key_pair(const char* content);

const char* save_config(const char* rpc, const char* keypair);

const char* balance(const char* content);
const char* transfer_to(const char* address, const char* amount);

#endif /* Bridging_Header_h */
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;3.swift 中直接使用 libsolana_core 中提供的方法。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@IBAction func balanceRequest(_ sender: Any) {
    let addr = balanceTextField.text ?? ""

    let result = balance(addr)!
    let resultstr = String(cString: result)
    print(resultstr)

    balanceLable.text = resultstr
}
@IBAction func transferRequest(_ sender: Any) {
    let addr = address.text ?? ""
    let am = amount.text ?? ""

    let result = transfer_to(addr, am)!
    let resultstr = String(cString: result)
    print(resultstr)

    transferLabel.text = resultstr
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4.solana-ios App 使用说明&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;首先要点击 Config 进入 Config 页，对 节点 rpc_url 和 payer 账户的 keypair 进行配置。&lt;/li&gt;
&lt;li&gt;输入完成后点击 verify 进行验证。rpc_url 配置正确下方会显示 "block_height: ..."，keypair 配置正确下方会显示"Address: ..."&lt;/li&gt;
&lt;li&gt;两项配置都验证通过后，点击右上角 "Save Config" 按钮会进行全局保存。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src="/uploads/photo/kl/00047fd0-7d9b-413b-84c1-22d6fdf97ab6.png!large" title="" alt=""&gt;
&lt;img src="/uploads/photo/kl/c9a4c784-e147-4e5e-b6cf-4599a02f94bc.png!large" title="" alt=""&gt;
&lt;img src="/uploads/photo/kl/5ca6b1f8-2f99-41cd-b150-acfaf7ab0abe.png!large" title="" alt=""&gt;&lt;/p&gt;</description>
      <author>kl</author>
      <pubDate>Sun, 29 Dec 2024 14:18:37 +0800</pubDate>
      <link>https://soldev.cn/topics/130</link>
      <guid>https://soldev.cn/topics/130</guid>
    </item>
    <item>
      <title>使用 solana program deploy 部署程序时一直失败问题的解决办法</title>
      <description>&lt;h3 id="问题描述"&gt;问题描述&lt;/h3&gt;
&lt;p&gt;使用 solana program deploy  在 testnet/devnet 部署程序时会失败（部署到 localhost 没问题），并报如下错误&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;thread 'main' panicked at cli/src/program.rs:2809:26:
Should return a valid tpu client: PubsubError(ConnectionError(Io(Os { code: 60, kind: TimedOut, message: "Operation timed out" })))
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id="解决办法"&gt;解决办法&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;CLI 选用 v1.18.18 或以上。&lt;/p&gt;

&lt;p&gt;可供安装的版本列表：  &lt;a href="https://github.com/solana-labs/solana/releases" rel="nofollow" target="_blank"&gt;https://github.com/solana-labs/solana/releases&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CLI 安装命令： &lt;code&gt;sh -c "$(curl -sSfL https://release.solana.com/v1.18.18/install)"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;查看当前安装的 CLI 版本： &lt;code&gt;solana --version&lt;/code&gt;
&lt;br&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;选择一个可靠的的 RPC node&lt;/p&gt;

&lt;p&gt;节点选择可以参考： &lt;a href="https://soldev.cn/topics/14" rel="nofollow" target="_blank"&gt;https://soldev.cn/topics/14&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;设置 solana CLI 默认的 RPC_URL： &lt;code&gt;solana config set --url &amp;lt;RPC_URL&amp;gt;&lt;/code&gt;
&lt;br&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;使用 --with-compute-unit-price 参数，设置一个合适的优先费。&lt;/p&gt;

&lt;p&gt;优先费的数值可以参考： &lt;a href="https://www.quicknode.com/gas-tracker/solana" rel="nofollow" target="_blank"&gt;https://www.quicknode.com/gas-tracker/solana&lt;/a&gt;
&lt;br&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;部署程序的完整命令为 (如果在步骤 2 中已经设置了默认的 RPC_URL，--url 可以省略)。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;solana program deploy &amp;lt;PROGRAM_FILEPATH&amp;gt; --with-compute-unit-price &amp;lt;compute-unit-price&amp;gt; --url &amp;lt;RPC_URL&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="扩展 - prioritization-fees"&gt;扩展 - prioritization-fees&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://solana.com/docs/core/fees#prioritization-fees" rel="nofollow" target="_blank"&gt;https://solana.com/docs/core/fees#prioritization-fees&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;solana 中每笔交易除了固定的签名费用之外，还可以设置一项可选费用"Prioritization fee"称为"优先费"。可以提高交易相对于其他交易的优先级，从而缩短执行时间。&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"优先费" 通过 ComputeUnitLimit * ComputeUnitPrice 来计算。&lt;/li&gt;
&lt;li&gt;在交易中使用 SetComputeUnitLimit 和 SetComputeUnitPrice 指令来设置 ComputeUnitLimit/ComputeUnitPrice 的值。
rust 或者 &lt;a href="/solana" class="user-mention" title="@solana"&gt;&lt;i&gt;@&lt;/i&gt;solana&lt;/a&gt;/web3.js 库中有快速生成这些指令的方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//rust solana-sdk crate
let instruction = ComputeBudgetInstruction::set_compute_unit_limit(300_000);
let instruction = ComputeBudgetInstruction::set_compute_unit_price(1);

//@solana/web3.js
const instruction = ComputeBudgetProgram.setComputeUnitLimit({
    units: 300_000,
});
const instruction = ComputeBudgetProgram.setComputeUnitPrice({
    microLamports: 1,
});
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;get_recent_prioritization_fees RPC 方法能获取节点最近处理的区块中最近支付的优先级费用列表。&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>kl</author>
      <pubDate>Fri, 08 Nov 2024 11:34:00 +0800</pubDate>
      <link>https://soldev.cn/topics/95</link>
      <guid>https://soldev.cn/topics/95</guid>
    </item>
  </channel>
</rss>
