您正在查看: Surou 发布的文章

Solana - JS/TS 客户端

Anchor 提供了一个 Typescript 客户端库(@coral-xyz/anchor),简化了使用 JavaScript 或 TypeScript 从客户端与 Solana 程序交互的过程。

客户端程序

Program 要使用客户端库,首先使用Anchor 生成的IDL 文件创建一个实例 。
创建 的实例Program需要程序的 IDL 和 AnchorProvider。AnchorProvider是结合了两件事的抽象:

  • Connection- 与Solana 集群的连接 (即 localhost、devnet、mainnet)
  • Wallet- (可选)用于支付和签署交易的默认钱包

前端/节点

当使用钱包适配器与前端集成时 ,您需要设置AnchorProvider和Program。

import { Program, AnchorProvider, setProvider } from "@coral-xyz/anchor";
import { useAnchorWallet, useConnection } from "@solana/wallet-adapter-react";
import type { HelloAnchor } from "./idlType";
import idl from "./idl.json";

const { connection } = useConnection();
const wallet = useAnchorWallet();

const provider = new AnchorProvider(connection, wallet, {});
setProvider(provider);

export const program = new Program(idl as HelloAnchor, {
  connection,
});

在上面的代码片段中:

  • idl.json是 Anchor 生成的 IDL 文件,可以 /target/idl/<program-name>.json在 Anchor 项目中找到。
  • idlType.ts是 IDL 类型(用于 TS), /target/types/<program-name>.ts在 Anchor 项目中找到。

Program或者,您可以仅使用 IDL 和Solana 集群创建 的实例Connection。这意味着没有默认的 Wallet,但允许您使用Program来获取帐户或构建指令,而无需连接钱包

import { clusterApiUrl, Connection, PublicKey } from "@solana/web3.js";
import { Program } from "@coral-xyz/anchor";
import type { HelloAnchor } from "./idlType";
import idl from "./idl.json";

const connection = new Connection(clusterApiUrl("devnet"), "confirmed");

export const program = new Program(idl as HelloAnchor, {
  connection,
});

测试文件

Anchor 会自动在新项目的默认测试文件中设置一个Program实例。但是,此设置与在 Anchor 工作区外部初始化的方式不同Program ,例如在 React 或 Node.js 应用程序中。

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { HelloAnchor } from "../target/types/hello_anchor";

describe("hello_anchor", () => {
  // Configure the client to use the local cluster.
  anchor.setProvider(anchor.AnchorProvider.env());

  const program = anchor.workspace.HelloAnchor as Program<HelloAnchor>;

  it("Is initialized!", async () => {
    // Add your test here.
    const tx = await program.methods.initialize().rpc();
    console.log("Your transaction signature", tx);
  });
});

调用指令

一旦Program使用程序 IDL 设置,您就可以使用 Anchor MethodsBuilder 来:

  • 建立个别指令
  • 建立交易
  • 建立并发送交易

基本格式如下:

await program.methods // 这是用于从程序的 IDL 创建指令调用的构建器 API
  .instructionName(instructionData) // 接下来.methods,从程序 IDL 中指定指令的名称,并将任何所需的参数作为逗号分隔的值传递。
  .accounts({}) // 按照IDL中指定的方式传入指令所需的账户地址
  .signers([]) //  可选地传入指令所需的作为附加签名者的密钥对数组。这通常在创建新帐户时使用,其中帐户地址是新生成的密钥对的公钥。请注意,.signers只有在使用.rpc()时才应使用。当使用 .transaction()或 时.instruction(),应在发送之前将签名者添加到交易中。
  .rpc();

Anchor 提供了多种构建程序指令的方法:

.rpc

该 rpc() 方法 发送 带有指定指令的签名交易TransactionSignature并返回。
当使用 时.rpc,Wallet来自 的Provider将自动包含在签名者中。

// Generate keypair for the new account
const newAccountKp = new Keypair();

const data = new BN(42);
const transactionSignature = await program.methods
  .initialize(data)
  .accounts({
    newAccount: newAccountKp.publicKey,
    signer: wallet.publicKey,
    systemProgram: SystemProgram.programId,
  })
  .signers([newAccountKp])
  .rpc();

.transaction()

该 transaction() 方法 使用指定的指令构建Transaction 而不发送交易。

// Generate keypair for the new account
const newAccountKp = new Keypair();

const data = new BN(42);
const transaction = await program.methods
  .initialize(data)
  .accounts({
    newAccount: newAccountKp.publicKey,
    signer: wallet.publicKey,
    systemProgram: SystemProgram.programId,
  })
  .transaction();

const transactionSignature = await connection.sendTransaction(transaction, [
  wallet.payer,
  newAccountKp,
]);

.instruction()

该 instruction() 方法 使用指定的指令构建TransactionInstruction 。如果您想手动将指令添加到交易并将其与其他指令相结合,这很有用。

// Generate keypair for the new account
const newAccountKp = new Keypair();

const data = new BN(42);
const instruction = await program.methods
  .initialize(data)
  .accounts({
    newAccount: newAccountKp.publicKey,
    signer: wallet.publicKey,
    systemProgram: SystemProgram.programId,
  })
  .instruction();

const transaction = new Transaction().add(instruction);

const transactionSignature = await connection.sendTransaction(transaction, [
  wallet.payer,
  newAccountKp,
]);

获取账户

客户端Program简化了获取和反序列化由 Anchor 程序创建的帐户的过程。
使用program.account后跟 IDL 中定义的帐户类型的名称。Anchor 提供了多种方法来获取帐户。

.all()

用于 all() 获取特定帐户类型的所有现有帐户。

const accounts = await program.account.newAccount.all();

memcmp

使用memcmp(内存比较)来筛选与特定偏移量处的特定值匹配的帐户数据。使用memcmp需要您了解要获取的帐户类型的数据字段的字节布局。
计算偏移量时,请记住 Anchor 程序创建的账户中的前 8 个字节是为账户鉴别器保留的。

const accounts = await program.account.newAccount.all([
  {
    memcmp: {
      offset: 8,
      bytes: "",
    },
  },
]);

fetch()

用于 fetch() 获取单个账户的账户数据

const account = await program.account.newAccount.fetch(ACCOUNT_ADDRESS);

fetchMultiple()

fetchMultiple() 通过传入账户地址数组来获取多个账户的账户数据

const accounts = await program.account.newAccount.fetchMultiple([
  ACCOUNT_ADDRESS_ONE,
  ACCOUNT_ADDRESS_TWO,
]);

原文:https://solana.com/docs/programs/anchor/client-typescript

Solana - IDL 文件

接口定义语言 (IDL) 文件提供了描述程序指令和账户的标准化 JSON 文件。此文件简化了链上程序与客户端应用程序集成的过程。

IDL 的主要优势:

  • 标准化:提供一致的格式来描述程序的指令和帐户
  • 客户端生成:用于生成与程序交互的客户端代码

anchor build命令生成一个位于 的 IDL 文件 /target/idl/<program-name>.json
下面的代码片段突出显示了程序、IDL 和客户端如何相互关联。

程序指令

IDL 中的数组instructions直接对应于程序中定义的指令。它指定每条指令所需的帐户和参数。

  • Program:下面的程序包含一条initialize指令,指定其所需的帐户和参数。
  • 程序帐户:accounts IDL 中的数组对应于程序中用宏注释的结构体。#[account]这些结构体定义了程序创建的账户中存储的数据。
use anchor_lang::prelude::*;

declare_id!("BYFW1vhC1ohxwRbYoLbAWs86STa25i9sD5uEusVjTYNd");

#[program]
mod hello_anchor {
    use super::*;
    pub fn initialize(ctx: Context<Initialize>, data: u64) -> Result<()> {
        ctx.accounts.new_account.data = data;
        msg!("Changed data to: {}!", data);
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(init, payer = signer, space = 8 + 8)]
    pub new_account: Account<'info, NewAccount>,
    #[account(mut)]
    pub signer: Signer<'info>,
    pub system_program: Program<'info, System>,
}

#[account]
pub struct NewAccount {
    data: u64,
}

IDL

生成的 IDL 文件包含标准化 JSON 格式的指令,包括其名称、帐户、参数和鉴别器。

{
  "address": "BYFW1vhC1ohxwRbYoLbAWs86STa25i9sD5uEusVjTYNd",
  "metadata": {
    "name": "hello_anchor",
    "version": "0.1.0",
    "spec": "0.1.0",
    "description": "Created with Anchor"
  },
  "instructions": [
    {
      "name": "initialize",
      "discriminator": [175, 175, 109, 31, 13, 152, 155, 237],
      "accounts": [
        {
          "name": "new_account",
          "writable": true,
          "signer": true
        },
        {
          "name": "signer",
          "writable": true,
          "signer": true
        },
        {
          "name": "system_program",
          "address": "11111111111111111111111111111111"
        }
      ],
      "args": [
        {
          "name": "data",
          "type": "u64"
        }
      ]
    }
  ],
  "accounts": [
    {
      "name": "NewAccount",
      "discriminator": [176, 95, 4, 118, 91, 177, 125, 232]
    }
  ],
  "types": [
    {
      "name": "NewAccount",
      "type": {
        "kind": "struct",
        "fields": [
          {
            "name": "data",
            "type": "u64"
          }
        ]
      }
    }
  ]
}

Client

然后使用IDL文件生成与程序交互的客户端,简化调用程序指令的过程。

import * as anchor from "@coral-xyz/anchor";
import { Program, BN } from "@coral-xyz/anchor";
import { HelloAnchor } from "../target/types/hello_anchor";
import { Keypair } from "@solana/web3.js";
import assert from "assert";

describe("hello_anchor", () => {
  const provider = anchor.AnchorProvider.env();
  anchor.setProvider(provider);
  const wallet = provider.wallet as anchor.Wallet;
  const program = anchor.workspace.HelloAnchor as Program<HelloAnchor>;

  it("initialize", async () => {
    // Generate keypair for the new account
    const newAccountKp = new Keypair();

    // Send transaction
    const data = new BN(42);
    const transactionSignature = await program.methods
      .initialize(data)
      .accounts({
        newAccount: newAccountKp.publicKey,
        signer: wallet.publicKey,
      })
      .signers([newAccountKp])
      .rpc();

    // Fetch the created account
    const newAccount = await program.account.newAccount.fetch(
      newAccountKp.publicKey,
    );

    console.log("Transaction signature: ", transactionSignature);
    console.log("On-chain data is:", newAccount.data.toString());
    assert(data.eq(newAccount.data));
  });
});

鉴别器

Anchor 为程序中的每个指令和账户类型分配一个唯一的 8 字节鉴别符。这些鉴别符作为标识符来区分不同的指令或账户类型。
鉴别器是使用前缀的 Sha256 哈希的前 8 个字节与指令或帐户名称相结合生成的。从 Anchor v0.30 开始,这些鉴别器包含在 IDL 文件中。
请注意,使用 Anchor 时,您通常不需要直接与这些鉴别器交互。本节主要介绍如何生成和使用鉴别器。

instructions

指令鉴别器被程序用来决定在调用时要执行哪条具体指令。
当调用 Anchor 程序指令时,鉴别符将作为指令数据的前 8 个字节包含在内。此操作由 Anchor 客户端自动完成。

IDL

  "instructions": [
    {
      "name": "initialize",
      "discriminator": [175, 175, 109, 31, 13, 152, 155, 237],
       ...
    }
  ]

accounts

账户鉴别器用于在反序列化链上数据时识别具体的账户类型,在账户创建时设置。

IDL

  "accounts": [
    {
      "name": "NewAccount",
      "discriminator": [176, 95, 4, 118, 91, 177, 125, 232]
    }
  ]

指令的鉴别器是前缀的 Sha256 哈希的前 8 个字节global加上指令名称。

例如:

sha256("global:initialize")

十六进制输出:

af af 6d 1f 0d 98 9b ed d4 6a 95 07 32 81 ad c2 1b b5 e0 e1 d7 73 b2 fb bd 7a b5 04 cd d4 aa 30

前 8 个字节用作指令的鉴别符。

af = 175
af = 175
6d = 109
1f = 31
0d = 13
98 = 152
9b = 155
ed = 237

您可以在此处的Anchor 代码库中找到鉴别器生成的实现 ,该代码库在此处使用 。

总结:https://solana.com/docs/programs/anchor/idl

Solana- Anchor项目文件结构

以下是 Anchor 工作区中默认文件结构的概述:

.
├── .anchor
│   └── program-logs
├── app
├── migrations
├── programs
│   └── [project-name]
│       └── src
│           ├── lib.rs
│           ├── Cargo.toml
│           └── Xargo.toml
├── target
│   ├── deploy
│   │   └── [project-name]-keypair.json
│   ├── idl
│   │   └── [project-name].json
│   └── types
│       └── [project-name].ts
├── tests
│   └── [project-name].ts
├── Anchor.toml
├── Cargo.toml
└── package.json

程序文件夹

/programs文件夹包含项目的 Anchor 程序。单个工作区可以包含多个程序。

测试文件夹

/tests文件夹包含项目的测试文件。创建项目时会为您创建一个默认测试文件。

目标文件夹

/target文件夹包含构建输出。主要子文件夹包括:

  • /deploy:包含您的程序的密钥对和程序二进制文件。
  • /idl:包含程序的 JSON IDL。
  • /types:包含 IDL 的 TypeScript 类型。

Anchor.toml文件

该Anchor.toml文件为您的项目配置工作区设置。

.anchor 文件夹

包含一个program-logs文件,其中包含上次运行测试文件的事务日志。

应用程序文件夹

该/app文件夹是一个空文件夹,您可以选择将其用于前端代码。

Solana Hello World(安装和故障排除)

这是 Solana 的 Hello World 教程。我们将引导您完成安装 Solana 的步骤并解决可能出现的问题。
如果您遇到问题,请查看本文末尾的故障排除部分。

安装步骤

安装 Rust

如果您已经安装了 Rust,请跳过此步骤。

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

安装 Yarn

您需要它来运行单元测试。如果您已经安装了 yarn,请跳过此步骤。

corepack enable

安装 Solana cli

我们强烈建议使用stable版本,而不是latest。 Solana 安装不再支持符号通道名称(edge、beta、stable),因此我们必须指定版本。

sh -c "$(curl -sSfL https://release.anza.xyz/v2.1.5/install)"

安装锚点

Anchor 是 Solana 开发的框架。它在很多方面与 hardhat 非常相似。

cargo install --git https://github.com/coral-xyz/anchor avm --locked --force

avm install latest
avm use latest

测试安装

初始化并构建一个锚点程序(用于 hello world)

调用您的程序day_1而不是day1因为 Anchor 有时似乎会在 idl路径上插入下划线。

anchor init day_1
cd day_1
anchor build

根据您的机器和互联网连接,此步骤可能需要一段时间。这也是您可能遇到安装问题的地方,因此如有必要,请参阅故障排除部分。

配置 Solana 在本地主机上运行

solana config set --url localhost

运行测试验证器节点

在新的 shell 中(而不是在 Anchor 项目中)运行以下命令。但不要关闭运行的 shell anchor build。这会在您的机器上运行本地(测试)Solana 节点实例:

solana-test-validator

确保 program_id 与 Anchor 键同步

返回 Anchor 项目的 shell 并运行以下命令:

anchor keys sync

运行测试

在 Anchor 项目中运行此命令

anchor test --skip-local-validator

上面的命令运行我们程序的测试。如果您尚未创建测试钱包,Anchor 将为您提供如何创建钱包的说明。我们在此不提供这些说明,因为它取决于您的操作系统和文件结构。您可能还需要通过在终端中运行来为自己空投一些本地 Sol 。您可以通过在命令行中solana airdrop 100 {YOUR_WALLET_ADDRESS}运行来获取您的钱包地址。solana address

预期输出如下:

你好世界

现在让我们让程序输出“Hello, world!”。将以下标有 的行添加NEW LINE HERE到programs/day_1/src/lib.rs。

use anchor_lang::prelude::*;

declare_id!("...");

#[program]
pub mod day_1 {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        msg!("Hello, world!"); // **** NEW LINE HERE ****
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize {}

再次运行测试之前,请终止本地验证器进程并使用以下命令重新启动它:

solana-test-validator --reset

再次运行测试

anchor test --skip-local-validator

通过运行查找日志文件

ls .anchor/program-logs/

打开该文件即可看到记录的“Hello world”

实时 Solana 日志

或者,您可以通过打开第三个 shell 并运行以下命令来查看日志:

solana logs

现在再次运行测试,您应该在运行的终端中看到相同的消息solana logs。

program 查询

查询已安装

solana program show --programs

关闭程序

solana program close 3ynNB373...2iTyg --bypass-warning

请注意,一旦关闭某个程序,则不能重新使用该程序 ID 来部署新程序。

问答

  1. 为什么 declared_id! 和 msg! 后面有感叹号?
    在 Rust 中,感叹号表示这些是宏。我们将在后面的教程中重新讨论宏。
  2. 我需要一个初始化函数吗?
    不,这是由 Anchor 框架自动生成的。您可以随意命名。
    在此上下文中,initialize 这个名称没有什么特殊之处,因此我们可以将其更改为任何我们喜欢的名称。这与其他一些关键字和语言不同,例如 main 在某些语言中是一个特殊名称,或者在 Solidity 中,constructor 是一个特殊名称。
    练习:尝试将initialize中的programs/day_1/src/lib.rs和initialize中的重命名为 ,tests/day_1.ts然后initialize2再次运行测试。请参见下面橙色圆圈中标记的更改。
  3. 为什么我们要使用 –skip-local-validator 来运行测试?
    当测试针对某个节点运行时,我们将能够查询该节点的状态变化。如果您无法让节点运行,则可以anchor test在不使用--skip-local-validator标志的情况下运行。但是,这将使您的开发和测试变得更加困难,因此我们建议让本地验证器正常工作。

故障排除

Solana 是一款快速发展的软件,您可能会遇到安装问题。我们在以下部分记录了您最有可能遇到的问题。
我们的教程系列使用以下版本编写: Anchor = 版本 0.29.0 Solana = 版本 1.16.25 * Rustc = 1.77.0-nightly
您可以通过运行以下命令来更改 Anchor 版本:

avm install 0.29.0
avm use 0.29.0
  1. 错误:solana-program v1.18.0无法构建包
    error: package `solana-program v1.18.0` cannot be built because it requires rustc 1.72.0 or newer, while the currently active rustc version is 1.68.0-dev
    Either upgrade to rustc 1.72.0 or newer, or use
    cargo update -p solana-program@1.18.0 --precise ver

    检查您正在使用的 Solana 版本solana --version。然后将该版本插入到ver上面的内容中。示例解决方案如下所示:

  2. 错误[E0658]:使用不稳定库功能“build_hasher_simple_hash_one”
    如果出现以下错误:
    error[E0658]: use of unstable library feature 'build_hasher_simple_hash_one'
    --> src/random_state.rs:463:5
    |
    463 | / fn hash_one<T: Hash>(&self, x: T) -> u64 {
    464 | | RandomState::hash_one(self, x)
    465 | | }
    | |_____^
    |
    = note: see issue #86161 https://github.com/rust-lang/rust/issues/86161 for more information
    = help: add #![feature(build_hasher_simple_hash_one)] to the crate attributes to enable

    运行以下命令:cargo update -p ahash@0.8.7 --precise 0.8.6
    来源:https ://solana.stackexchange.com/questions/8800/cant-build-hello-world

  3. 错误:部署程序失败:错误处理指令 1:自定义程序错误:0x1
    Error: Deploying program failed: Error processing Instruction 1: custom program error: 0x1
    There was a problem deploying: Output { status: ExitStatus(unix_wait_status(256)), stdout: "", stderr: "" }.

    如果出现此错误,则表示您的密钥未同步。运行anchor keys sync

  4. 错误:无法发送交易:交易模拟失败:尝试加载不存在的程序
    您的密钥未同步。运行anchor keys sync
  5. 错误:您配置的 rpc 端口:8899 已被使用
    当验证器在后台运行时,您anchor test没有运行--skip-local-validator。请关闭验证器并运行,anchor test或在验证器运行时运行anchor test --skip-local-validator。跳过本地验证器意味着跳过它为项目创建的临时验证器,而不是在后台运行的验证器。
  6. 错误:帐户 J7t…zjK 资金不足,无法消费
    运行以下命令将 100 SOL 空投到您的开发地址:
    solana airdrop 100 J7t...zjK
  7. 错误:RPC 请求错误:集群版本查询失败
    Error: RPC request error: cluster version query failed: error sending request for url (http://localhost:8899/): error trying to connect: tcp connect error: Connection refused (os error 61)
    There was a problem deploying: Output { status: ExitStatus(unix_wait_status(256)), stdout: "", stderr: "" }.

    这意味着solana-test-validator不在后台运行。solana-test-validator在另一个 shell 中运行。

  8. 线程“main”因“调用Option::unwrap()某个None值”而惊慌失措
    thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', /Users/username/.cargo/git/checkouts/anchor-50c4b9c8b5e0501f/347c225/lang/syn/src/idl/file.rs:214:73
    note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

    你可能还没跑anchor build

  9. 我在使用 Mac,出现错误:无法启动验证器:无法在测试分类账中创建分类账:块存储错误
    按照此Stack Exchange 线程中的说明进行操作。
  10. 我的 Mac 上有 node.js,但是没有 corepack
    运行以下命令:
    brew install corepack
    brew link --overwrite corepack

    来源:https ://stackoverflow.com/questions/70082424/command-not-found-corepack-when-installing-yarn-on-node-v17-0-1

  11. 错误:不是目录
    BPF SDK: /Users/rareskills/.local/share/solana/install/releases/stable-43daa37937907c10099e30af10a5a0b43e2dd2fe/solana-release/bin/sdk/bpf
    cargo-build-bpf child: rustup toolchain list -v
    cargo-build-bpf child: rustup toolchain link bpf /Users/rareskills/.local/share/solana/install/releases/stable-43daa37937907c10099e30af10a5a0b43e2dd2fe/solana-release/bin/sdk/bpf/dependencies/bpf-tools/rust
    error: not a directory:

    清除缓存:运行rm -rf ~/.cache/solana/*

  12. 错误:target/idl/day_1.json 不存在。您运行了吗anchor build?
    创建一个新项目并将其命名为 day_1 而不是 day1。Anchor 似乎会在某些机器上默默插入下划线。

原文:https://www.rareskills.io/post/hello-world-solana

Solana 智能合约编程语言

Solana 编程语言

Solana 的主要编程语言是Rust,但也支持C、C++甚至Python 。
Solana 编码语言以类似的方式使用 Rust、C 和 C++。我们将在后面的部分讨论 Python。
Rust 是一门编译型语言,如果你在电脑上编译 Rust,它最终会变成 LLVM-IR(低级虚拟机中间表示),LLVM 会把它变成可以在你的机器(x86、arm64 等)上运行的字节码。

在 Solana 中,该序列如下所示:
1)将 Rust 编译为 LLVM-IR,然后编译为 BPF(伯克利数据包过滤器),并将字节码存储在区块链上。
2)验证器 JIT 编译(即时编译)BPF 为与其硬件兼容的指令集,通常是 x86,但 arm64 可能是另一个常见目标。

你可能会问,数据包过滤器?Solana 编程语言为什么要与互联网数据包有关?
这实际上是 Solana 的一个聪明的设计选择。

用户空间与内核空间

Linux 有内核空间和用户空间的概念。如果你想要执行诸如打开文件或启动另一个进程之类的操作,你的可执行文件需要请求操作系统替它执行这些操作。假设你编写了一个 Python 脚本来打开一个文件并打印出每个偶数行。文件字节码的实际加载发生在内核空间中,但是一旦将字节码提供给脚本,对 ASCII 的解释以及确定行号是偶数还是奇数就会发生在用户空间中。

这种抽象存在的原因有很多,但其中一个明显的原因就是安全性。并非每个用户或可执行文件都应该能够打开或执行任意文件。操作系统决定允许使用哪些“API”。(顺便说一句,打开文件的“API”在操作系统术语中技术上称为“系统调用”)。

同样,程序和可执行文件不应被允许任意访问传入的互联网数据包。默认情况下,它们必须进行系统调用,以请求操作系统允许查看只能从内核空间访问的数据包。

这里必须强调一个重要概念:在用户空间和内核空间之间来回转换通常很慢。

如果您正在过滤传入的互联网数据包,那么用户空间和内核空间之间就会有很多来回跳转。想象一下将每个传入数据包从内核空间复制到用户空间。这将产生大量开销。

这就是 BPF 被发明的原因。你可以在内核空间内运行可执行文件来避免这种跳跃

但是,如果您对拥有内核特权有所了解,您就会知道这非常危险!如果出现错误,控制内核(操作系统)可能会导致计算机崩溃。更糟糕的是,如果执行了恶意代码,则损害是无限的。

当然,BPF 设计者已经想到了这一点。在执行 BPF 代码之前,会对其进行验证,以确保其运行一段固定的时间,即必须终止、只能访问指定的内存区域,并遵循其他适当的限制

顺便说一句,自从 BPF 发明以来,它的用途就已不仅限于过滤数据包,但它的名字却一直沿用至今。

为什么 Solana 语言使用 BPF

通过利用现有的确保 BPF 程序安全的研究,Solana 可以在运行速度最快的地方运行智能合约 — 内核内部!仔细想想,这真是太了不起了。您可以在操作系统最敏感(但最高效)的部分 — 操作系统内核中运行任何人都可以编写的不受信任的智能合约。Solana 可以利用数十年来在该领域的研究和投资来获得良好的性能提升。

BPF 不是机器指令;它仍然是自己的一组字节码。但是,它可以被 JIT 编译为各种 CPU 架构。

Solana 链上程序通过LLVM 编译器基础结构编译 为 可执行和可链接格式 (ELF),其中包含伯克利数据包过滤器 (BPF)字节码 的变体 。
由于 Solana 使用 LLVM 编译器基础架构,因此可以使用任何能够针对 LLVM 的 BPF 后端的编程语言编写程序。
BPF 提供了一套高效的 指令集,可以在解释型虚拟机中执行,也可以作为高效的即时编译的本机指令执行。

回到 Rust、C 和 C++

这三种编程语言早已得到 LLVM 的支持。这是 Solana 可以利用数十年投资的另一个领域(是的,用几十年来谈论技术有点奇怪,但LLVM 早在 2003 年就问世了)。

您可以使用 C 或 C++ 作为 Solana 编程语言,但工具支持会少得多。通常建议您使用 Rust,即使您是 C 或 C++ 专家。如果您已经精通 C++,那么 Rust 将很容易学习。

你需要了解多少 Rust 才能编写 Solana 程序?

不是很多,但仍然需要一些学习。Rust 不是一种可以“谷歌一下”的语言。例如,假设您从 Java 或 Scala 背景开始使用 Ruby 编程。在这种情况下,您可以很容易地向 Google 询问等效的编程模式(您的代码可能看起来不太符合惯用语,但它可读且功能齐全)。如果(当)您从 Stackoverflow 复制和粘贴代码,您仍然会对代码的作用有很好的直觉。

但是,如果你使用 Rust 来做这件事,你会遇到一些令人沮丧的障碍。Rust 的语法很难查找(试着在搜索引擎中查找“#”),并且它有一些其他编程语言中没有的概念。

Rust 是一种广泛的语言。但是,您只需要了解它的一个子集。我们的Solana 开发课程会同时教授 Rust 和 Solana,这样您就可以只关注 Rust 中您需要的部分。

Solana 如何使用 Python

将 Rust、C 或 C++ 编译为 BPF 非常简单。对于 Python,情况则截然不同;显然,Python 是一种解释型语言,而不是一种可以用 LLVM 编译的语言。

简单来说,Python 被转换成 Rust,然后一切都按上面的方式运行。

如果你想要确切的工作流程,可以在seahorse-lang github上查看详细的文档

Solana 程序通常不是用原始 Rust 编写的;大多数开发人员使用 Anchor Framework。因此,尽管 Seahorse 执行相当典型的转译,但它也利用了计划中的框架相似性。

Seahorse 框架与 Anchor 框架紧密建模,因此 Python 代码可以转换为 Rust 代码,而 Rust 代码的编写方式与 Anchor 框架中的编写方式紧密建模。

请注意,该项目目前处于测试阶段。

Solana 使用 solidity 吗?

是的,可以用 Solidity 编写 Solana 应用程序,但有些实验性。solang solidity 编译器是为了支持将 Solidity 编译为 BPF 而构建的。

Solana 客户端编程语言

Solana 客户端本身(即在为区块链提供支持的节点上运行的程序,而不是程序(智能合约))是用 Rust 编写的。目前,Jump Crypto 正在重新实现 Solana 客户端firedancer,该客户端完全用 C 语言编写。

附录:Rust 正在将 BPF 迁移到 SBF。

自 2022 年 10 月起,Solana 开始从 BPF 迁移到 SBF(Solana 二进制格式)。截至撰写本文时,有关此更改的文档非常稀少,但这不会影响大多数开发人员。如果您的构建工具配置为编译为 BPF,您将收到弃用警告,但一切仍将运行。只需更改您的构建标志即可。

资源

https://www.kernel.org/doc/html/latest/bpf/instruction-set.html
https://docs.rs/solana_rbpf/latest/solana_rbpf/ebpf/index.html
https://www.youtube。 com/watch?v=5jQvuPWpzcE
https://ebpf.io/what-is-ebpf/
https://www.youtube.com/watch?v=Q8eY67hDvkc

原文:https://www.rareskills.io/post/solana-smart-contract-language