feat: docs i18n support (continuation from #14818) (#17190)

* Initial commit for i18n support

* Bring out index page

* Lint
	modified:   src/pages/index.js

* More Crowdin ephemera

* copy src content to i18n/**/docusaurus-plugin-content-docs/current folders

* run write-translations

* remove superflous files in i18n

* fix formatting

* remove icons folders from translations, add ja, pt, vi translations

* fix formatting

* update translation JSON files

* add crowdin support & crowdin:sync command, sync with crowdin

* exclude failing locales

* remove i18n from git cache, add write-heading-ids script

* add crowdin:sync to before_install.sh script

* add write-i18m shortening script & crowdin:download script (also to before_install.sh)

* move crowdin:download from before_install.sh to prebuild hook

* remove prebuild hook

* remove crowdin script, bump deps

* re-add cli/usage

* update package-lock.json

* add translation sync to build.sh for master

* update README

* add ja to localesNotBuilding, write e-ids

* fix formatting

* get on sync with master

* replace check for master branch

* change build script to work on travis

* use refs instead of names

* fix build.sh

* replace head ref check with channel check

* get stable channel in build.sh

* remove zip

* remove header ids

Co-authored-by: rmshea <8948187+rmshea@users.noreply.github.com>
This commit is contained in:
Tim Hagn 2021-05-28 06:21:37 +02:00 committed by GitHub
parent e833d2ba5b
commit 53c91ee89e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
171 changed files with 15824 additions and 26701 deletions

4
docs/.gitignore vendored
View File

@ -14,6 +14,7 @@ vercel.json
# Misc
.DS_Store
.env
.env.local
.env.development.local
.env.test.local
@ -28,3 +29,6 @@ yarn-error.log*
/src/tests.ok
/src/cli/usage.md
/src/.gitbook/assets/*.svg
# i18n gets generated by write-translations & Crowdin
i18n

View File

@ -5,7 +5,7 @@ Static content delivery is handled using `vercel`.
### Installing Docusaurus
```
```sh
$ npm install
```
@ -15,7 +15,7 @@ This command starts a local development server and open up a browser window.
Most changes are reflected live without having to restart the server.
(You might have to run build.sh first if you run into failures)
```
```sh
$ npm run start
```
@ -24,10 +24,28 @@ $ npm run start
This command generates static content into the `build` directory and can be
served using any static contents hosting service.
```
```sh
$ docs/build.sh
```
### Translations
Translations are sourced from [Crowdin](https://docusaurus.io/docs/i18n/crowdin)
and generated when master is built.
For local development use the following two commands in the `docs` directory.
To download the newest Documentation translations run:
```sh
npm run crowdin:download
```
To upload changes from `src` & generate [explicit IDs](https://docusaurus.io/docs/markdown-features/headings#explicit-ids):
```shell
npm run crowdin:upload
```
### CI Build Flow
The docs are built and published in Travis CI with the `docs/build.sh` script.

View File

@ -14,12 +14,20 @@ source ../ci/rust-version.sh
../ci/docker-run.sh "$rust_stable_docker_image" docs/convert-ascii-to-svg.sh
./set-solana-release-tag.sh
# Get current channel
eval "$(../ci/channel-info.sh)"
# Synchronize translations with Crowdin only on stable channel
if [ "$CHANNEL" = stable ]; then
echo "Downloading & updating translations..."
npm run crowdin:download
npm run crowdin:upload
fi
# Build from /src into /build
npm run build
echo $?
eval "$(../ci/channel-info.sh)"
# Publish only from merge commits and beta release tags
if [[ -n $CI ]]; then
if [[ -z $CI_PULL_REQUEST ]]; then

16
docs/crowdin.yml Normal file
View File

@ -0,0 +1,16 @@
project_id: '2'
api_token_env: 'CROWDIN_PERSONAL_TOKEN'
base_url: 'https://solana.crowdin.com'
preserve_hierarchy: true
files: [
# JSON translation files
# {
# source: '/i18n/en/**/*',
# translation: '/i18n/%two_letters_code%/**/%original_file_name%',
#},
# Docs Markdown files
{
source: '/src/**/*.md',
translation: '/i18n/%two_letters_code%/docusaurus-plugin-content-docs/current/**/%original_file_name%',
},
]

View File

@ -20,8 +20,29 @@ module.exports = {
},
],
i18n: {
defaultLocale: 'en',
locales: ['en', 'zh'],
defaultLocale: "en",
locales: ["en", "de", "es", "ru", "ar"],
// localesNotBuilding: ["ko", "pt", "vi", "zh", "ja"],
localeConfigs: {
en: {
label: "English",
},
ru: {
label: "Русский",
},
es: {
label: "Español",
},
de: {
label: "Deutsch",
},
ar: {
label: "العربية",
},
ko: {
label: "한국어",
},
},
},
themeConfig: {
navbar: {
@ -57,8 +78,8 @@ module.exports = {
position: "left",
},
{
type: 'localeDropdown',
position: 'right',
type: "localeDropdown",
position: "right",
},
{
href: "https://discordapp.com/invite/pquxPsq",

View File

@ -1,50 +0,0 @@
{
"⛏ Start Building": {
"message": "⛏ 开始开发",
"description": "start-building"
},
"Get started building your decentralized app or marketplace.": {
"message": "开始开发您的去中心化应用或市场。",
"description": "get-started-building"
},
"🎛 Run a Validator Node": {
"message": "🎛 运行一个验证节点",
"description": "run-validator"
},
"Validate transactions, secure the network, and earn rewards.": {
"message": "验证交易,保护网络并获得奖励。",
"description": "validate-transactions"
},
"🏛 Create an SPL Token": {
"message": "🏛 创建一个 SPL 代币",
"description": "create-spl"
},
"Launch your own SPL Token, Solana's equivalent of ERC-20.": {
"message": "启动您自己的 SPL 代币,类似于 Solana 区块链的 ERC-20。",
"description": "erc-20"
},
"🏦 Integrate an Exchange": {
"message": "🏦 集成一个交易所。",
"description": "integrate-exchange"
},
"Follow our extensive integration guide to ensure a seamless user experience.": {
"message": "请遵循我们通用集成指南,来确保用户体验。",
"description": "integration-guide"
},
"📲 Manage a Wallet": {
"message": "📲 管理钱包",
"description": "manage-wallet"
},
"Create a wallet, check your balance, and learn about wallet options.": {
"message": "创建一个钱包,检查余额,了解钱包选项。",
"description": "wallet-options"
},
"🤯 Learn How Solana Works": {
"message": "🤯 了解 Solana 工作原理",
"description": "learn-how"
},
"Get a high-level understanding of Solana's architecture.": {
"message": "对 Solana 架构进行深入了解。",
"description": "high-level"
}
}

View File

@ -1,110 +0,0 @@
{
"version.label": {
"message": "下一步",
"description": "The label for version current"
},
"sidebar.docs.category.About": {
"message": "关于",
"description": "The label for category About in sidebar docs"
},
"sidebar.docs.category.Wallets": {
"message": "钱包",
"description": "The label for category Wallets in sidebar docs"
},
"sidebar.docs.category.Web Wallets": {
"message": "网页钱包",
"description": "The label for category Web Wallets in sidebar docs"
},
"sidebar.docs.category.Hardware Wallets": {
"message": "硬件钱包",
"description": "The label for category Hardware Wallets in sidebar docs"
},
"sidebar.docs.category.Command-line Wallets": {
"message": "命令行钱包",
"description": "The label for category Command-line Wallets in sidebar docs"
},
"sidebar.docs.category.Staking": {
"message": "质押",
"description": "The label for category Staking in sidebar docs"
},
"sidebar.docs.category.Command Line": {
"message": "命令行",
"description": "The label for category Command Line in sidebar docs"
},
"sidebar.docs.category.Developing": {
"message": "开发中",
"description": "The label for category Developing in sidebar docs"
},
"sidebar.docs.category.Programming Model": {
"message": "编程模型",
"description": "The label for category Programming Model in sidebar docs"
},
"sidebar.docs.category.Clients": {
"message": "客户端",
"description": "The label for category Clients in sidebar docs"
},
"sidebar.docs.category.Builtins": {
"message": "内置模式",
"description": "The label for category Builtins in sidebar docs"
},
"sidebar.docs.category.Deployed Programs": {
"message": "部署程序",
"description": "The label for category Deployed Programs in sidebar docs"
},
"sidebar.docs.category.Integrating": {
"message": "集成",
"description": "The label for category Integrating in sidebar docs"
},
"sidebar.docs.category.Validating": {
"message": "验证",
"description": "The label for category Validating in sidebar docs"
},
"sidebar.docs.category.Incenvitized Testnet": {
"message": "激励测试网",
"description": "The label for category Incenvitized Testnet in sidebar docs"
},
"sidebar.docs.category.Registration": {
"message": "注册",
"description": "The label for category Registration in sidebar docs"
},
"sidebar.docs.category.Participation": {
"message": "参与",
"description": "The label for category Participation in sidebar docs"
},
"sidebar.docs.category.Clusters": {
"message": "集群",
"description": "The label for category Clusters in sidebar docs"
},
"sidebar.docs.category.Architecture": {
"message": "架构",
"description": "The label for category Architecture in sidebar docs"
},
"sidebar.docs.category.Cluster": {
"message": "集群",
"description": "The label for category Cluster in sidebar docs"
},
"sidebar.docs.category.Validator": {
"message": "验证节点",
"description": "The label for category Validator in sidebar docs"
},
"sidebar.docs.category.Design Proposals": {
"message": "设计提案",
"description": "The label for category Design Proposals in sidebar docs"
},
"sidebar.docs.category.Implemented": {
"message": "实现",
"description": "The label for category Implemented in sidebar docs"
},
"sidebar.docs.category.Economic Design": {
"message": "经济设计",
"description": "The label for category Economic Design in sidebar docs"
},
"sidebar.docs.category.Validation Client Economics": {
"message": "验证客户端经济学",
"description": "The label for category Validation Client Economics in sidebar docs"
},
"sidebar.docs.category.Accepted": {
"message": "已接受",
"description": "The label for category Accepted in sidebar docs"
}
}

View File

@ -1,16 +0,0 @@
---
title: 命令行指南
---
在本节中我们将描述如何使用Solana命令行工具创建_钱包_来发送和接收SOL代币以及通过委托质押来参与到集群中。
为了与Solana集群进行交互我们需要使用其命令行界面也称为CLI。 命令行是Solana核心团队首次部署新功能所用到的工具。 命令行界面不一定是最容易使用的但它提供了对Solana帐户的最直接、灵活和安全的访问。
## 准备工作
要开始使用 Solana 命令行 (CLI) 工具:
- [安装 Solana 工具](cli/install-solana-cli-tools.md)
- [选择一个集群](cli/choose-a-cluster.md)
- [创建一个钱包](wallet-guide/cli.md)
- [查看 CLI 协议](cli/conventions.md)

View File

@ -1,37 +0,0 @@
---
title: 连接到一个集群
---
查看 [Solana 集群](../clusters.md) 获取关于可用集群的通用信息。
## 配置命令行工具
您可以通过下述指令来检查集群正在运行的 Solana 命令行工具 (CLI)
```bash
solana config get
```
通过 `solana config set` 命令来制定某个集群。 设置一个目标集群后,未来的任何子命令都会从该集群发送/接收信息。
例如,要指定 Devnet 集群,请运行:
```bash
solana config set --url https://api.devnet.solana.com
```
## 确保版本相匹配
虽然严格来说没有必要,但是当 CLI 版本与运行在集群中的软件版本相匹配时,一般来说 CLI 版本能够发挥最大作用。 查看本地安装的 CLI 版本,请运行:
```bash
solana --version
```
查看集群版本,请运行:
```bash
solana cluster-version
```
确保本地的 CLI 版本比群集版本新或者至少相同。

View File

@ -1,59 +0,0 @@
---
title: 使用 Solana CLI 命令
---
在运行 Solana CLI 命令之前,让我们先来熟悉一下所有的命令。 首先Solana CLI 实际上是你可能会用到操作的命令集合。 您可以通过运行以下操作查看所有可能命令的列表:
```bash
solana --help
```
需要研究特定的命令,请运行:
```bash
solana <COMMAND> --help
```
您可以将文本 `<COMMAND>` 替换为你想了解的命令名称。
命令使用信息通常包含诸如 `<AMOUNT>`、`<ACCOUNT_ADDRESS>` 或 `<KEYPAIR>` 等字段。 每个字段都是 _type_ 文本的占位符,您可以使用它执行命令。 例如,您可以将 `<AMOUNT>` 替换为诸如 `42``100.42` 等数字。 您可以将 `<ACCOUNT_ADDRESS>` 替换为公钥的 base58 编码,例如 `9grmKMwTiZwUHSExjtbFzHLPTdWoXgcg1bZkhvwTrTww`
## 密钥对协议
许多使用 CLI 工具的命令需要一个 `<KEYPAIR>` 值。 密钥对应使用的值取决于您创建的 [命令行钱包的类型](../wallet-guide/cli.md)。
例如,显示钱包地址(也称为密钥)CLI 帮助文档显示:
```bash
solana-keygen pubkey <KEYPAIR>
```
下面我们来展示根据钱包类型来解决应该在 `<KEYPAIR>` 中插入什么的问题。
#### 纸钱包
在纸质钱包中,密钥对源自助记词和你在钱包创建时输入的可选密码。 若要让纸钱包密钥在任意地方显示 `<KEYPAIR>` 文本在示例或帮助文档中,请输入单词 `ASK`,然后程序会强制您在运行命令时输入种子单词。
显示纸钱包的钱包地址:
```bash
solana-keygen pubkey
```
#### 文件系统钱包
有了一个文件系统钱包,密钥对就会存储在您的计算机上的文件中。 将 `<KEYPAIR>` 替换为对密钥文件的完整文件路径。
例如,如果文件系统密钥对文件位置是 `/home/solana/my_wallet.json`,请输入来显示地址:
```bash
solana-keygen pubkey /home/solana/my_wallet.json
```
#### 硬软件钱包
如果您选择了硬件钱包,请使用您的 [密钥对链接](../wallet-guide/hardware-wallets.md#specify-a-hardware-wallet-key),比如 `blap://ledger?key=0`
```bash
solana-keygen pubkey usb://ledger?key=0
```

View File

@ -1,149 +0,0 @@
---
title: 委托您的质押
---
通过 [ 获取 SOL ](transfer-tokens.md) 以后,您可以通过 _stake_ 将它委托给一个验证节点。 质押Stake就是在 _stake account_ 中的代币。 Solana 根据质押权重为验证节点分配投票权重,权重会影响它们在区块链中决定下一个有效交易区块。 然后 Solana 会按周期生成新的 SOL 来奖励质押者和验证节点。 您委托的代币越多,获得的奖励就越高。
## 创建一个质押账户
要委托代币,您首先要将代币转入一个质押帐户。 而要创建一个帐户,您需要一个密钥对: 它的公钥将作为 [质押账户地址](../staking/stake-accounts.md#account-address)。 此处无需密码或加密;此密钥对将在创建密钥账户后被丢弃。
```bash
solana-keygen new --no-passphrase -o stake-account.json
```
输出结果将在文本 `pubkey:` 后面包括该地址。
```text
pubkey: GKvqsuNcnwWqPzzuhLmGi4rzzh55FhJtGizkhHaEJqiV
```
复制公钥并将它安全地存储起来。 在后续创建质押账户的操作中您将随时需要用到它。
创建一个质押账户:
```bash
solana create-stake-account --from <KEYPAIR> stake-account.json <AMOUNT> \
--stake-authority <KEYPAIR> --withdraw-authority <KEYPAIR> \
--fee-payer <KEYPAIR>
```
`<AMOUNT>` 的代币从 `<KEYPAIR>` 转到了 stake-account.json 公钥的一个新质押账户。
现在可以丢弃 stake-account.json 文件了。 要授权额外的操作,您可以通过 `--stake-authority``--rap-authority` 密钥对,而无需使用 stak-account.json。
使用 `solana stake-account` 命令查看新的质押账户:
```bash
solana stake-account <STAKE_ACCOUNT_ADDRESS>
```
结果大概呈这样:
```text
Total Stake: 5000 SOL
Stake account is undelegated
Stake Authority: EXU95vqs93yPeCeAU7mPPu6HbRUmTFPEiGug9oCdvQ5F
Withdraw Authority: EXU95vqs93yPeCeAU7mPPu6HbRUmTFPEiGug9oCdvQ5F
```
### 设置质押和取款权限
创建账号时,如果需要设置 [质押和提现权限](../staking/stake-accounts.md#understanding-account-authorities),您可以通过 `--stake-authority` and `--withdraw-authority` 选项或 `solana stake-authorize` 命令来实现。 例如,要设置一个新的质押权限,请运行:
```bash
solana stake-authorize <STAKE_ACCOUNT_ADDRESS> \
--stake-authority <KEYPAIR> --new-stake-authority <PUBKEY> \
--fee-payer <KEYPAIR>
```
这将针对已有的质押账号 `<STAKE_ACCOUNT_ADDRESS>`,通过现有的质押权限 `<KEYPAIR>` 来授权一个新的质押权限 `<PUBKEY>`
### 高级功能:派生质押账户地址
当委托质押时,你需要将所有密钥账户中的代币委托给某一个验证节点。 而要委托给多个验证节点,您就需要多个质押账户。 为每个帐户创建一个新密钥对并管理那些地址可能比较繁琐。 好在您可以通过 `--seed` 选项来派生多个质押地址:
```bash
solana create-stake-account --from <KEYPAIR> <STAKE_ACCOUNT_KEYPAIR> --seed <STRING> <AMOUNT> \
--stake-authority <PUBKEY> --withdraw-authority <PUBKEY> --fee-payer <KEYPAIR>
```
`<STRING>` 是一个最多32字节的任意字符串通常情况下是一个对应该派生账户的数字。 第一个账户是"0",第二个是 "1",以此类推。 `<STAKE_ACCOUNT_KEYPAIR>` 公钥发挥基本地址的作用。 该命令将从基础地址和种子字符串中派生一个新地址。 要查看派生出哪个质押地址,请使用 `solana create-address-with-seed`命令:
```bash
solana create-address-with-seed --from <PUBKEY> <SEED_STRING> STAKE
```
`<PUBKEY>` is the public key of the `<STAKE_ACCOUNT_KEYPAIR>` passed to `solana create-stake-account`.
该命令将输出派生地址,可以用于质押操作中的 `<STAKE_ACCOUNT_ADDRESS>` 参数。
## 委托您的质押
想要委托您的质押给某个验证节点,您首先需要它的投票帐号地址。 您可以通过 `solana validators` 命令来查询所有验证节点列表和他们的投票账户:
```bash
solana 验证节点
```
每行的第一列包含验证节点的身份,第二列是投票帐户地址。 选择一个验证节点,并在 `solana delegate-stake` 中使用它的投票帐户地址:
```bash
solana delegate-stake --stake-authority <KEYPAIR> <STAKE_ACCOUNT_ADDRESS> <VOTE_ACCOUNT_ADDRESS> \
--fee-payer <KEYPAIR>
```
质押权限 `<KEYPAIR>` 对地址 `<STAKE_ACCOUNT_ADDRESS>` 进行帐户授权操作。 该质押被委托给投票账户地址 `<VOTE_ACCOUNT_ADDRESS>`
委托质押后,使用 `solana stake-account` 查看质押账户的变化:
```bash
solana stake-account <STAKE_ACCOUNT_ADDRESS>
```
您将在输出中看到“Delegated Stake”和“Delegated Vote Account Address”两个新字段。 结果大概呈这样:
```text
Total Stake: 5000 SOL
Credits Observed: 147462
Delegated Stake: 4999.99771712 SOL
Delegated Vote Account Address: CcaHc2L43ZWjwCHART3oZoJvHLAe9hzT2DJNUpBzoTN1
Stake activates starting from epoch: 42
Stake Authority: EXU95vqs93yPeCeAU7mPPu6HbRUmTFPEiGug9oCdvQ5F
Withdraw Authority: EXU95vqs93yPeCeAU7mPPu6HbRUmTFPEiGug9oCdvQ5F
```
## 取消质押
质押委托以后,您可以使用 `solana deactivate-stake` 命令来取消委托的质押:
```bash
solana deactivate-stake --stake-authority <KEYPAIR> <STAKE_ACCOUNT_ADDRESS> \
--fee-payer <KEYPAIR>
```
质押权限 `<KEYPAIR>` 对地址 `<STAKE_ACCOUNT_ADDRESS>` 进行帐户授权操作。
请注意,质押需要几个 epoch 才能“冷却cool down”。 在冷却期间进行重新质押的操作将会失败。
## 提现质押
使用 `solana withdraw-stake` 命令将代币转移出质押帐户:
```bash
solana withdraw-stake --withdraw-authority <KEYPAIR> <STAKE_ACCOUNT_ADDRESS> <RECIPIENT_ADDRESS> <AMOUNT> \
--fee-payer <KEYPAIR>
```
其中,`<STAKE_ACCOUNT_ADDRESS>` 是现有的质押帐户,质押权限 `<KEYPAIR>` 是提现权限, 而 `<AMOUNT>` 是要转账给接收账户 `<RECIPIENT_ADDRESS>` 的代币数量。
## 拆分质押
在现有质押不能取款的时候,您可能想将质押分配给另外的验证节点。 无法取回的原因可能是处于质押、冷却或锁定的状态。 若要将代币从现有质押账户转移到一个新的帐户,请使用 `solana split-stake` 命令:
```bash
solana split-stake --stake-authority <KEYPAIR> <STAKE_ACCOUNT_ADDRESS> <NEW_STAKE_ACCOUNT_KEYPAIR> <AMOUNT> \
--fee-payer <KEYPAIR>
```
其中,`<STAKE_ACCOUNT_ADDRESS>` 是现有的质押帐户,质押权限 `<KEYPAIR>` 是质押账户的权限, `<NEW_STAKE_ACCOUNT_KEYPAIR>` 是新账户的密钥对,`<AMOUNT>` 是要转账给新账户的代币数量。
若要将质押账户拆分到派生账户地址,请使用 `--seed` 选项。 详情请参阅 [衍生质押账户地址](#advanced-derive-stake-account-addresses)。

View File

@ -1,131 +0,0 @@
---
title: 安装 Solana 工具包
---
取决于您喜欢的工作公式,在电脑上安装 Solana 工具的方法有多种:
- [使用 Solana 的安装工具 (最简单的方法)](#use-solanas-install-tool)
- [下载预置的二进制文件](#download-prebuilt-binaries)
- [通过源代码安装](#build-from-source)
## 通过 Solana 安装工具
### MacOS & Linux
- 打开您最喜欢的终端应用
- 通过运行下述指令,安装 Solana 版本[LATEST_SOLANA_RELEASE_VERSION](https://github.com/solana-labs/solana/releases/tag/LATEST_SOLANA_RELEASE_VERSION) 到您的机器:
```bash
sh -c "$(curl -sSfL https://release.solana.com/LATEST_SOLANA_RELEASE_VERSION/install)"
```
- 您可以用 `LATEST_SOLANA_RELEASE_VERSION` 发布标签替换想要的软件版本,或者使用以下三个通道名称之一: `stable``beta` 或 `edge`
- 以下输出表示更新成功:
```text
downloading LATEST_SOLANA_RELEASE_VERSION installer
Configuration: /home/solana/.config/solana/install/config.yml
Active release directory: /home/solana/.local/share/solana/install/active_release
* Release version: LATEST_SOLANA_RELEASE_VERSION
* Release URL: https://github.com/solana-labs/solana/releases/download/LATEST_SOLANA_RELEASE_VERSION/solana-release-x86_64-unknown-linux-gnu.tar.bz2
Update successful
```
- 根据您的系统,安装程序消息的结束可能稍有不同
```bash
请更新您的 PATH 环境变量来包含 Solana 程序:
```
- 如果您收到上述消息,复制并粘贴下面的推荐命令来更新 `PATH`
- 通过运行以下命令来确认您已经安装了想要的 `solana` 版本:
```bash
solana --version
```
- 安装成功后,就可以通过 `solana-install update` 随时更新 Solana 软件到新版本。
---
### Windows 系统
- 以管理员身份打开命令提示(`cmd.exe`)
- 在 Windows 搜索栏中搜索命令提示。 当命令提示应用出现后,右键单击并选择“以管理员打开”。 如果弹出窗口请求“允许此应用进行设备更改?”,请点击是。
- 复制并粘贴以下命令,然后按回车下载 Solana 安装程序到临时目录:
```bash
curl https://release.solana.com/LATEST_SOLANA_RELEASE_VERSION/solana-install-init-x86_64-pc-windows-msvc.exe --output C:\solana-install-tmp\solana-install-init.exe --create-dirs
```
- 复制并粘贴以下命令,然后按 Enter 安装最新版本的 Solana 软件。 如果系统弹出安全提示窗口,请选择允许程序运行。
```bash
C:\solana-install-tmp\solana-install-init.exe LATEST_SOLANA_RELEASE_VERSION
```
- 安装程序完成后,请按 Enter 键。
- 关闭命令提示窗口,并以普通用户身份重新打开
- 在搜索栏中搜索“Command Prompt”然后点击命令提示应用图标无需以管理员身份运行)
- 通过运行以下命令来确认您已经安装了想要的 `solana` 版本:
```bash
solana --version
```
- 安装成功后,就可以通过 `solana-install update` 随时更新 Solana 软件到新版本。
## 下载预置二进制文件
如果您不想通过 `solana-install` 来管理安装,您也可以手动下载并安装二进制安装包。
### Linux 系统
打开 [https://github.com/solana-labs/solana/releases/latest](https://github.com/solana-labs/solana/releases/latest), download **solana-release-x86_64-unknown-linux-msvc.tar.bz2** 地址,下载二进制文件,然后提取文件:
```bash
tar jxf solana-release-x86_64-unknown-linux-gnu.tar.bz2
cd solana-release/
export PATH=$PWD/bin:$PATH
```
### MacOS 系统
打开 [https://github.com/solana-labs/solana/releases/latest](https://github.com/solana-labs/solana/releases/latest), download **solana-release-x86_64-apple-darwin.tar.bz2** 地址,下载二进制文件,然后提取文件:
```bash
tar jxf solana-release-x86_64-apple-darwin.tar.bz2
cd solana-release/
export PATH=$PWD/bin:$PATH
```
### Windows 系统
- 打开 [https://github.com/solana-labs/solana/releases/latest](https://github.com/solana-labs/solana/releases/latest), download **solana-release-x86_64-pc-windows-msvc.tar.bz2** 地址,下载二进制文件,然后提取文件:
- 打开命令提示并导航到提取二进制文件的目录并运行:
```bash
cd solana-release/
set PATH=%cd%/bin;%PATH%
```
## 通过源代码安装
如果您无法使用预构建的二进制文件或者想通过源代码安装,请打开 [https://github.com/solana-labs/solana/releases/latest](https://github.com/solana-labs/solana/releases/latest), download **solana-release-x86_64-unknown-linux-msvc.tar.bz2** 地址,下载二进制文件,然后提取文件: 提取代码并生成二进制文件:
```bash
./scripts/cargo-install-all.sh .
export PATH=$PWD/bin:$PATH
```
然后你可以运行以下命令来获得与预置二进制文件相同的结果:
```bash
solana-install init
```

View File

@ -1,71 +0,0 @@
---
title: 管理质押账户
---
如果想将质押分配到多个不同的验证节点,您需要为每个验证节点创建一个单独的质押帐户。 如果你按照约定创建了一个种子seed为"0"的质押帐户那么第二个则是“1”第三个是“2”以此类推然后您可以使用 `solana-stock-account` 工具对所有的账户进行单次调用。 您可以用它来汇总所有帐户的余额,将帐户移动到一个新钱包,或设置新的权限。
## 使用方法
### 创建一个质押账户
在质押公钥上创建一个派生的质押帐户并转账进去:
```bash
solana-stake-accounts new <FUNDING_KEYPAIR> <BASE_KEYPAIR> <AMOUNT> \
--stake-authority <PUBKEY> --withdraw-authority <PUBKEY> \
--fee-payer <KEYPAIR>
```
### 账户统计
统计派生账户的数量:
```bash
solana-stake-accounts count <BASE_PUBKEY>
```
### 获取质押账户余额
汇总派生抵押账户的余额:
```bash
solana-stake-accounts balance <BASE_PUBKEY> --num-accounts <NUMBER>
```
### 获取质押账户地址
列出来自给定公钥的每一个质押账户地址:
```bash
solana-stake-accounts addresses <BASE_PUBKEY> --num-accounts <NUMBER>
```
### 设置新权限
为生成的每个抵押帐户设置新权限:
```bash
solana-stake-accounts authorize <BASE_PUBKEY> \
--stake-authority <KEYPAIR> --withdraw-authority <KEYPAIR> \
--new-stake-authority <PUBKEY> --new-withdraw-authority <PUBKEY> \
--num-accounts <NUMBER> --fee-payer <KEYPAIR>
```
### 重定向质押账户
重定向质押账户:
```bash
solana-stake-accounts rebase <BASE_PUBKEY> <NEW_BASE_KEYPAIR> \
--stake-authority <KEYPAIR> --num-accounts <NUMBER> \
--fee-payer <KEYPAIR>
```
对每个质押账户进行原子级别重置并授权,请使用 'move' 命令:
```bash
solana-stake-accounts move <BASE_PUBKEY> <NEW_BASE_KEYPAIR> \
--stake-authority <KEYPAIR> --withdraw-authority <KEYPAIR> \
--new-stake-authority <PUBKEY> --new-withdraw-authority <PUBKEY> \
--num-accounts <NUMBER> --fee-payer <KEYPAIR>
```

View File

@ -1,125 +0,0 @@
---
title: 发送和接收代币
---
该网页展示了如何通过命令行钱包,使用命令行工具接收和发送 SOL代币例如 [纸钱包](../wallet-guide/paper-wallet.md) [文件系统钱包](../wallet-guide/file-system-wallet.md), 或[硬件钱包](../wallet-guide/hardware-wallets.md). 在开始之前,请确认您已经创建了一个钱包,并且可以访问其地址 (pubkey) 和签名密钥对。 请查看我们的[约定来输入不同钱包类型的密钥对](../cli/conventions.md#keypair-conventions).
## 测试您的钱包
在与其他人分享公钥前,您可能需要首先确认密钥的有效性,并确保真正拥有相应的私钥。
在这个例子中,我们将在第一个钱包的基础上再创建另一个钱包,然后转入一些代币。 这个步骤确保您可以在该钱包正常发送和接收代币。
该测试将通过我们的开发者测试网称为devnet。 测试网发行的代币**并没有**实际价值,所以无需担心资产损失。
#### 获取一些空投代币,开始操作
首先在测试网给您的钱包_空投_ 一些虚拟代币。
```bash
solana airdrop 10 <RECIPIENT_ACCOUNT_ADDRESS> --url https://api.devnet.solana.com
```
其中,用您的 base58-encoded 公钥/钱包地址替换此处的 `<RECIPIENT_ACCOUNT_ADDRESS>`文本。
#### 检查钱包余额
通过检查帐户余额确认空投已经成功。 输出值应当为 `10 SOL`:
```bash
solana balance <ACCOUNT_ADDRESS> --url https://api.devnet.solana.com
```
#### 创建第二个钱包地址
我们需要一个新地址来接收代币。 创建第二个密钥对并记录其公钥:
```bash
solana-keygen new --no-passphrase --no-outfile
```
输出将在文本 `pubkey:` 后面包括该地址。 复制该地址。 我们在下一步中要用到它。
```text
pubkey: GKvqsuNcnwWqPzzuhLmGi4rzzh55FhJtGizkhHaEJqiV
```
您还可以通过下述方式创建任何类型的一个(或多个)钱包: [paper](../wallet-guide/paper-wallet#creating-multiple-paper-wallet-addresses), [file system](../wallet-guide/file-system-wallet.md#creating-multiple-file-system-wallet-addresses), 或者 [hardware](../wallet-guide/hardware-wallets.md#multiple-addresses-on-a-single-hardware-wallet).
#### 将代币从您的第一个钱包转到第二个地址
接下来,通过发送来证明你拥有空投代币。 Solana 集群只有在您用交易发送方公钥对应的私钥签名时,才会接受交易。
```bash
solana transfer --from <KEYPAIR> <RECIPIENT_ACCOUNT_ADDRESS> 5 --url https://api.devnet.solana.com --fee-payer <KEYPAIR>
```
其中,用第一个钱包的秘钥对的路径替换 `<KEYPAIR>`,用第二个钱包地址替换 `<RECIPIENT_ACCOUNT_ADDRESS>`
使用 `solana balance` 确认余额已经更新:
```bash
solana balance <ACCOUNT_ADDRESS> --url http://api.devnet.solana.com
```
其中 `<ACCOUNT_ADDRESS>` 是您密钥对的公钥或收件人的公钥。
#### 转账测试的完整示例
```bash
$ solana-keygen new --outfile my_solana_wallet.json # 创建第一个文件系统钱包
产生新的密钥对
为了增加安全性,输入一个密码(空白表示不设置密码)
将新密钥对写入 my_solana_wallet.json
==========================================================================
pubkey: DYw8jCTfwHNRJhhmFcbXvVDTqWMEVFBX6ZKUmG5CNSKK # 第一个钱包的地址
==========================================================================
保存恢复密钥对的助记词:
width enhance concert vacant ketchup eternal spy craft spy guard tag punch # 如果这是一个真实的钱包,不要将这次单词分享到网络上!
==========================================================================
$ solana airdrop 10 DYw8jCTfwHNRJhhmFcbXvVDTqWMEVFBX6ZKUmG5CNSKK --url https://api.devnet.solana.com # 空投 10 个 SOL 到我的钱包地址/公钥
正在从 35.233.193.70:9900 请求 10 SOL
10 SOL
$ solana balance DYw8jCTfwHNRJhhmFcbXvVDTqWMEVFBX6ZKUmG5CNSKK --url https://api.devnet.solana.com # 检查钱包余额
10 SOL
$ solana-keygen new --no-outfile # 创建第二个钱包即纸钱包
生成新的密钥对
为了增加安全性,输入一个密码(空白表示不设置密码)
====================================================================
pubkey: 7S3P4HxJpyyigGzodYwHtCxZyUQe9JiBMHyRWXArAaKv # 这是第二个钱包即纸钱包的地址
=======================================================
保存助记词(用于恢复新秘钥对):
clump panic cousin hurt coast charge engage fall eager urge win love # 如果这是一个真实的钱包,切记不要将这次单词分享到网络上!
====================================================================
$ solana transfer --from my_solana_wallet.json 7S3P4HxJpyyigGzodYwHtCxZyUQe9JiBMHyRWXArAaKv 5 --url https://api.devnet.solana.com --fee-payer my_solana_wallet.json # 发送代币到纸钱包的公钥地址
3gmXvykAd1nCQQ7MjosaHLf69Xyaqyq1qw2eu1mgPyYXd5G4v1rihhg1CiRw35b9fHzcftGKKEu4mbUeXY2pEX2z # 该笔交易的签名
$ solana balance DYw8jCTfwHNRJhhmFcbXvVDTqWMEVFBX6ZKUmG5CNSKK --url https://api.devnet.solana.com
4.999995 SOL # 由于需要 0.000005 SOL 的交易费用,发送金额要稍微小于 5 SOL
$ solana balance 7S3P4HxJpyyigGzodYwHtCxZyUQe9JiBMHyRWXArAaKv --url https://api.devnet.solana.com
5 SOL # 第二个钱包现在已经接收到第一个钱包发送的 5 SOL
```
## 接收代币
首先您需要一个地址让别人来发送代币。 在 Solana 区块链,钱包地址就是密钥对的公钥。 生成密钥对的方法有好几种。 这些方法取决于您选择如何存储密钥对。 密钥对存储在钱包里。 在接收代币之前,您需要通过 [来创建一个钱包](../wallet-guide/cli.md) 完成该步骤后,您就能获得每个密钥对生成的公钥。 公钥是一个 base58 字符的长字节。 其长度从 32 到 44 个字符不等。
## 发送代币
如果您已经持有 SOL 并想要向其他人发送代币,您将需要密钥对的路径, 他们的 base58 编码公钥和准备发送的代币。 上述条件准备好了以后,您可以使用 `solana transfer` 命令来发送代币:
```bash
solana transfer --from <KEYPAIR> <RECIPIENT_ACCOUNT_ADDRESS> <AMOUNT> --fee-payer <KEYPAIR>
```
使用 `solana balance` 确认余额已经更新:
```bash
solana balance <ACCOUNT_ADDRESS>
```

View File

@ -1,63 +0,0 @@
---
title: CLI 使用参考
---
[solana-cli crate](https://crates.io/crates/solana-cli) 为 Solana 提供了一个命令行界面工具
## 示例:
### 获取公钥
```bash
// 命令
$solana-keygen pubkey
// 返回
<PUBKEY>
```
### 空投 SOL/Lamports
```bash
// 命令
$ solana airdrop 2
// 返回
"2.0000000 SOL"
```
### 获取余额
```bash
// 命令
$ solana balance
// 返回
"3.00050001 SOL"
```
### 确认交易
```bash
// 命令
$ solana confirm <TX_SIGNATURE>
// 返回
"Confirmed" / "Not found" / "Transaction failed with error <ERR>"
```
### 部署程序
```bash
// 命令
$ solana deploy <PATH>
// 返回
<PROGRAM_ID>
```
## 使用方法
###
```text
```

View File

@ -1,128 +0,0 @@
---
title: 集群基准
---
Solana git 仓库涵盖了配置本地测试网可能用到的所有脚本。 根据实现的目标,您可能想配置一个全新、增强性能的不同版本的多节点测试网,那么它可能比单纯的仅支持 Rust 的单节点测试节点要复杂得多。 如果您正在尝试开发高级功能(例如智能合约),那么利用一些已有的配置,直接使用 Rust 支持的单节点模型就好。 如果您正在对交易流程进行性能优化,请考虑增强的单节点 demo。 如果你在着手共识算法的工作,那么你将至少需要一个 Rust 的多节点 demo。 如果您想要复制 TPS 性能表,请运行强化的多节点 demo。
对于上述的四种变型,您可能需要最新的 Rust 工具链和 Solana 源代码:
首先,请设置 Solana [README](https://github.com/solana-labs/solana#1-install-rustc-cargo-and-rustfmt) 中提到的 Rust、Cargo 和系统安装包。
请检查 github 代码:
```bash
git clone https://github.com/solana-labs/solana.git
cd solana
```
演示代码有时在我们添加新的低级功能时会失败,所以如果这是您第一次运行 demo为了提高成功的概率请在继续操作之前先查看 [latest release](https://github.com/solana-labs/solana/releases)
```bash
TAG=$(git describe --tags $(git rev-list --tags --max-count=1))
git checkout $TAG
```
### 设置配置
确保在任何节点启动之前都能建立例如投票程序之类的重要程序。 请注意,为了良好的性能,我们在这里使用版本构建的方式。 如果你想要调试构建,只需使用 `cargo build` 并省略 `NDEBUG=1` 命令的一部分。
```bash
cargo build --release
```
运行下面的脚本来初始化网络的创世账本。
```bash
NDEBUG=1 ./multinode-demo/setup.sh
```
### 水龙头
为了验证程序和客户端正常工作,我们需要打开一个水龙头来领取一些测试代币。 水龙头按照 Milton Friedman 的风格“空投”\(免费代币给请求客户\),然后用于测试交易。
打开水龙头:
```bash
NDEBUG=1 ./multinode-demo/faucet.sh
```
### 单节点测试网
在启动验证节点之前,请确保您获取了想要启动验证节点的机器的 IP 地址,并确保 udp 端口 8000-1000 处于打开状态。
现在在独立的 shell 中启动验证节点:
```bash
NDEBUG=1 ./multinode-demo/bootstrap-validator.sh
```
等待几秒钟进行初始化。 当准备好接收交易时它会打印“leader ready...”。 如果领导者没有任何测试代币,它将从水龙头请求一些。 在领导者启动之前,水龙头不用一直运行。
### 多节点测试网
如果要运行一个多节点测试网,在启动一个领导者节点后,需要在独立 shell 中加入一些额外的验证节点:
```bash
NDEBUG=1 ./multinode-demo/validator-x.sh
```
如果要在 Linux 上运行增强性能验证节点,必须在系统中安装 [CUDA 10.0](https://developer.nvidia.com/cuda-downloads)
```bash
./fetch-perf-libs.sh
NDEBUG=1 SOLANA_CUDA=1 ./multinode-demo/bootstrap-validator.sh
NDEBUG=1 SOLANA_CUDA=1 ./multinode-demo/validator.sh
```
### 测试网客户端演示
现在您的单节点或多节点测试网已启动并正常运行了,接下来我们发送一些交易!
在另一个 shell 启动客户端:
```bash
NDEBUG=1 ./multinode-demo/bench-tps.sh # runs against localhost by default
```
刚刚发生了什么? 客户端演示将尽最快的速度将 500,000 笔交易发送到测试网。 然后客户端定期连接测试网,看看它当时处理了多少笔交易。 请注意,这个 demo 故意将大量 UDP 数据包发送给网络,因此网络几乎会丢失很大一部分。 这确保了试验网有机会达到 710k TPS。 客户端 demo 在确保测试网不再处理任何其他交易后就停止运行。 您应该看到一些 TPS 数值出现在屏幕上。 在多节点变体中,您也会看到每个验证节点的 TPS 测量值。
### 测试网调试
代码中有一些非常有用的调试消息,您可以在每个模块和每个级别的基础上启用它们。 在运行一个领导者或验证节点之前,请设置正常的 RUST_LOG 环境变量。
例如:
- 要在任意位置启用 `info` 以及只能在 solana::banking_stage 模块中启用 `debug`
```bash
export RUST_LOG=solana=info,solana::banking_stage=debug
```
- 启用 BPF 程序日志记录:
```bash
export RUST_LOG=solana_bpf_loader=trace
```
一般来说,我们正在使用 `debug` 处理不经常的调试消息, `trace` 处理可能频繁的消息, `info` 用于与性能相关的记录。
您也可以通过 GDB 附加到一个运行过程。 领导者进程命名为 _solana-validator_:
```bash
sudo gdb
attach <PID>
set logging on
thread apply all bt
```
这将把所有线程堆栈跟踪转储到 gdb.txt
## 开发者测试网
在此示例中,我们将把客户端连接到公共测试网。 在测试网上运行验证器,您需要打开 udp 端口 `8000-1000`
```bash
NDEBUG=1 ./multinode-demo/bench-tps.sh --entrypoint devnet.solana.com:8001 --faucet devnet.solana.com:9900 --duration 60 --tx_count 50
```
您可以在 [metrics dashboard](https://metrics.solana.com:3000/d/monitor/cluster-telemetry?var-testnet=devnet) 上观察客户端交易的影响

View File

@ -1,64 +0,0 @@
---
title: 生成分叉
---
本节描述了由于 [轮换领导者](leader-rotation.md) 而自然产生分叉的情况。
## 概览
节点会变为领导者并生成编码更改的 PoH。 群集可以通过综合领导者 _**可能**_ 生成的内容,来容忍与任何领导者的连接中断,但不会进行任何的状态更改。 因此可能的分叉数量仅限于“有/无”跳过在领导者轮换时,在插槽边界可能出现的分叉列表。 在任何指定的插槽上,只接受一名领导者的交易。
## 消息流
1. 交易被当前的领导者吸纳。
2. 领导者过滤有效的交易。
3. 领导人执行有效交易的状态更新。
4. 以当前 PoH 插槽为基础的领导者软件包交易记录。
5. 领导者将条目传送到验证节点 \(嵌入签名的碎片\)
1. PoH 流包括滴答计数;空白条目显示了领导者的主动性和集群时间的流逝。
2. 领导者传输首先是必要的滴答目录,以便 PoH 返回领导者最近观察到的上一个领导者插槽。
6. 验证节点将条目重新传输给他们集合中的对等点和未来的下游节点。
7. 验证节点验证交易并在其状态下执行它们。
8. 验证节点计算状态的哈希值。
9. 在特定时间, 例如特定的 PoH 滴答计数, 验证节点将投票传给领导者。
1. 投票是指计算状态的 PoH 滴答计数的哈希签名。
2. 投票也是通过 Gossip 进行传播的。
10. 领导者进行与任何其他交易相同的选票,并将投票广播到集群。
11. 验证节点观察他们的投票和该集群的所有选票。它
## 分区,分叉
在 PoH 滴答计数时,可能出现与投票相对应的分叉。 下一位领导者可能没有观察到最后一次投票,可能会用生成的虚拟 PoH 条目开始它们的插槽。 这些空白滴答是由集群中的所有节点按集群配置的每次滴答哈希 `Z` 生成的。
投票时间段中只能有两个版本的 PoH `T` 滴答的 PoH 并且有当前领导者生成的条目或仅进行滴答的 PoH。 "只滴答"的 PoH 版本可以被视为一个虚拟账本,集群中的所有节点都可以从上一个插槽的最后一次滴答产生。
验证节点可以在其它方面忽略分叉\(例如从错误的领导者\),或罚没产生分叉的领导者。
验证节点根据贪婪算法进行投票,以最大限度提高他们在 [Tower BFT](../implemented-proposals/tower-bft.md) 中描述的奖励。
### 验证节点视图
#### 时间进度
下面图表代表了验证器关于 PoH 流的视图,以及一段时间内可能的分叉。 L1、L2 等是领导者插槽, `E`s 代表领导者插槽中的领导者条目。 `x`s 代表仅滴答,时间流向图表下方。
![生成分叉](/img/fork-generation.svg)
注意,在同一个槽位的两个分叉上显示 `E` 是一个可罚没条件,因此一个验证节点观察 `E3``E3` 可以对 L3 进行罚没,该插槽安全地选择 `x`。 一旦验证节点承认了某个分叉,在该滴答下方的其他分叉可以被丢弃了。 对于任何插槽,验证节点只需要考虑一个单一的“有条目”链或一个由一位领导者提出的“只滴答”的链。 但由于多个虚拟条目与上一个插槽相连接,它们可能会重叠。
#### 时间分隔
将领导者轮换对 PoH 滴答计数视为集群编码状态的时间分隔非常有用。 下表列出了上述分叉树作为一个时间分隔的帐本。
| 领导者插槽 | L1 | L2 | L3 | L4 | L5 |
|:------- |:-- |:-- |:-- |:-- |:-- |
| 数据 | E1 | E2 | E3 | E4 | E5 |
| 自上次起的滴答 | | | | x | xx |
请注意,领导者插槽 L3 期间只接受来自领导者 L3 的数据。 如果 L3 没有观察到 L2 的数据L3 的数据可能包括回到 L2 以外的另一个插槽。 L4 和 L5 的传输包括“ticks to prev” PoH 条目。
网络数据流的这种结构允许节点将其准确保存到账本,用于重新播放、重新启动和检查点。
### 领导者视图
当新的领导者开始一个插槽时,它必须首先发送将新插槽与最近观察到并投票的插槽链接所需的任何 PoH \(ticks\)。 领导者提议的分叉将把目前的插槽连接到前一个分叉,而前一个分叉是该分叉的虚拟分叉。

View File

@ -1,88 +0,0 @@
---
title: 领导者轮换
---
在任何时候,集群都只需要一个验证节点来生成账本条目。 由于每次只有一个领导者,所有验证节点都能够重复相同的账本副本。 然而,一次只有一名领导者的缺点是,恶意领导者可能会审查选票和交易。 由于审查无法同网络丢弃数据包区分开,因此集群不能简单地选择某一个节点来长期担任领导者角色。 相反,集群通过轮换来决定哪个节点担任领导者,从而最大限度减少恶意领导者的影响。
每个验证节点使用下文描述的同一种算法来选择预期的领导者。 当验证节点收到一个新的签名账本条目时,可以肯定某条目是来自预期的领导者。 分配给每位领导者的插槽顺序称为 _leader schedule领导者安排表_
## 领导者轮换计划
一个验证节点会拒绝未经过 _插槽领导者_ 签名的区块。 所有插槽领导者的身份列表称为 _领导者安排表_。 领导者安排表是通过本地定期重新计算产生的。 它指派插槽领导者持续一段称为 _epoch纪元_ 的时间。 安排表必须早于它分配的时间段, 这样它保证了计算计划的账本状态最后能够确定。 该持续时间称为 _领导者安排表偏移时间_。 Solana 将偏移时间设置为直到下一个 epoch 的插槽持续时间。 也就是说,一个 epoch 的领导者计划通过上一个 epoch 开始时的账本状态来计算得到。 一个纪元的偏移量是比较随意的,并且假定时间足够长,使所有验证节点都将在生成下一个计划之前确定其账本状态。 集群可以选择缩短偏移时间,来缩短质押变化与领导者计划更新之间的时间。
在没有分区的情况下运行时间比一个 epoch 长的时候,只有在根分叉的 epoch 边界才能生成安排表。 由于安排表用于下一个纪元,因此在下一个纪元之前,任何质押给根分叉的新质押都不会被激活。 用于生成领导者计划的区块是跨过纪元边界的第一个区块。
如果分区比一个 epoch 时间短,集群将按以下方式运作:
1. 验证节点在投票时不断更新自己的根分叉。
2. 每次在纪元边缘的插槽高度的时候,验证节点将更新其领导者安排表。
例如:
Epoch 持续时间为 100 个插槽。 根分叉从在插槽高度 99 处计算的分叉更新为在插槽高度 102 处计算出来的。 由于故障,跳过了插槽高度分别为 100、101 的分叉。 新的领导者计划通过分叉在高度为 102 的位置计算出来。 在插槽 200 中它处于活动状态,直到再次更新。
不会存在共识不一致的情况,因为当群集的根节点通过 102 时,对群集进行投票的每个验证节点都跳过了 100 和 101。 无论采用哪种投票方式,所有验证节点都将提交为 102 或 102 后代的根。
### 带有纪元分区的领导者轮换计划。
领导者安排表偏移的持续时间与群集对正确的领导者安排表不一致的可能性有直接关系。
考虑以下几种假设情况:
两个分区正在生成每个区块的一半。 但它们两个都不是最终的大多数分叉。 两者都将跨过纪元 100 和 200而实际上并没有形成一个根因此将在整个集群范围内承诺一个新的领导者安排表。
在这种不稳定的情况下,存在多个有效的领导者计划。
- 这时候,系统将为直系父级在上一个纪元的每个分叉生成一个领导者安排表。
- 领导者安排表在后代分叉的下一个纪元开始后才有效,直到更新为止。
每个分区的日程表将在一个分区持续超过一个纪元之后发生变化。 因此,纪元持续时间应当设置为远大于插槽时间和分叉提交到根的预期长度。
在观察集群足够长的时间后,可以根据中位数分区持续时间及其标准偏差来选择领导者安排表偏移量。 例如,偏移量长于中位数分区持续时间再加上六个标准偏差,则可以将群集中账本计划不一致的可能性降低到百万分之一。
## 生产创世领导者安排表
创世配置声明第一个纪元的第一个领导者。 该领导者计划在前两个纪元结束,因为领导者计划也在下一个时期的插槽 0 中生成。 前两个纪元的长度也可以在创世配置中指定。 前几个纪元的最小长度必须大于或等于 [Tower BFT](../implemented-proposals/tower-bft.md) 中定义的最大回滚深度。
## 创世领导者安排表算法
领导者时间表通过预定义的种子生成。 过程如下:
1. 定期使用 PoH 滴答高度 \(单调递增的计数器\) 产生稳定的伪随机算法种子。
2. 在该高度下,对银行中所有具有领导者身份且已在集群配置的滴答中进行投票的抵押帐户进行抽样。 该示例被称为 _活动集合activate set_
3. 按质押权重对活动集进行排序。
4. 使用随机种子选择按质押加权的节点,来创建质押加权排序。
5. 在群集配置的一定滴答后,该排序开始生效。
## 安排表攻击矢量
### 种子
所选择的种子是可预测的,但是不存在偏差。 没有任何可怕的攻击会影响其结果。
### 活动集
领导者可以通过审查验证人的投票来使活动集偏差。 领导者可能通过两种方式来审查活动集:
- 忽略验证节点的投票
- 拒绝使用验证节点的票对区块进行投票
为了减少审查的可能性在_活动集采样期间_网络将在领导者安排表偏移量边界上计算活动集。 有效设置的采样持续时间足够长,从而让多个领导者收集到投票。
### 质押
领导者可以审查新的质押交易,或拒绝验证有新质押的区块。 这种攻击类似于对验证节点选票的审查。
### 验证节点操作密钥丢失
领导者和验证节点应使用临时密钥进行操作,而质押所有者授权验证节点通过委托来处理其质押。
群集应该能够能通过领导者和验证者丢失的所有临时密钥进行恢复,这可能是所有节点共享出现的常见软件漏洞。 质押所有人应该能够通过共同签署验证节点的投票直接进行表决,即使质押已经委托给验证节点。
## 追加条目
领导者时间表的生命周期称为一个 _纪元epoch_。 纪元又划分为多个 _插槽_,每个插槽的持续时间 `T` 称为 PoH 滴答时间。
领导者在其插槽期间发送条目。 在 `T` 滴答时间后,所有验证节点都将切换到下一个预定的领导者。 验证节点必须忽略在领导者指定的插槽之外发送的条目。
下一位领导者必须观察所有的 `T` 滴答,以便其建立自己的条目。 如果没有观察到条目\(领导者掉线\) 或条目无效 \(领导者故障或作恶\) 则下一个领导者必须滴答来填充前一个领导者的插槽。 请注意,下一位领导者应并行执行维修请求,并推迟发送滴答,直到确信其他验证节点也没有观察到上一位领导者的条目。 如果领导者错误地产生自己的滴答,那么跟随它的领导者必须替换其所有滴答。

View File

@ -1,36 +0,0 @@
---
title: 管理分叉
---
在插槽边界处,账本是可能会产生分叉的。 产生的数据结构形成一个称为 _块存储blockstore_ 的树。 验证器解释blockstore时它必须维护区块链中每个分叉的状态。 我们称每个实例为 _活跃分叉active fork_。 验证节点有责任对这些分叉进行权衡,从而最终选择一个分叉。
验证节点通过向该分叉的插槽负责人投票来选择一个分叉。 投票将让验证节点提交一个称为 _锁定期lockout period_的持续时间。 在该锁定期到期之前,验证节点无法对其他分叉进行投票。 随后,对同一分叉的每次投票都会将把锁定期延长一倍。 经过一些群集配置的投票数\(当前为32 \) 后锁定期的持续时间达到了所谓的_最长锁定期max lockout_。 在达到最长锁定期之前,验证节点可以选择一直等待,直到锁定期结束然后再投票给另一个分叉。 当它在另一个分叉上投票时,所执行称为 _回滚rollback_ 的操作,此时状态会及时回滚到共享检查点,然后跳转到刚刚投票分叉的初始位置。 分叉能够回滚的最大限度称为 _回滚深度rollback depth_。 回滚深度是达到最长锁定期所需的票数。 每当验证节点投票时,超出回滚深度的所有检查点都将无法访问。 也就是说,在任何情况下,验证节点回滚的限度都不用超过回滚深度。 因此,它可以安全地 _修剪prune_ 无法到达的分叉,并将超出回滚深度的所有检查点 _局限squash_ 于根检查点中。
## 活跃分叉
活跃的分叉是一系列检查点,其长度至少比回滚深度长一倍。 最短分叉的长度刚好为回滚深度的一倍。 例如:
![分叉](/img/forks.svg)
以下序列是 _活跃分叉_
- {4, 2, 1}
- {5, 2, 1}
- {6, 3, 1}
- {7, 3, 1}
## 修剪和挤压
验证节点可以对树中的任何检查点进行投票。 在上图中,就是除了树叶以外的每个节点。 投票后,验证节点修剪从比回滚深度更远的距离派生的节点,然后通过将其可以挤压到根部的任何节点,利用机会来最小化其内存使用量。
从上面的示例开始,回滚深度为 2考虑对 5 投票与对 6 投票。 首先,对 5 进行投票:
![修剪后的分叉](/img/forks-pruned.svg)
新的根为 2任何不为 2 后代的活跃分叉都会被修剪。
或者,对 6 进行投票:
![分叉](/img/forks-pruned2.svg)
该树的根始终为 1因为从 6 开始的活跃分叉距它仅有 2 个检查点。

View File

@ -1,42 +0,0 @@
---
title: Solana 集群
---
Solana 集群是一组验证人,共同为客户交易服务并保持账本的完整性。 可能存在着多个集群。 当两个集群都来自同一个创世区块时,它们会趋向收敛。 否则,他们就会忽略了另一个集群的存在。 发送到错误集群的交易会慢慢被拒绝。 在本章节,我们将讨论创建集群、节点加入集群、共享账本、确保账本被复制以及处理有漏洞和恶意的节点。
## 创建一个集群
在启动任何验证节点之前首先需要创建一个_创世配置_。 该配置引用了两个公钥一个_铸造_和一个_引导验证节点_。 拥有引导验证节点私钥的验证节点负责将第一个条目附加到账本。 它使用铸造帐户初始化其内部状态。 该帐户将保存由创世配置定义的原生代币数。 然后,第二个验证节点联系引导验证节点,来注册为一个 _验证节点_。 然后,其他验证节点将在集群的任何已注册成员中注册。
验证节点会收到领导者的所有条目,并提交投票以确认这些条目的有效性。 投票后,验证节点需要存储这些条目。 一旦验证节点发现存在足够多的副本,它将删除自身的副本。
## 加入一个集群
验证节点通过发送到_控制台control plane_的注册消息进入集群。 控制台使用 _八卦gossip_ 协议实现,这意味着节点可以向任何现有节点注册,并期望其注册传播到集群中的所有节点。 所有节点同步所需的时间与参与群集节点数的平方成正比。 从算法上讲,人们都认为它非常慢,但是牺牲时间所换来的,就是一个节点可以确保它最终拥有与每个其他节点相同的信息,并且任何一个节点都无法审查该信息。
## 将交易发送到集群
客户端将交易发送到任何验证节点的交易处理单元\(TPU\) 端口。 如果该节点处于验证节点角色,则它将交易转发给指定的领导者。 如果处于领导者角色则该节点将传入的事务捆绑在一起对其打上时间戳来创建一个_条目entry_然后将其推送到集群的 _数据中心data plane_。 进入数据中心后,交易将由验证节点进行验证,从而将交易有效地添加到账本中。
## 确认交易
Solana集群能够在亚秒级的时间内_确认confirmation_ 最多150个节点并计划扩展到成千上万个节点。 一旦完全实施,确认时间预计只会随着验证节点数量的对数而增加,而对数的基数又很高。 例如,如果基数为一千,则意味着对于前一千个节点,确认将是三个网络跃点的持续时间加上一个最慢验证节点进行投票所花费的时间。 对于接下来的一百万个节点,确认仅增加一个网络跃点。
Solana将确认定义为从领导者为新条目添加时间戳到达成账本的绝大多数投票为止的持续时间。
八卦网络增长到一定规模后,就会变得太慢而无法实现亚秒级确认。 将消息发送到所有节点所花费的时间与节点数的平方成正比。 如果区块链想要获得低确认率并尝试使用八卦网络来做到这一点,它将被迫集中到少数几个节点上。
可以使用以下技术组合来实现可扩展的确认:
1. 使用VDF样本对交易打上时间戳并签名。
2. 将交易分为几批,将每笔交易发送到单独的节点,同时
每个节点都与对等节点共享其批次。
3. 递归地重复上一步,直到所有节点都具有所有批次。
Solana以固定的时间间隔称为_插槽_轮换领导者。 每个领导者只能在其分配的时段内产生条目。 领导者因此对交易加上时间戳记,以便验证节点可以查找指定领导者的公钥。 然后,领导者对时间戳进行签名,以便验证节点验证签名,证明签名者是指定领导者公钥的所有者。
接下来,将交易分成批处理,以便节点可以将交易发送给多方,而无需进行多份复制。 例如如果领导者需要将60笔交易发送到6个节点则它将把60笔交易的集合分成10笔交易的批次并向每个节点发送一个交易。 这能够让领导者将60笔交易放在网络上而不是每个节点60笔交易。 接着,每个节点都与对等节点共享其批次。 一旦节点收集了全部6个批次它将重建60个交易的原始集合。
一批交易只能进行多次拆分,直至它变得非常小,信息头成为网络带宽的主要负载。 在撰写本文时该方法最多可扩展至约150个验证节点。 为了扩展到成千上万个验证节点,每个节点可以将与引导者节点相同的技术应用于另一组相同大小的节点。 我们称这种技术为 [_涡轮区块传播Turbine Block Propogation_](turbine-block-propagation.md)。

View File

@ -1,25 +0,0 @@
---
title: 性能指标
---
Solana群集性能的衡量标准是网络可以维持的每秒平均交易数\(TPS\)。 并且,交易需要多长时间才能由群集的大多数\(确认时间\) 确认。
每个群集节点都维护各种计数器,它们随着某些事件增加。 这些计数器会定期上传到一个云数据库。 Solana的指标仪表板获取这些计数器并计算性能指标将其显示在仪表板上。
## TPS
每个节点库运行时都维护一个已处理的交易计数。 仪表板首先计算集群中所有启用了指标的节点之间的交易中位数。 然后将中位数集群交易计数在2秒的时间段内取平均值并显示在TPS时间序列图中。 仪表板还显示了TPS均值、最大TPS和总交易计数统计信息这些统计信息都是根据交易次数的中位数计算得出的。
## 确认时间
每个验证器节点都维护一个可见的活跃账本分叉列表。 当节点已接收并处理了与该分叉相对应的所有条目时,该分叉被视为冻结。 当分叉获得了累积的绝大多数投票,并且其中一个子分叉被冻结时,该分叉将被视为已确认。
节点为每个新的分叉分配一个时间戳,并计算确认分叉所花费的时间。 此时间在性能指标中即为验证节点确认时间。 性能仪表板将每个验证节点的确认时间的平均值显示为时间序列图。
## 硬件设置
验证节点软件已部署到配有1TB pd-ssd磁盘和2x Nvidia V100 GPU的GCP n1-standard-16实例。 它们部署在us-west-1地区。
solana-bench-tps从网络在配有n1-standard-16 CPU实例的客户机的网络收敛之后开始计算该客户机参数如下 `--tx\_count=50000 --thread-batch-sleep 1000`
在bench-tps重新开始阶段的5分钟内TPS和确认时间指标从仪表板编号中获取。

View File

@ -1,18 +0,0 @@
---
title: Solana 集群 RPC 端点
---
Solana 维护专用的 API 节点来完成 [JSON RPC](developing/clients/jsonrpc-api.md) 对每个公共集群的请求,第三方同样可以提供托管 API 节点服务。 以下为目前可用的公共 RPC 端点,推荐给每个公共集群:
## Devnet开发者网络
- `https://api.devnet.solana.com` - 单个 Solana 托管的 api 节点;限定频率
## Testnet测试网
- `https://api.testnet.solana.com` - 单个 Solana 托管的 api 节点;限定频率
## Mainnet Beta主网 Beta
- `https://api.mainnet-beta.solana.com` - Solana 托管的 api 节点集群,由负载平衡器支持;限定频率
- `https://solana-api.projectserum.com` - Project Serum 托管的 api 节点

View File

@ -1,194 +0,0 @@
---
title: 质押委托和奖励
---
质押者因帮助验证账本而获得奖励。 他们通过将其质押委托给验证节点来做到这一点。 这些验证节点会进行繁重的工作,广播账本,并将选票发送到每个节点的投票帐户(其中质押者可以委托其质押)。 当出现分叉时,集群的其余部分使用那些质押加权投票来选择一个区块。 验证节点和质押者都需要某种经济激励来发挥作用。 验证节点的硬件需要得到补偿,质押者需要获得奖励来降低其质押风险。 该部分设计详见[质押奖励](../implemented-proposals/staking-rewards.md)。 本章节将描述其实现的基本机制。
## 基本设计
通常的想法是验证节点有一个投票帐户。 投票帐户跟踪验证节点的投票,对验证节点产生的信用进行计数,并提供其他任何针对验证节点的状态。 投票帐户无法悉知委托给它的任何质押,账号本身也没有质押。
单独的质押帐户 \(由质押者创建\) 命名了一个将质押委托的投票帐户。 所产生的奖励与质押的lamports数量成正比。 质押帐户仅由质押人拥有。 此帐户中存储的某些部分Lamport属于质押。
## 被动委托
无需控制投票帐户或向该帐户提交投票的身份进行交互操作,任何数量的质押帐户都可以委托给一个投票帐户。
可以通过将投票帐户pubkey作为 `StakeState::Stake::voter_pubkey` 的所有质押帐户的总和来计算分配给Vote帐户的总质押。
## 投票和质押账户
奖励过程分为两个链上程序。 投票程序解决了让质押处于罚没的问题状态。 质押计划是奖励池的托管人,提供被动委托。 当显示质押者的代表已参与验证账本时Stake程序负责向质押者和投票者支付奖励。
### 投票状态
VoteState是验证节点已提交给网络的所有投票的当前状态。 VoteState包含以下状态信息
- `投票` - 提交的投票数据结构。
- `积分` - 该投票程序在其整个生命期内产生的奖励总额。
- `root_slot` - 达到奖励所需的全部锁定的最后一个插槽。
- `佣金` - VoteState从质押者的Stake帐户获得的任何奖励中抽取的佣金。 这是奖励的百分比上限。
- Account::lamports - 佣金累计获得的lamports。 这些并不算作质押。
- `authorized_voter` - 只有该身份能提交投票。 此字段只能通过身份认证进行修改。
- `node_pubkey` - 在这个帐户中投票的 Solana 节点。
- `authorized_withdrawer` - 负责该账户lamports 实体的身份,独立于帐户地址和授权的投票签名者。
### VoteInstruction::Initialize\(VoteInit\)
- `account[0]` - RW - 选票状态。
`VoteInit` 带有新投票帐户的 `node_pubkey`, `authorized_porer`, `authorized_withdrawer`, 和 `commission`
其他投票状态成员处于默认状态。
### VoteInstruction::Authorize\(Pubkey, VoteAuthorize\)
根据VoteAuthorize参数\(`投票者``提款者`\),使用新的授权投票人或提款人更新帐户。 交易必须由投票帐户当前的` 授权投票人`或`授权提款人`签名。
- `account[0]` - RW - VoteState。 `VoteState::authorized_voter``authorized_withdrawer` 设置为 `Pubkey`.
### VoteInstruction::Vote\(Vote\)
- `account[0]` - RW - The VoteState。 `VoteState::lockouts``VoteState::credit` 是根据投票锁规则更新的,参考 [Tower BFT](../implemented-proposals/tower-bft.md)。
- `account[1]` - RO - `sysvar::slot_hash` 需要验证投票反对的一些 N 最近插槽及其哈希列表。
- `account[2]` - RO - `sysvar::clock` 当前的网络时间,以 slot、epoch 等表示。
### 质押状态StakeState
StakeState 通常为这个四种形式之一StakeState::Uninitialized、StakeState::Initialized、StakeState::Stake 以及 StakeState::RewardsPool。 质押中仅使用前三种形式,但是只有 StakeState::Stake 非常有趣。 所有奖励池都是在创始时创建的。
### StakeState::Stake
StakeState:: Stake 是**质押者**的当前委托首选项,并包含以下状态信息:
- Account::lamports - 可用于质押的 lamports。
- `stake` - 产生奖励的 \(受到预热和冷却的影响\)质押,总是小于或等于 Account::lampport。
- `voter_pubkey` - 把 lamport 委托给 VoteState 实例的 pubkey 。
- `credits_observed` - 在程序的整个生命周期内获得的总积分。
- `activated` - 激活/委托质押的epoch。 所有质押将在预热后计算在内。
- `deactivated` - 停用此质押的epoch在完全停用帐户之前需要一些冷却epoch才能提取质押。
- `authorized_staker` - 必须签名委托,激活和停用交易的实体的公钥。
- `authorized_withdrawer` - 负责该帐户实体的身份,独立于帐户的地址和授权质押者。
### StakeState::RewardsPool
为避免单个网络范围内的兑换锁定或争用在预先确定的密钥下创世的一部分包括256个奖励池每个密钥具有std::u64::MAX信用额度以便能够根据积分值满足赎回要求。
质押和奖励池是同一`质押`程序所拥有的帐户。
### StakeInstruction::DelegateStake
质押账户从初始化形式转移到StakeState::Stake形式或者从已停用(即完全冷却)的StakeState::Stake转变为激活的StakeState::Stake。 这是质押者选择其质押账户Lamport委托给的投票帐户和验证节点节点的方式。 交易必须由质押的`授权质押者`签名。
- `account[0]` - RW - StakeState::Stake 实例。 `StakeState::Stake::credits_observed` 已初始化到 `VoteState::credits``StakeState::Stake::voter_pubkey` 已初始化到 `account[1]`。 如果这是首次委托质押,`StakeState::Stake::stake` 会被初始化到账户的 lamports余额`StakeState::Stake::activated` 被初始化到 Bank epoch并且 `StakeState::Stake::deactivated` 被初始化到 std::u64::MAX
- `account[1]` - R - VoteState 实例。
- `account[2]` - R - sysvar::clock 账户,包含有关当银行时间的信息。
- `account[3]` - R - sysvar::stakehistory 帐户,包含有关质押历史的信息。
- `account[4]` - R - stake::Config 帐户,负责预热,冷却和罚没配置。
### StakeInstruction::Authorize\(Pubkey, StakeAuthorize\)
根据质押授权参数\(`质押者``提款人`\),使用新的授权质押者或提款人更新帐户。 交易必须由质押帐户当前的`授权质押人`或`授权提款者`签名。 任何质押锁定必须已到期,或者锁定托管人也必须签名交易。
- `account[0]` - RW - StakeState。
`StakeState::authorized_staker``authorized_withdrawer` 已设置为 `Pubkey`
### StakeInstruction::Deactivate
质押持有者可能希望从网络中提款。 为此,他必须首先停用自己的质押,然后等待冷却。 交易必须由质押的`授权质押者`签名。
- `account[0]` - RW - 读写正在停用的 StakeState::Stake 实例。
- `account[1]` - R - 带有当前时间的 Bank 的 sysvar::clock 帐户。
StakeState::Stake::deactivated 停用设置为当前时间+冷却时间。 到那个epoch帐户的质押将减少到零并且Account::lamports将可以提款。
### StakeInstruction::Withdraw\(u64\)
Lamports会随着时间在一个质押账户中累积超过已激活质押的任何多余部分都可以提取。 交易必须由质押的`授权提款人`签名。
- `account[0]` - RW - 需要取款的 StakeState::Stake。
- `account[1]` - RW - 应当计入已提取Lamport的帐户。
- `account[2]` - R - 带有当前时间的 Bank sysvar::clock 账户,用于计算质押。
- `account[3]` - R - 来自 Bank 的 sysvar::stake_history 帐户,具有质押预热/冷却历史记录。
## 这种设计的好处
- 所有质押者进行一次投票。
- 清除积分变量对于索取奖励不是必需的。
- 每个委派的质押都可以独立索取奖励。
- 当委托质押要求奖励时,将交纳工作佣金。
## 示例通话流
![被动质押调用流](/img/passive-staking-callflow.png)
## 质押Staking奖励
此处概述了验证者奖励制度的具体机制和规则。 通过将质押委托给正确投票的验证人来赚取奖励。 投票不正确会使验证者的质押面临[slashing罚没](../proposals/slashing.md)的风险。
### 基础知识
网络获得一部分网络[通胀](../terminology.md#inflation)奖励。 可用于支付时间奖励的Lamports数量是固定的并且必须根据它们的相对权重和参与度在所有质押节点之间平均分配。 加权单位称为[积分point](../terminology.md#point))。
一个epoch结束以后才能获得该epoch的奖励。
在每个epoch结束时将在该epoch期间获得的总积分求和并用于划分epoch通货膨胀的奖励求出一个积分值。 该值记录在将时间映射到点值的[sysvar](../terminology.md#sysvar)中。
在赎回期间质押计划会计算每个epoch的质押所赚取的点数再乘以该epoch的点值然后根据奖励账户的佣金设置将该金额的Lamports从奖励账户转移到质押和投票账户中。
### 经济学
一个epoch的积分取决于总的网络参与度。 如果参与epoch缩短则那些参与epoch的分值会更提高。
### 赚取积分
验证节点对于超出最大锁定范围的每一个正确投票都会获得一票积分,即,每次验证节点的投票帐户从其锁定列表中退出某个版位时,该投票就将成为该节点的根。
委派给该验证程序的抵押人根据其所持质押比例获得积分。 所获得的积分是投票信用和质押的乘积。
### 质押预热、冷却与取回
质押委托以后就不会立即生效。 他们必须首先经过一个预热期。 在此期间,质押的某些部分被视为“有效”,其余部分被视为“激活”。 变化发生在epoch边界上。
质押程序将更改总网络质押的速率限制在质押程序的`config::warmup_rate`中\(在当前实现中设置为每个epoch 25\)。
每个epoch可以预热的质押数量是前一个epoch的总有效质押总激活质押以及质押程序配置的预热率的函数。
冷却时间的工作方式相同。 取消抵押以后,某些部分将被视为“有效”,也被视为“停用”。 随着质押冷却,质押继续获得奖励并有罚没风险,但也可以取回。
引导质押则不需预热。
奖励是针对该epoch的“有效”质押部分进行支付的。
#### 预热示例
考虑在第 N 个 epoch 激活了 1,000 个单一质押的情况,网络预热率为 20第 N 个 epoch 的静态总网络质押为 2,000 个。
在第 N + 1 个阶段可激活的网络数量为400 \(2000的20%\),在第 N 个阶段,此示例质押是唯一激活的质押,因此有权使用所有的预热质押。
| epoch | 有效的 | 激活中 | 总有效 | 总激活中 |
|:----- | ----:| -----:| -----:| -----:|
| N-1 | | | 2,000 | 0 |
| N | 0 | 1,000 | 2,000 | 1,000 |
| N+1 | 400 | 600 | 2,400 | 600 |
| N+2 | 880 | 120 | 2,880 | 120 |
| N+3 | 1000 | 0 | 3,000 | 0 |
如果在epochN激活了2个质押(X和Y)他们将按其质押的比例获得20的一部分。 在每个epoch每个质押的有效和激活是前一个epoch的状态的函数。
| epoch | 有效 X | 激活 X | 有效 Y | 激活 Y | 总有效 | 总激活中 |
|:----- | ----:| -----:| ----:| ----:| -----:| -----:|
| N-1 | | | | | 2,000 | 0 |
| N | 0 | 1,000 | 0 | 200 | 2,000 | 1,200 |
| N+1 | 333 | 667 | 67 | 133 | 2,400 | 800 |
| N+2 | 733 | 267 | 146 | 54 | 2,880 | 321 |
| N+3 | 1000 | 0 | 200 | 0 | 3,200 | 0 |
### 提现
任何时候都只能提取超过有效+激活质押的Lamports。 这意味着在预热期间,实际上无法取回任何抵押。 在冷却期间,超过有效质押的任何代币都可能被取回\(activating == 0\)。 由于赚取的奖励会自动添加到质押中,因此通常只有在停用后才可以提现。
### 锁定
质押账户支持锁定的概念,直到指定的时间,提款账户的余额才能提现。 锁定指定为一个epoch高度即在可提取质押账户余额之前网络必须达到的最小epoch高度除非交易也由指定的托管人签署。 此信息在创建质押帐户时收集并存储在质押帐户状态的Lockup字段中。 更改授权的质押者或提款人也会受到锁定,因为这样的操作实际上就是转移代币。

View File

@ -1,28 +0,0 @@
---
title: 同步
---
快速、可靠的同步是 Solana 实现超高吞吐量的最大原因。 同步到包含大量交易的传统区块链被称为区块。 通过在区块上同步,某笔交易只能在一个被称为“区块时间”的时间范围内完成。 工作量证明共识的区块时间通常非常大 \(~10分钟\) 才能最大限度地减少多个验证节点同时生成一个新有效区块的概率。 权益证明共识则不存在这样的限制,但它缺乏可靠的时间戳,验证节点无法决定进入区块的顺序。 最常见的做法是通过 [挂钟时间戳](https://en.bitcoin.it/wiki/Block_timestamp) 来标记每个区块。 由于时钟偏差和网络延迟的波动,时间戳只能保持一两小时的准确性。 要是该系统顺利运行,它们会增长区块时间,以便合理地确定每个区块上的中位时间戳总在不断增加。
Solana 采取了非常独特的做法,称为 _历史证明__PoH_。 带有加密证明“时间戳”的领导节点能够证明自上次确认以来,确实已经过了一段时间。 所有哈希到证明中的数据肯定都是在证明之前发生的。 然后该节点将新区块分享给验证节点,它们能够验证这些证据。 区块可以按照任何顺序甚至延迟好几年才传到验证节点那里。 通过这种可靠的同步保证Solana 能够将区块分解成更小的批量交易,称为 _条目entries_。 在达成任何共识之前,条目都会实时传输给验证节点。
在技术的角度Solana 从来都没有发送 _区块_ 但是会使用这个词语来描述验证节点对条目进行投票,最终取得 _确认_。 这样Solana 的确认时间就可以达到媲美两天苹果设备之间传输的速度。 当前的实现区块时间为 800 毫秒。
在这个模式下,条目很快就能传输到验证节点,因为领导节点可以将一组有效的交易归入条目。 验证节点早在投票其有效性之前就处理了这些条目。 以乐观的方式处理交易, 从收到最后一个条目到节点可以投票的期间,实际上不存在延迟。 如果对某个事件 **无法** 达成共识,节点只需要简单地回滚其状态。 这种优化处理技术早在 1981 年就发明了,被称为 [Optimistic Concurrency Control最优同值控制](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.65.4735)。 它也可以应用到区块链结构中,其中一个集群对代表整个账本的哈希进行投票,直到达到某个 _区块高度_。 Solana 通过使用最后一个条目的 PoH 哈希来轻松实现这一点。
## 与 VDF 的关系
Solana 在 2017 年 11 月首次介绍了历史证明技术在区块链中的使用。 次年 6 月, 斯坦福阐述了类似的技术,称为 [verifiable delay function可验证延迟函数](https://eprint.iacr.org/2018/601.pdf) 或简称为 _VDF_
VDF 的一个优势是验证所需时间非常短。 Solana 验证其延迟函数的方法它与创建所需的时间成正比。 拆分超过 4000 个核心 GPU它足以满足 Solana 的性能需要,但如果您请教上述论文的作者,他们可能会跟说 Solana 的方法在算法上很慢 \([因此](https://github.com/solana-labs/solana/issues/388)\),不应该叫作 VDF。 我们的观点是 VDF 一词应当指可证明的延迟方程,而不一定要满足某些性能特征。 在这个问题解决之前Solana 很可能会继续在其特定的应用程序 VDF 使用 PoH 一词。
PoH 与 VDF 之间的另一个区别是VDF 仅用于跟踪时间。 另一方面PoH 的哈希区块链包括应用程序观察到的任何数据的哈希部分。 这些数据是一把双刃剑。 一方面,数据提供了“证明历史”――数据在哈希之后肯定是存在的。 另一方面这意味着当数据被哈希的_时候_应用程序可以通过更改它来操纵区块链。 因此PoH 区块链并不是一个很好的随机数来源,但是没有这种数据的 VDF 却非常合适。 例如Solana 的 [领导人轮换算法](synchronization.md#leader-rotation),只来源于 VDF _高度_ ,而不是该高度的哈希值。
## 与共识机制的关系
历史证明不是一个共识机制,它用于改进 Solana 权益证明共识的性能。 它还用于改进数据平面协议的性能。
## 关于历史证明的更多信息
- [水时钟类比](https://medium.com/solana-labs/proof-of-history-explained-by-a-water-clock-e682183417b8)
- [历史证明概述](https://medium.com/solana-labs/proof-of-history-a-clock-for-blockchain-cf47a61a9274)

View File

@ -1,88 +0,0 @@
---
title: 涡轮Turbine区块传播
---
Solana 集群使用一个叫做 _Turbine_ 的多层区块传播机制来广播所有节点的交易,该过程只产生少量的重复信息。 群集将自身分成小的节点集合,叫做 _neighborhoods邻居_。 每个节点负责与其附近的其他节点分享它收到的任何数据,同时将数据传播到其他邻居的小节点。 这样,每个节点只需要与少数节点通信。
在其插槽中,领导人节点会在第一个邻居的验证节点\(层 0\) 之间分配碎片。 每个验证节点在其邻居之间共享各自的数据,但也在下一层\(层 1\) 中将碎片转化为某个邻居的一个节点。 每个 1 层节点与邻居节点共享数据, 并在下一层重新传输到节点,以此类推,直到集群中的所有节点都收到了所有碎片。
## 邻居分配 - 权重选择
为了使数据扩散发挥作用,整个集群必须就如何将划分邻居达成一致。 为了实现这一点,所有公认的验证节点 \(TVU 节点\) 都按质押权重排列并存储在一个列表中。 然后以不同的方式索引这该列表,来查明邻居边界并相互重新传送。 例如,领导者只需选择第一个节点来构建层 0。 最高权重的质押持有者会让得票最多的节点优先成为领导者。 0 层和下层节点使用相同的逻辑来找到他们的邻居和下一个层节点。
为了减少攻击向量的可能性,每个碎片都向邻居发送一个随机树。 每个节点使用代表集群的同一批节点。 随机树是通过碎片集生成的它使用领导者ID、插槽和碎片索引来生成种子。
## 层和邻居结构
当前领导者将其初始广播到最多 `DATA_PLANE_FANOUT` 的节点。 如果该层 0 小于集群中的节点数,则数据平面扫描机制在下面添加图层。 随后的图层遵循这些约束来决定图层容量:每个邻居包含的 `DATA_PLANE_FANOUT` 节点。 0 层从带有扩散节点的 1 个邻居开始计算。 每个附加层中的节点数量增加了一个扩散因素。
如上文所述, 图层中的每个节点只能向邻居广播它的碎片,并且在某些下层邻居中只能播放一个节点, 不适用于集群中的每个 TVU 节点。 有一个理解该问题的好方法0 层从带有扩散节点的 1 个邻居开始计算1 层增加扩散邻居, 每个带有扩散节点和 2 层的节点都会有 `扩散 * 在 1 层的节点数量`,以此类推。
这样,每个节点进行通信最大只能达到 `2 * DATA_PLANE_FANOUT - 1` 个节点。
下面的图表显示了领导人如何在 0 层向 0 令居发送两个扩散的碎片,以及 0 邻居的节点如何彼此分享他们的数据。
![领导者向 0 层中的 0 号邻居发送碎片](/img/data-plane-seeding.svg)
下面的图表显示了 Neighborhood 0 如何向 Neighborhood 1和2 进行扩散。
![Neighborhood 0 向Neighborhood 1 和 2 号散播](/img/data-plane-fanout.svg)
最后,下图显示两个层集群都有 2 个扩散。
![具有两个扩散的两层集群](/img/data-plane.svg)
### 设置值
`DATA_PLANE_FANOUT` - 确定层 0 的大小。 后续的层由 `DATA_PLANE_FANOUT` 系数生成。 邻居的节点数量等于扩散值。 某个邻居要在添加新邻居之前被填满即如果某个邻居没有满它_必须_是最后一个。
目前,集群启动的时候就进行了配置。 未来这些参数可能会托管在链上,从而能够随着群组大小的变化而自行调整。
## 计算所需的 FEC 比率
Turbine 依赖于验证器之间数据包的再传输。 由于重新传输,任何网络宽包丢失都会加重, 同时每次访问的数据包未能到达目的地的概率也会增加。 FEC 比率需要考虑网络宽数据包丢失和传播深度。
碎片组是可以使用重建彼此的数据和编码数据集。 每个碎片组都有失败的可能性,其概率大致相当于数据包错误率超过 FEC。 如果验证节点无法重建碎片组,那就无法构建区块,它就必须先修复区块。
碎片组失败的概率可以通过二项分布计算。 如果 FEC 率是 `16:4`, 那么群组大小则为 20而且至少要有 4 个碎片失败才会导致整个组失败。 这等于 20 个碎片中的四条或更多个体失败的概率之和。
涡轮区块成功的概率为:
- 数据包失败的概率为: `P = 1 - (1 - network_packet_loss_rate)^2`
- FEC 率: `K:M`
- 尝试次数: `N = K + M`
- 碎片组失败率: `S = SUM of i=0 -> M for binomial(prob_failure = P, trials = N, failures = i)`
- 每个区块的碎片: `G`
- 区块成功率: `B = (1 - S) ^(G / N)`
- `i` 的精确结果的双向分布被定义为 `(N choose i) * P^i * (1 - P)^(N-i)`
例如:
- 网络丢包率为 15%。
- 50k Tps 的网络每秒产生 6400个碎片。
- FEC 率增加了每个块按 FEC 笔录的总碎片数。
其中 FEC 率为: `16:4`
- `G = 8000`
- `P=1 - 0.85 * 0.85 = 1 - 0.7225 = 0.2775`
- `S = i 的总和 =0 -> 4 为二项分布(prob_fail= 0.2775, trials = 20, fails = i) = 0.689414`
- `B = (1 - 0.689) ^(8000 / 20) = 10^-203`
其中 FEC 率为: `16:16`
- `G = 12800`
- `S = i 的总和 =0 -> 32 为二项分布(prob_fail= 0.2775, trials = 64, fails = i) = 0.002132`
- `B = (1 - 0.002132) ^(12800 / 32) = 0.42583`
其中 FEC 率为: `32:32`
- `G = 12800`
- `S = i 的总和 =0 -> 32 为二项分布(prob_fail= 0.2775, trials = 64, fails = i) = 0.000048`
- `B = (1 - 0.000048) ^(12800 / 64) = 0.99045`
## 邻居Neighborhoods
以下图表显示不同层中两个邻居之间的相互作用。 若要使邻居瘫痪,就需要从上面的邻居建立足够的节点\(擦除代码+1\)。 由于每个邻居都会从上层一个邻居的多个节点收到碎片, 因此上层网络出现大规模故障,才会产生不完整的数据。
![邻居的内部工作](/img/data-plane-neighborhood.svg)

View File

@ -1,59 +0,0 @@
---
title: 安全投票签名
---
验证节点接收当前领导者的条目并提交这些条目的确认票数。 这个投票对安全提出了一种挑战,因为违反共识规则的伪造票可以用来罚没验证节点的质押。
验证节点通过一个采用不对称密钥来验证签名结果的交易,来对其选定的分叉进行投票。 其他实体可以使用验证节点的公钥验证此签名。 如果验证节点密钥被用来签名不正确的数据\(例如) 对账本的多个分叉进行投票\,那么节点的质押或资源可能会受到损害。
解决这个风险的办法是将单独的 _投票签名者_ 服务分割出来,评估每次投票以确保它没有违反罚没条件。
## 验证节点,投票签名者和质押者
当一个验证节点收到多个区块的同一个插槽时,它会跟踪所有可能的分叉,直到确定一个“最佳”的。 验证节点通过投票选择最好的分叉,通过投票签名者尽量减少投票造成违反共识和导致质押被罚没的可能性。
投票签名者对验证节点提议的投票进行评估,并且只在不违反罚没条件的情况下进行投票。 一个投票签名者只需要保持它所签署的投票和其余集群成员所签署的选票处于最低状态。 它不需要处理全部的交易。
质押者是一个对其质押资产具有控制权的实体。 质押者可以将其质押委托给投票签署人。 一旦某个质押被委托,投票人将代表所有受委托质押行驶投票权,同时为他们带来奖励。
目前,验证节点和投票签名者之间是 1:1 的关系,质押人将他们的全部质押委托给某一个的签名者。
## 签名服务
投票签名服务由 JSON RPC 服务器和请求处理器组成。 启动时,服务会在配置端口启动 RPC 服务器,等待验证节点请求。 请求类型包括如下几种:
1. 注册一个新的验证节点
- 请求必须包含验证节点身份\(公钥\)
- 请求必须通过验证节点的私钥签名
- 如果无法验证请求签名,服务就会丢弃该请求
- 服务为验证节点创建了一个新的投票权不对称密钥,并返回公钥作为响应
- 如果验证节点试图再次注册,服务会从原有的密钥对返回公钥
2. 签名投票
- 请求必须包含投票交易和所有验证数据
- 请求必须通过验证节点的私钥签名
- 如果无法验证请求签名,服务就会丢弃该请求
- 服务验证投票数据
- 服务返回交易的签名
## 验证节点投票
某个验证节点在启动时创建一个新的投票帐户,并通过提交一个新的“投票登记表”交易,在集群中进行注册。 集群中的其他节点处理该笔交易,并在活动集合中包含新的验证节点。 随后,验证节点在每次投票事件中提交了由验证节点投票私钥签名的“新投票”交易。
### 配置
验证节点通过签名服务的网络端点\(IP/port\) 配置。
### 注册
在启动时,验证节点使用 JSON RPC 注册其签名服务。 RPC 调用返回验证节点的投票公钥。 验证节点创建一个新的“投票注册”交易并把公钥包含进去,将其提交给集群。
### 投票收集
验证节点在上次投票期间查找集群中所有节点提交的票数。 然后把该信息提交给签名服务,并附有新的投票签名请求。
### 新的投票签名
验证节点创建了一笔“新投票”交易,并通过 JSON RPC 将其发送到签名服务。 RPC 请求还包括了投票检查数据。 如果成功RPC 调用将返回投票的签名。 如果失败RPC 调用将返回故障编码。

View File

@ -1,149 +0,0 @@
---
title: Solana 集群
---
Solana维护着几个不同用途的集群。
在开始之前,请确保已首先安装了[Solana命令行工具](cli/install-solana-cli-tools.md)
浏览器:
- [http://explorer.solana.com/](https://explorer.solana.com/)。
- [http://solanabeach.io/](http://solanabeach.io/).
## Devnet开发者网络
- Devnet可以作为希望将Solana进行测试的任何人用户代币持有者应用开发者或验证者的游乐场。
- 应用程序开发人员应针对Devnet。
- 潜在的验证者应首先针对Devnet。
- Devnet和Mainnet Beta之间的主要区别
- Devnet代币是**不是真实的**
- Devnet包含用于空投的代币龙头用于应用程序测试
- Devnet可能会重置账本
- Devnet通常运行比Mainnet Beta更新的软件版本
- Devnet的八卦入口点`entrypoint.devnet.solana.com8001`
- Devnet的指标环境变量
```bash
export SOLANA_METRICS_CONFIG="host=https://metrics.solana.com:8086,db=devnet,u=scratch_writer,p=topsecret"
```
- Devnet RPC URL`https://api.devnet.solana.com`
##### 示例 `solana` 命令行配置
```bash
solana config set --url https://api.devnet.solana.com
```
##### 示例 `solana-validator` 命令行
```bash
$ solana-validator \
--identity ~/validator-keypair.json \
--vote-account ~/vote-account-keypair.json \
--trusted-validator dv1LfzJvDF7S1fBKpFgKoKXK5yoSosmkAdfbxBo1GqJ \
--no-untrusted-rpc \
--ledger ~/validator-ledger \
--rpc-port 8899 \
--dynamic-port-range 8000-8010 \
--entrypoint entrypoint.devnet.solana.com:8001 \
--expected-genesis-hash EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG \
--wal-recovery-mode skip_any_corrupted_record \
--limit-ledger-size
```
`--trusted-validator`由 Solana 运行
## Testnet测试网
- Testnet是我们在实时群集上重点测试最新发布功能的地方尤其侧重于网络性能稳定性和验证程序行为。
- 集群[Tour de SOL](tour-de-sol.md)计划在Testnet上运行在该计划中我们接受恶意行为和对网络的攻击以帮助我们发现和消除错误或网络漏洞。
- Testnet代币**不是真实的**
- Testnet可能会重置账本。
- Testnet包括用于空投的代币水龙头用于应用程序测试
- Testnet通常运行比Devnet和Mainnet Beta都更新的软件版本
- 测试网 Gossip 入口: `entrypoint.testnet.solana.com:8001`
- Testnet的指标环境变量
```bash
export SOLANA_METRICS_CONFIG="host=https://metrics.solana.com:8086,db=tds,u=testnet_write,p=c4fa841aa918bf8274e3e2a44d77568d9861b3ea"
```
- Testnet 的 RPC URL: `https://api.testnet.solana.com`
##### 示例 `solana` 命令行配置
```bash
solana config set --url https://api.testnet.solana.com
```
##### 示例 `solana-validator` 命令行
```bash
$ solana-validator \
--identity ~/validator-keypair.json \
--vote-account ~/vote-account-keypair.json \
--trusted-validator 5D1fNXzvv5NjV1ysLjirC4WY92RNsVH18vjmcszZd8on \
--trusted-validator ta1Uvfb7W5BRPrdGnhP9RmeCGKzBySGM1hTE4rBRy6T \
--trusted-validator Ft5fbkqNa76vnsjYNwjDZUXoTWpP7VYm3mtsaQckQADN \
--trusted-validator 9QxCLckBiJc783jnMvXZubK4wH86Eqqvashtrwvcsgkv \
--no-untrusted-rpc \
--ledger ~/validator-ledger \
--rpc-port 8899 \
--dynamic-port-range 8000-8010 \
--entrypoint entrypoint.testnet.solana.com:8001 \
--expected-genesis-hash 4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY \
--wal-recovery-mode skip_any_corrupted_record \
--limit-ledger-size
```
`--trusted-validator` 的身份是:
- `5D1fNXzv5NjV1ysLjirC4WY92RNsVH18vjmcszZd8on` - testnet.solana.com (Solana)
- `ta1Uvfb7W5BRPrdGnhP9RmeCKzBySGM1hTE4rBRy6T` - Break RPC 节点 (Solana)
- `Ft5fbkqNa76vnsjYNwjDZUXoTWpP7VYm3mtsaQckQADN` - Certus One
- `9QxCLckBiJc783jnMvXZubK4wH86Eqqvashtrwvcsgkv` - Algo|Stake
## Mainnet Beta主网 Beta
适用于早期代币持有者和启动合作伙伴,未经许可的持久集群。 当前,奖励和通货膨胀被禁用。
- 在Mainnet Beta上发行的代币是**真实的**SOL
- 如果您通过我们的硬币清单拍卖等方式支付了购买/发行代币的费用则这些代币将在Mainnet Beta上转移。
- 注意:如果您使用的是非命令行钱包,例如集群[Solflare](wallet-guide/solflare.md)则该钱包将始终连接到Mainnet Beta。
- Mainnet Beta 的 Gossip 入口: `entrypoint.mainnet-beta.solana.com:8001`
- Mainnet Beta的指标环境变量
```bash
export SOLANA_METRICS_CONFIG="host=https://metrics.solana.com:8086,db=mainnet-beta,u=mainnet-beta_write,p=password"
```
- Mainnet Beta 的 RPC URL `https://api.mainnet-beta.solana.com`
##### 示例 `solana` 命令行配置
```bash
solana config set --url https://api.mainnet-beta.solana.com
```
##### 示例 `solana-validator` 命令行
```bash
$ solana-validator \
--identity ~/validator-keypair.json \
--vote-account ~/vote-account-keypair.json \
--trusted-validator 7Np41oeYqPefeNQEHSv1UDhYrehxin3NStELsSKCT4K2 \
--trusted-validator GdnSyH3YtwcxFvQrVVJMm1JhTS4QVX7MFsX56uJLUfiZ \
--trusted-validator DE1bawNcRJB9rVm3buyMVfr8mBEoyyu73NBovf2oXJsJ \
--trusted-validator CakcnaRDHka2gXyfbEd2d3xsvkJkqsLw2akB3zsN1D2S \
--no-untrusted-rpc \
--ledger ~/validator-ledger \
--rpc-port 8899 \
--private-rpc \
--dynamic-port-range 8000-8010 \
--entrypoint entrypoint.mainnet-beta.solana.com:8001 \
--entrypoint entrypoint2.mainnet-beta.solana.com:8001 \
--entrypoint entrypoint3.mainnet-beta.solana.com:8001 \
--entrypoint entrypoint4.mainnet-beta.solana.com:8001 \
--entrypoint entrypoint5.mainnet-beta.solana.com:8001 \
--expected-genesis-hash 5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d \
--wal-recovery-mode skip_any_corrupted_record \
--limit-ledger-size
```
所有的四个 `--trusted-validator可信验证节点` 由 Solana 运行

View File

@ -1,115 +0,0 @@
---
title: 后向兼容政策
---
随着Solana开发人员生态系统的发展围绕打破API和行为更改影响为Solana构建的应用程序和工具的明确期望也将越来越高。 在一个完美的世界中Solana的开发可以以非常快的速度继续进行而不会给现有开发人员造成任何问题。 但是,将需要做出一些妥协,因此,本文档尝试澄清和整理新发行版的过程。
### 期望值
- Solana软件版本包括APISDK和CLI工具(带有一些[exceptions](#exceptions))。
- Solana软件版本遵循语义版本控制更多详细信息请参见下文。
- 适用于`MINOR`版本的软件将与同一`MAJOR`版本上的所有软件兼容。
### 弃用过程
1. 在任何`PATCH`或`MINOR`版本中功能、API、端点等都可以标记为已弃用。
2. 根据代码升级的难度,某些功能将在几个发布周期内被弃用。
3. 在未来的`MAJOR`版本中,不赞成使用的功能将以不兼容的方式删除。
### 发布时间
Solana RPC API、Rust SDK、CLI工具和BPF程序SDK均已更新并随每个Solana软件版本一起提供并且应始终在特定`MINOR`版本的`PATCH`更新之间兼容。
#### 发布频道
- `edge`软件,其中包含最先进的功能,没有向后兼容策略
- 在Solana Tour de SOL测试网集群上运行的`beta`软件
- 在Solana Mainnet Beta和Devnet集群上运行的`stable`软件
#### 主要版本(x.0.0)
`MAJOR`版本(例如2.0.0)可能包含重大更改并删除了以前不推荐使用的功能。 客户端SDK和工具将开始使用在先前`MAJOR`版本中启用的新功能和端点。
#### 次要版本(1.x.0)
新功能和建议实施已添加到_new_版本的`MINOR`版本(例如1.4.0)中并且首先在Solana的Tour de SOL测试网集群上运行。 在测试网上运行时,`MINOR`版本被认为在`beta`发布渠道中。 在对这些更改进行了修补并证明是可靠的之后,`MINOR`版本将升级到`stable`发布渠道并部署到Mainnet Beta集群。
#### 修补程序版本(1.0.x)
低风险功能,不间断的更改以及安全性和错误修复作为`PATCH`版本发行版(例如1.0.11)的一部分提供。 补丁可以同时应用于`beta`和`stable`发布渠道。
### RPC API
补丁发布:
- Bug修复
- 安全修复
- 端点/功能弃用
次要版本:
- 新的RPC端点和功能
主要版本:
- 删除过时的功能
### Rust Crates
* [`solana-sdk`](https://docs.rs/solana-sdk/) - 用于创建交易和解析帐户状态的Rust SDK
* [`solana-program`](https://docs.rs/solana-program/) - 用于编写程序的Rust SDK
* [`solana-client`](https://docs.rs/solana-client/) - 用于连接RPC API的Rust客户端
* [`solana-cli-config`](https://docs.rs/solana-cli-config/) - 用于管理Solana CLI配置文件的Rust客户端
补丁发布:
- Bug修复
- 安全修复
- 性能提升
次要版本:
- 新的 API
主要版本:
- 删除过时的API
- 向后不兼容的行为更改
### CLI 工具
补丁发布:
- 错误和安全修复
- 性能提升
- 弃用子命令/参数
次要版本:
- 新的子命令
主要版本:
- 切换到先前主要版本中引入的新RPC API端点/配置。
- 删除过时的功能
### Runtime功能
Solana新的runtime功能已进行功能切换并手动激活。 Runtime功能包括引入新的本机程序sysvars和syscalls并改变他们的行为。 功能激活与群集无关因此可以在Mainnet-beta上激活之前在Testnet上建立置信度。
发布过程如下:
1. 新版本中包含新的runtime功能默认情况下已禁用
2. 一旦有足够的抵押验证程序升级到新版本就可以通过指令手动激活runtime功能开关
3. 该功能在下一个纪元开始时生效
### 基础架构变更
#### 公共API节点
Solana提供了公开可用的RPC API节点供所有开发人员使用。 Solana团队将尽最大努力将任何更改传达给主机端口限速行为可用性等。 但是我们建议开发人员依靠自己的验证器节点来阻止对Solana操作的节点的依赖。
#### 本地集群脚本和Docker映像
重大更改将仅限于`MAJOR`版本更新。 `MINOR`和`PATCH`更新应始终向后兼容。
### 例外
#### Web3 JavaScript SDK
Web3.JS SDK还遵循语义版本控制规范但与Solana软件版本分开提供。
#### 攻击向量
如果在现有代码中发现了新的攻击媒介,则可以根据问题的严重性来规避上述过程,以便快速部署修复程序。

View File

@ -1,5 +0,0 @@
---
title: Web3 JavaScript API
---
请查看 [solana-web3](https://solana-labs.github.io/solana-web3.js/)。

View File

@ -1,72 +0,0 @@
---
title: "调试"
---
Solana程序在链上运行因此在链外调试可能会很困难。 为了使调试程序更容易开发人员可以编写单元测试以通过Solana运行时直接测试其程序的执行情况或者运行允许RPC客户端与其程序进行交互的本地集群。
## 运行单元测试 {#running-unit-tests}
- [用Rust进行测试](developing-rust.md#how-to-test)
- [用C进行测试](developing-c.md#how-to-test)
## 记录 {#logging}
在程序执行期间,运行时,程序日志状态和错误消息均会出现。
有关如何从程序登录的信息,请参阅特定语言的文档:
- [从Rust程序记录日志](developing-rust.md#logging)
- [从C程序记录日志](developing-c.md#logging)
运行本地集群时,只要通过`RUST_LOG`日志掩码启用了日志日志就会写入stdout。 从程序开发的角度来看,仅关注运行时和程序日志,而不关注其余的集群日志会有所帮助。 为了专注于程序特定的信息,建议使用以下日志掩码:
`export
RUST_LOG=solana_runtime::system_instruction_processor=trace,solana_runtime::message_processor=info,solana_bpf_loader=debug,solana_rbpf=debug`
直接来自程序(而不是runtime) 的日志消息将以以下形式显示:
`Program log: <user defined message>`
## 错误处理 {#error-handling}
可以通过事务错误传达的信息量是有限的,但是有很多可能的失败点。 以下是可能的故障点,有关预期发生哪些错误以及在何处获取更多信息的信息:
- BPF加载程序可能无法解析程序这应该不会发生因为加载程序已经对程序的帐户数据进行了_最终处理_。
- `InstructionError::InvalidAccountData`将作为交易错误的一部分返回。
- BPF加载程序可能无法设置程序的执行环境
- `InstructionError::Custom(0x0b9f_0001)`将作为交易错误的一部分返回。 "0x0b9f_0001"是[`VirtualMachineCreationFailed`](https://github.com/solana-labs/solana/blob/bc7133d7526a041d1aaee807b80922baa89b6f90/programs/bpf_loader/src/lib.rs#L44)的十六进制表示形式。
- BPF加载程序可能在程序执行过程中检测到致命错误(紧急情况,内存冲突,系统调用错误等)。
- `InstructionError::Custom(0x0b9f_0002)`将作为交易错误的一部分返回。 "0x0b9f_0002"是[`VirtualMachineFailedToRunProgram`](https://github.com/solana-labs/solana/blob/bc7133d7526a041d1aaee807b80922baa89b6f90/programs/bpf_loader/src/lib.rs#L46)的十六进制表示。
- 程序本身可能返回错误
- `InstructionError::Custom(<user defined value>)`将被返回。 “用户定义的值”不得与任何[内置运行时程序错误](https://github.com/solana-labs/solana/blob/bc7133d7526a041d1aaee807b80922baa89b6f90/sdk/program/src/program_error.rs#L87)相冲突 。 程序通常使用枚举类型来定义从零开始的错误代码,因此它们不会冲突。
如果出现`VirtualMachineFailedToRunProgram`错误,则将有关失败原因的详细信息写入[程序的执行日志](debugging.md#logging)。
例如,涉及堆栈的访问冲突将如下所示:
`BPF程序4uQeVj5tqViQh7yWWGStvkEG1Zmhx6uasJtWCJziofM失败out of bounds
memory store (insn #615), addr 0x200001e38/8`
## 监控计算预算消耗 {#monitoring-compute-budget-consumption}
程序可以记录停止程序执行之前将允许的剩余计算单元数。 程序可以使用这些日志来包装希望分析的操作。
- [从Rust程序记录剩余的计算单元](developing-rust.md#compute-budget)
- [从C程序记录剩余的计算单元](developing-c.md#compute-budget)
有关更多信息,请参见[计算预算](developing/programming-model/runtime.md#compute-budget)。
## ELF转储 {#elf-dump}
可以将BPF共享对象的内部信息转储到文本文件中以更深入地了解程序的组成及其在运行时的工作方式。
- [创建Rust程序的转储文件](developing-rust.md#elf-dump)
- [创建C程序的转储文件](developing-c.md#elf-dump)
## 指令追踪 {#instruction-tracing}
在执行期间可以将运行时BPF解释器配置为记录每个执行的BPF指令的跟踪消息。 对于诸如精确指出导致内存访问冲突的运行时上下文之类的事情,这可能非常有用。
跟踪日志与[ELF转储](#elf-dump)一起可以提供更多参考(尽管跟踪会产生很多信息)。
要在本地集群中打开BPF解释器跟踪消息请将`RUST_LOG`中的`solana_rbpf`级别配置为`trace`。 例如:
`export RUST_LOG=solana_rbpf=trace`

View File

@ -1,11 +0,0 @@
---
title: "部署"
---
![SDK 工具](/img/sdk-tools.svg)
如上图所示,程序作者创建了一个程序,将它编译成包含 BFF 字节代码的 ELF 共享对象,然后包含一笔特殊的 _deploy_ 交易,将其上传到 Solana 集群。 群集通过一个 _program ID_ 将其提供给客户端。 程序 ID 是部署时指定的 _地址_,用于在后续交易中引用程序。
一旦部署成功,持有程序的账户将被标记为可执行,并且其账户数据变得永久不可篡改。 如果程序需要更改(功能、补丁等...),新程序必须部署到一个新程序 ID。
Solana 命令行接口支持部署程序,更多信息请见 [`deploy`](cli/usage.md#deploy-program) 命令行使用 文档。

View File

@ -1,131 +0,0 @@
---
title: "用 C 语言开发"
---
Solana 支持使用 C 和 C++ 语言编写链上的程序。
## 项目布局 {#project-layout}
C 项目规定如下:
```
/src/<program name>
/makefile
```
`makefile` 应该包含以下内容:
```bash
OUT_DIR := <path to place to resulting shared object>
include ~/.local/share/solana/install/active_release/bin/sdk/bpf/c/bpf.mk
```
Bpf-sdk可能不在上面指定的确切位置但是如果您根据[如何开发](#how-to-build)来设置环境,那么就是这样。
来看一下的 C 程序的[helloworld](https://github.com/solana-labs/example-helloworld/tree/master/src/program-c)示例。
## 如何开发 {#how-to-build}
首先设置环境:
- 从https://rustup.rs安装最新的Rust稳定版本
- 从https://docs.solana.com/cli/install-solana-cli-tools安装最新的Solana命令行工具
然后使用make构建
```bash
make -C <program directory>
```
## 如何测试 {#how-to-test}
Solana 使用 [Criterion](https://github.com/Snaipe/Criterion) 测试框架,并且在每次构建程序时都会执行测试,[如何开发](#how-to-build)。
要添加测试,请在源文件`test_<program
name>.c`旁边创建一个新文件,并使用标准测试用例填充它。 有关示例,请参见[helloworld C测试](https://github.com/solana-labs/example-helloworld/blob/master/src/program-c/src/helloworld/test_helloworld.c)或[Criterion文档](https://criterion.readthedocs.io/en/master),获取编写测试用例的信息。
## 程序入口点 {#program-entrypoint}
程序导出一个已知的入口点符号在调用程序时Solana运行时将查找并调用该入口点符号。 Solana支持多个[BPF加载程序版本](overview.md#versions),它们之间的入口点可能会有所不同。 程序必须为相同的加载器编写并部署。 有关更多详细信息,请参见[概览](overview#loaders)。
当前有两个受支持的加载器:[BPF加载器](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/bpf_loader.rs#L17)和[已弃用BFT加载器](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/bpf_loader_deprecated.rs#L14)。
它们都有相同的原始入口点定义,以下是运行时查找和调用的原始符号:
```c
extern uint64_t entrypoint(const uint8_t *input)
```
该入口点采用通用字节数组,其中包含序列化的程序参数(程序ID帐户指令数据等)。 为了反序列化参数,每个加载器都包含其自己的[帮助器函数](#Serialization)。
请参阅 [使用入口点的简单实例](https://github.com/solana-labs/example-helloworld/blob/bc0b25c0ccebeff44df9760ddb97011558b7d234/src/program-c/src/helloworld/helloworld.c#L37),来看看它们是如何配合使用的。
### 序列化 {#serialization}
请参阅[helloworld对反序列化功能的使用](https://github.com/solana-labs/example-helloworld/blob/bc0b25c0ccebeff44df9760ddb97011558b7d234/src/program-c/src/helloworld/helloworld.c#L43)。
每个加载程序都提供一个帮助程序功能,该功能将程序的输入参数反序列化为 C 类型:
- [BPF加载器反序列化](https://github.com/solana-labs/solana/blob/d2ee9db2143859fa5dc26b15ee6da9c25cc0429c/sdk/bpf/c/inc/solana_sdk.h#L304)
- [BPF 加载器已弃用的反序列化](https://github.com/solana-labs/solana/blob/8415c22b593f164020adc7afe782e8041d756ddf/sdk/bpf/c/inc/deserialize_deprecated.h#L25)
某些程序可能希望自己执行序列化,并且可以通过提供其自己的[原始入口点](#program-entrypoint)实现来实现。 请注意,提供的反序列化功能会将引用保留回序列化字节数组,以引用允许程序修改的变量(lamport帐户数据)。 这样做的原因是,在返回时,加载程序将读取这些修改,以便可以将其提交。 如果程序实现其自己的反序列化功能,则需要确保将程序希望进行的所有修改都写回到输入字节数组中。
有关加载程序如何序列化程序输入的详细信息,请参见[Input Parameter Serialization](overview.md#input-parameter-serialization)文档。
## 数据类型 {#data-types}
加载程序的反序列化助手函数将填充[SolParameters](https://github.com/solana-labs/solana/blob/8415c22b593f164020adc7afe782e8041d756ddf/sdk/bpf/c/inc/solana_sdk.h#L276)结构:
```c
/**
* 程序进入点输入数据被反序列化的结构。
*/
typef structt volt_
SolAccountInfo* ka; /** 指向SolAccountInfo阵列的指针 必须已经
指向一个 SolAccountInfos */
uint64_t ka_num; /** `ka`中的 SolAccountInfo 条目数 */
const uint8_t *数据; /** 指示数据指针*/
uint64_t data_len; /** 指令数据字节长度 */
const SolPubkey *program_id; /** 当前正在执行的程序 */
} Solameters;
```
“ ka”是指令引用帐户的有序数组并表示为[SolAccountInfo](https://github.com/solana-labs/solana/blob/8415c22b593f164020adc7afe782e8041d756ddf/sdk/bpf/c/inc/solana_sdk.h#L173)结构。 帐户在数组中的位置表示其含义例如在转移lamports时一条指令可以将第一个帐户定义为源将第二个帐户定义为目的地。
`AccountInfo`结构的成员是只读的,但`lamports`和`data`除外。 程序都可以根据[runtime执行策略](developing/programming-model/accounts.md#policy)对两者进行修改。 当一条指令多次引用相同的帐户时,数组中可能有重复的`SolAccountInfo`条目,但它们都指向原来的输入字节数组。 程序应谨慎处理这些情况,以避免对同一缓冲区的读/写重叠。 如果程序实现其自己的反序列化功能,则应注意适当地处理重复帐户。
`数据`是正在处理的[指令的指令数据](developing/programming-model/transactions.md#instruction-data)中的通用字节数组。
`program_id`是当前正在执行的程序的公钥。
## 堆Heap{#heap}
C 程序可以通过系统调用[`calloc`](https://github.com/solana-labs/solana/blob/c3d2d2134c93001566e1e56f691582f379b5ae55/sdk/bpf/c/inc/solana_sdk.h#L245)或者通过虚拟的 32 Kb heap 区域顶部实现它们自己的堆地址 x300000000。 堆区域也被 `calloc` 使用,因此如果一个程序实现了自己的堆,它不应该同时调用 `calloc`
## 日志 {#logging}
运行时提供了两个系统调用,这些系统调用将获取数据并将其记录到程序日志中。
- [`sol_log(const char*)`](https://github.com/solana-labs/solana/blob/d2ee9db2143859fa5dc26b15ee6da9c25cc0429c/sdk/bpf/c/inc/solana_sdk.h#L128)
- [`sol_log_64(uint64_t, uint64_t, uint64_t, uint64_t, uint64_t)`](https://github.com/solana-labs/solana/blob/d2ee9db2143859fa5dc26b15ee6da9c25cc0429c/sdk/bpf/c/inc/solana_sdk.h#L134)
[调试](debugging.md#logging) 章节有更多关于程序日志工作的信息。
## 计算预算 {#compute-budget}
使用系统调用[`sol_log_compute_units()`](https://github.com/solana-labs/solana/blob/d3a3a7548c857f26ec2cb10e270da72d373020ec/sdk/bpf/c/inc/solana_sdk.h#L140)记录包含剩余编号的消息暂停执行之前程序可能消耗的计算单元数。
相关的更多信息,请参见[计算预算](developing/programming-model/runtime.md#compute-budget)。
## ELF转储 {#elf-dump}
可以将BPF共享对象的内部信息转储到文本文件中以更深入地了解程序的组成及其在运行时的工作方式。 转储将包含ELF信息以及所有符号和实现它们的指令的列表。 一些BPF加载程序的错误日志消息将引用发生错误的特定指令号。 可以在ELF转储中查找这些引用以标识有问题的指令及其上下文。
创建一个转储文件:
```bash
$ cd <program directory>
$ make dump_<program name>
```
## 示例 {#examples}
[Solana 程序库github](https://github.com/solana-labs/solana-program-library/tree/master/examples/c)代码库包含了 C 语言的例子集合。

View File

@ -1,272 +0,0 @@
---
title: "用Rust开发"
---
Solana 支持使用[Rust](https://www.rust-lang.org/) 编程语言编写链上的程序。
## 项目布局 {#project-layout}
Solana Rust程序遵循典型的[Rust项目布局](https://doc.rust-lang.org/cargo/guide/project-layout.html)
```
/inc/
/src/
/Cargo.toml
```
但也必须包括:
```
/Xargo.toml
```
必须包含:
```
[target.bpfel-unknown-unknown.dependencies.std]
features = []
```
Solana Rust 程序可能会直接依赖于对方,以便在进行 [交叉程序调用](developing/programming-model/calling-between-programs.md#cross-program-invocations)时获得指令协助。 这样做时,重要的是不要拉入依赖程序的入口点符号,因为它们可能与程序本身的符号冲突。 为避免这种情况,程序应在 `Cargo.toml` 中定义一个 ` exclude_entrypoint `功能,并使用它来排除入口点。
- [定义特性](https://github.com/solana-labs/solana-program-library/blob/a5babd6cbea0d3f29d8c57d2ecbbd2a2bd59c8a9/token/program/Cargo.toml#L12)
- [排除入口点](https://github.com/solana-labs/solana-program-library/blob/a5babd6cbea0d3f29d8c57d2ecbbd2a2bd59c8a9/token/program/src/lib.rs#L12)
然后,当其他程序将此程序作为依赖项包括在内时,它们应该使用`exclude_entrypoint`功能来实现这一点。
- [不将入口点包含在内](https://github.com/solana-labs/solana-program-library/blob/a5babd6cbea0d3f29d8c57d2ecbbd2a2bd59c8a9/token-swap/program/Cargo.toml#L19)
## 项目依赖关系 {#project-dependencies}
至少Solana Rust程序必须引入[solana-program](https://crates.io/crates/solana-program)。
Solana BPF程序具有某些[限制](#Restrictions),可能会阻止将某些箱体作为依赖项包含进来或需要特殊处理。
例如:
- 要求架构的箱体Crates是官方工具链支持箱体的子集。 除非解决了这个问题并且没有将BPF添加到那些体系结构检查中否则没有解决方法。
- 箱体可能取决于Solana确定性程序环境中不支持的`rand`。 要包含`rand`相关的箱体,请参考[在 Rand 开发](#depending-on-rand)。
- 即使程序本身未包含堆栈溢出代码,箱体也可能会使堆栈溢出。 有关的更多信息,请参见[Stack](overview.md#stack)。
## 如何开发 {#how-to-build}
首先设置环境:
- 从https://rustup.rs/安装最新的Rust稳定版本
- 从https://docs.solana.com/cli/install-solana-cli-tools安装最新的Solana命令行工具
正常的cargo构建可用于针对您的主机构建程序该程序可用于单元测试
```bash
$ cargo build
```
要为可部署到集群的Solana BPF目标构建一个特定的程序例如SPL代币请执行以下操作
```bash
$ cd <the program directory>
$ cargo build-bpf
```
## 如何测试 {#how-to-test}
通过直接行使程序功能,可以通过传统的`cargo test`机制对Solana程序进行单元测试。
为了帮助在更接近实时集群的环境中进行测试,开发人员可以使用[`program-test`](https://crates.io/crates/solana-program-test)箱体。 `程序测试`箱体将启动运行时的本地实例,并允许测试发送多个事务,同时在测试期间保持状态。
有关更多信息,请参见[在sysvar示例中测试](https://github.com/solana-labs/solana-program-library/blob/master/examples/rust/sysvar/tests/functional.rs)来学习如何包含一条指令syavar帐户由程序发送和处理。
## 程序入口点 {#project-entrypoint}
程序导出一个已知的入口点符号在调用程序时Solana运行时将查找并调用该入口点符号。 Solana支持多个[BPF加载程序版本](overview.md#versions),它们之间的入口点可能会有所不同。 程序必须为相同的加载器编写并部署。 有关更多详细信息,请参见[概览](overview#loaders)。
当前有两个受支持的加载器:[BPF加载器](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/bpf_loader.rs#L17)和[已弃用BFT加载器](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/bpf_loader_deprecated.rs#L14)。
它们都有相同的原始入口点定义,以下是运行时查找和调用的原始符号:
```rust
#[no_mangle]
pub unsafe extern "C" fn entrypoint(input: *mut u8) -> u64;
```
该入口点采用通用字节数组,其中包含序列化的程序参数(程序ID帐户指令数据等)。 为了反序列化参数,每个加载程序都包含其自己的包装宏,该宏导出原始入口点,反序列化参数,调用用户定义的指令处理函数并返回结果。
您可以在此处找到入口点宏:
- [BPF加载程序的入口点宏](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/entrypoint.rs#L46)
- [BPF 加载器不推荐使用的入口点宏](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/entrypoint_deprecated.rs#L37)
入口点宏调用的程序定义的指令处理功能必须具有以下形式:
```rust
pub type ProcessInstruction =
fn(program_id: &Pubkey, accounts: &[AccountInfo], instruction_data: &[u8]) -> ProgramResult;
```
请参阅 [使用入口点的简单实例](https://github.com/solana-labs/example-helloworld/blob/c1a7247d87cd045f574ed49aec5d160aefc45cf2/src/program-rust/src/lib.rs#L15),来看看它们是如何配合使用的。
### 参数反序列化 {#parameter-deserialization}
每个加载程序都提供一个帮助程序功能该功能将程序的输入参数反序列化为Rust类型。 入口点宏会自动调用反序列化帮助器:
- [BPF加载器反序列化](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/entrypoint.rs#L104)
- [BPF 加载器已弃用的反序列化](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/entrypoint_deprecated.rs#L56)
某些程序可能希望自己执行反序列化,并且可以通过提供其自己的[原始入口点](#program-entrypoint)实现来实现。 请注意,提供的反序列化功能会将引用保留回序列化字节数组,以引用允许程序修改的变量(lamport帐户数据)。 这样做的原因是,在返回时,加载程序将读取这些修改,以便可以将其提交。 如果程序实现其自己的反序列化功能,则需要确保将程序希望进行的所有修改都写回到输入字节数组中。
有关加载程序如何序列化程序输入的详细信息,请参见[Input Parameter Serialization](overview.md#input-parameter-serialization)文档。
### 数据类型 {#data-types}
加载程序的入口点宏使用以下参数调用程序定义的指令处理器功能:
```rust
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8]
```
程序ID是当前正在执行的程序的公钥。
帐户是指令引用的帐户的有序切片,并表示为[AccountInfo](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/account_info.rs#L10)结构。 帐户在数组中的位置表示其含义例如在转移lamports时一条指令可以将第一个帐户定义为源将第二个帐户定义为目的地。
`AccountInfo`结构的成员是只读的,但`lamports`和`data`除外。 程序都可以根据[runtime执行策略](developing/programming-model/accounts.md#policy)对两者进行修改。 这两个成员都受`RustRefCell`构造的保护,因此必须借用它们以对其进行读写。 这样做的原因是它们都指向原始输入字节数组,但是帐户片中可能有多个条目指向同一帐户。 使用`RefCell`确保程序不会通过多个`AccountInfo`结构意外地对相同的基础数据执行重叠的读/写操作。 如果程序实现其自己的反序列化功能,则应注意适当地处理重复帐户。
指令数据是正在处理的[指令的指令数据](developing/programming-model/transactions.md#instruction-data)中的通用字节数组。
## 堆Heap{#heap}
Rust程序通过定义自定义[`global_allocator`](https://github.com/solana-labs/solana/blob/8330123861a719cd7a79af0544617896e7f00ce3/sdk/program/src/entrypoint.rs#L50)直接实现堆。
程序可以根据其特定需求实现自己的`global_allocator`。 相关的更多信息,请参考[自定义heap示例](#examples)。
## 限制 {#restrictions}
链上Rust程序支持Rust的大多数libstdlibcore和liballoc以及许多第三方包装箱。
由于这些程序在资源受限的单线程环境中运行,因此存在一定的局限性,并且必须是确定性的:
- 无法访问
- `rand`
- `std::fs`
- `std::net`
- `std::os`
- `std::future`
- `std::net`
- `std::process`
- `std::sync`
- `std::task`
- `std::thread`
- `std::time`
- 有限的访问权:
- `std::hash`
- `std::os`
- 二进制代码在周期和调用深度上在计算上都非常昂贵,应该尽量避免。
- 应该避免字符串格式化,因为它在计算上也很昂贵。
- 不支持 `println!``print!`应该使用Solana [logging helpers](#logging)。
- 运行时对程序在一条指令的处理过程中可以执行的指令数施加了限制。 相关的更多信息,请参见[计算预算](developing/programming-model/runtime.md#compute-budget)。
## 在Rand开发 {#depending-on-rand}
程序必须确定性地运行,因此不能使用随机数。 有时,即使程序不使用任何随机数功能,程序也可能依赖于自己的`rand`。 如果程序依赖于`rand`则编译将失败因为对Solana没有对`get-random`进行支持。 报错通常如下所示:
```
error: target is not supported, for more information see: https://docs.rs/getrandom/#unsupported-targets
--> /Users/jack/.cargo/registry/src/github.com-1ecc6299db9ec823/getrandom-0.1.14/src/lib.rs:257:9
|
257 | / compile_error!("\
258 | | target is not supported, for more information see: \
259 | | https://docs.rs/getrandom/#unsupported-targets\
260 | | ");
| |___________^
```
要解决此依赖性问题,请将以下依赖性添加到程序的`Cargo.toml`中:
```
getrandom = { version = "0.1.14", features = ["dummy"] }
```
## 日志 {#logging}
Rust的`println`宏在计算上很昂贵,不被支持。 而是提供了辅助宏[`msg!`](https://github.com/solana-labs/solana/blob/6705b5a98c076ac08f3991bb8a6f9fcb280bf51e/sdk/program/src/log.rs#L33)。
`msg!` 有两种形式:
```rust
msg!("A string");
```
或者
```rust
msg!(0_64, 1_64, 2_64, 3_64, 4_64);
```
两者都将输出结果到程序日志。 如果程序愿意,他们可以使用`format!`来模拟`println!`
```rust
msg!("Some variable: {:?}", variable);
```
[debugging](debugging.md#logging)章节提供了有关使用程序日志的更多信息,[Rust示例](#examples)包含一个日志记录示例。
## 恐慌Panicking{#panicking}
默认情况下Rust 的`panic!`、`assert!`和内部恐慌结果被打印到[程序日志](debugging.md#logging)。
```
INFO solana_runtime::message_processor] Finalized account CGLhHSuWsp1gT4B7MY2KACqp9RUwQRhcUFfVSuxpSajZ
INFO solana_runtime::message_processor] Call BPF program CGLhHSuWsp1gT4B7MY2KACqp9RUwQRhcUFfVSuxpSajZ
INFO solana_runtime::message_processor] Program log: Panicked at: 'assertion failed: `(left == right)`
left: `1`,
right: `2`', rust/panic/src/lib.rs:22:5
INFO solana_runtime::message_processor] BPF program consumed 5453 of 200000 units
INFO solana_runtime::message_processor] BPF program CGLhHSuWsp1gT4B7MY2KACqp9RUwQRhcUFfVSuxpSajZ failed: BPF program panicked
```
### 自定义恐慌处理器 {#custom-panic-handler}
程序可以通过提供自己的实现来覆盖默认的紧急处理程序。
首先在程序的`Cargo.toml`中定义`custom-panic`功能。
```toml
[features]
default = ["custom-panic"]
custom-panic = []
```
然后提供应急处理程序的自定义实现:
```rust
#[cfg(all(feature = "custom-panic", target_arch = "bpf"))]
#[no_mangle]
fn custom_panic(info: &core::panic::PanicInfo<'_>) {
solana_program::msg!("program custom panic enabled");
solana_program::msg!("{}", info);
}
```
在上面的代码段中,显示了默认的实现,但是开发人员可以用更适合他们需求的东西代替它。
默认情况下支持完整的紧急消息的副作用之一是程序会产生将Rust的更多`libstd`实现引入程序共享对象的代价。 典型的程序将已经引入了相当数量的`libstd`,并且可能不会注意到共享对象大小的增加。 但是那些通过避免使用`libstd`显式地试图变得很小的程序可能会产生很大的影响(~25kb)。 为了消除这种影响,程序可以为自己的自定义应急处理程序提供空的实现。
```rust
#[cfg(all(feature = "custom-panic", target_arch = "bpf"))]
#[no_mangle]
fn custom_panic(info: &core::panic::PanicInfo<'_>) {
// Do nothing to save space
}
```
## 计算预算 {#compute-budget}
使用系统调用[`sol_log_compute_units()`](https://github.com/solana-labs/solana/blob/d3a3a7548c857f26ec2cb10e270da72d373020ec/sdk/program/src/log.rs#L102)]记录包含剩余编号的消息暂停执行之前程序可能消耗的计算单元数。
相关的更多信息,请参见[计算预算](developing/programming-model/runtime.md#compute-budget)。
## ELF转储 {#elf-dump}
可以将BPF共享对象的内部信息转储到文本文件中以更深入地了解程序的组成及其在运行时的工作方式。 转储将包含ELF信息以及所有符号和实现它们的指令的列表。 一些BPF加载程序的错误日志消息将引用发生错误的特定指令号。 可以在ELF转储中查找这些引用以标识有问题的指令及其上下文。
创建一个转储文件:
```bash
$ cd <program directory>
$ cargo build-bpf --dump
```
## 示例 {#examples}
[Solana 程序库github](https://github.com/solana-labs/solana-program-library/tree/master/examples/rust)代码库包含了Rust例子集合。

View File

@ -1,48 +0,0 @@
---
title: "示例"
---
## Hello World
Hello World是一个演示项目展示了如何使用Solana Javascript API以及Rust和C程序来构建、部署、与Solana区块链程序进行交互。
该项目包括:
- 链上的Hello World程序
- 向某个帐户发送 hello 并获取发送次数。
### 构建并运行
首先获取示例代码的最新版本:
```bash
$ git clone https://github.com/solana-labs/example-helloworld.git
$ cd example-helloworld
```
接下来按照git仓库[README](https://github.com/solana-labs/example-helloworld/blob/master/README.md)中的步骤进行操作。
## 中断
[Break](https://break.solana.com/)是一个React应用程序它使用户对Solana网络的速度和高性能有熟悉的感觉。 你能_干倒_Solana区块链吗 在15秒的游戏过程中每次单击按钮或击键都会向集群发送新的事务。 尽可能快地敲击键盘,并观看交易实时实时完成,而网络正在正常运行!
可以在我们的Devnet、Testnet和Mainnet Beta网络上来玩这个游戏。 在Devnet和Testnet可以免费测试用户可从网络水龙头获得空投测试代币。 在Mainnet Beta上用户每次玩耍需要支付0.08 SOL的费用。 可以通过本地密钥库钱包或通过扫描Trust Wallet的QR码以传输代币为测试帐户转入代币。
[点击这里玩这个游戏](https://break.solana.com/)
### 构建并运行
首先获取示例代码的最新版本:
```bash
$ git clone https://github.com/solana-labs/break.git
$ cd break
```
接下来按照git仓库的[README](https://github.com/solana-labs/break/blob/master/README.md)中的步骤进行操作。
## 特定语言
- [Rust](developing-rust.md#examples)
- [C](developing-c.md#examples)

View File

@ -1,61 +0,0 @@
---
title: "常见问题解答"
---
在编写或与Solana程序进行交互时经常会遇到一些常见的问题或困难。 以下是有助于回答这些问题的资源。
如果还没有解决您的问题那么Solana[#developers](https://discord.gg/RxeGBH)Discord频道是一个不错的资源。
## `CallDepth`错误
此错误意味着跨程序调用超出了允许的调用深度。
请参见[跨程序调用调用深度](developing/programming-model/calling-between-programs.md#call-depth)
## `CallDepthExceeded`错误
此错误表示已超出BPF堆栈深度。
请参阅[通话深度](overview.md#call-depth)
## 计算约束
请参见[计算约束](developing/programming-model/runtime.md#compute-budget)
## 浮动Rust类型
请参见[浮动支持](overview.md#float-support)
## Heap大小
请参见[heap](overview.md#heap)
## 无效账户数据
该程序错误的发生可能有很多原因。 通常,这是由于在指令中的错误位置或与正在执行的指令不兼容的帐户向程序传递了程序不期望的帐户所致。
当执行跨程序指令而忘记提供您正在调用的程序的帐户时,程序的实现也可能导致此错误。
## 无效指示数据
尝试反序列化指令时,可能会发生此程序错误,请检查传入的结构是否与指令完全匹配。 字段之间可能会有一些填充。 如果程序实现了Rust的`Pack`特性,则尝试打包和解压缩指令类型`T`以确定程序期望的确切编码:
https://github.com/solana-labs/solana/blob/v1.4/sdk/program/src/program_pack.rs
## MissingRequiredSignature
有些说明要求帐户必须是签名者;如果预计将对帐户进行签名但未签名,则返回此错误。
当执行需要签名程序地址的跨程序调用时,程序的实现也可能会导致此错误,但是传递的签名者种子将传递给[`invoke_signed`](developing/programming-model/calling-between-programs.md)与用于创建程序地址[`create_program_address`](developing/programming-model/calling-between-programs.md#program-derived-addresses)的签名者种子不匹配。
## `rand` Rust依赖导致编译失败
请参见[Rust项目依赖项](developing-rust.md#project-dependencies)
## Rust限制
请参见[Rust限制](developing-rust.md#restrictions)
## 堆栈大小
请参见[stack](overview.md#stack)

View File

@ -1,127 +0,0 @@
---
title: "概述"
---
开发人员可以编写自己的程序并将其部署到Solana区块链。
[Helloworld示例](examples.md#helloworld)是了解如何编写、构建、部署、与链上程序交互的入门材料。
## Berkley数据包过滤器(BPF)
Solana链上程序通过[LLVM编译器基础结构](https://llvm.org/)编译为[可执行和可链接格式(ELF)](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format),其中包含了[Berkley数据包过滤器(BPF)](https://en.wikipedia.org/wiki/Berkeley_Packet_Filter)字节码的变体。
由于Solana使用LLVM编译器基础结构因此可以使用可以针对LLVM的BPF后端的任何编程语言编写程序。 Solana当前支持用Rust和C / C ++编写程序。
BPF提供了有效的[指令集](https://github.com/iovisor/bpf-docs/blob/master/eBPF.md),可以在解释的虚拟机中执行,也可以作为高效的即时编译原生执行指令。
## 内存映射
Solana BPF程序使用的虚拟地址内存映射是固定的其布局如下
- 程序代码从0x100000000开始
- 堆栈数据从0x200000000开始
- 堆数据从0x300000000开始
- 程序输入参数从0x400000000开始
上面的虚拟地址是起始地址,但是程序可以访问存储器映射的子集。 如果程序尝试读取或写入未授予其访问权限的虚拟地址则会panic并且将返回`AccessViolation`错误,其中包含尝试违反的地址和大小。
## 堆栈 {#stack}
BPF使用堆栈帧而不是可变堆栈指针。 每个堆栈帧的大小为4KB。
如果程序违反了该堆栈帧大小,则编译器将报告溢出情况,作为警告。
例如:`Error: Function
_ZN16curve25519_dalek7edwards21EdwardsBasepointTable6create17h178b3d2411f7f082E
Stack offset of -30728 exceeded max offset of -4096 by 26632 bytes, please
minimize large stack variables`
该消息标识哪个符号超出了其堆栈框架但是如果它是Rust或C ++符号,则名称可能会被修饰。 要对Rust符号进行解码请使用[rustfilt](https://github.com/luser/rustfilt)。 上面的警告来自Rust程序因此已取消组合的符号名称为
```bash
$ rustfilt _ZN16curve25519_dalek7edwards21EdwardsBasepointTable6create17h178b3d2411f7f082E
curve25519_dalek::edwards::EdwardsBasepointTable::create
```
要对C ++符号进行解码请使用binutils中的`c++filt`。
报告警告而不提示错误的原因是,即使程序不使用该功能,某些从属工具也可能包含违反堆栈框架限制的功能。 如果程序在运行时违反了堆栈大小,则会报告`AccessViolation`错误。
BPF堆栈帧占用一个从0x200000000开始的虚拟地址范围。
## 调用深度
程序被限制为必须快速运行并且为了方便起见程序的调用堆栈被限制为最大深度为64帧。
## 堆Heap
程序可以直接在C中或通过Rust `alloc` API来访问运行时堆。 为了促进快速分配使用了一个简单的32KB凹凸堆。 堆不支持`free`或`realloc`,因此请慎重使用它。
在内部程序可以访问从虚拟地址0x300000000开始的32KB内存区域并且可以根据程序的特定需求实现自定义堆。
- [Rust程序堆使用情况](developing-rust.md#heap)
- [C程序堆使用情况](developing-c.md#heap)
## 浮点数支持
程序支持Rust的float操作的有限子集尽管由于涉及的开销而强烈不建议使用。 如果程序尝试使用不受支持的浮点运算,则运行时将报告未解决的符号错误。
## 静态可写入数据
程序共享对象不支持可写共享数据。 使用相同的共享只读代码和数据在多个并行执行之间共享程序。 这意味着开发人员不应在程序中包含任何静态可写变量或全局变量。 将来,可以添加写时复制机制以支持可写数据。
## 签名分配
BPF 指令集不支持 [签名分配](https://www.kernel.org/doc/html/latest/bpf/bpf_design_QA.html#q-why-there-is-no-bpf-sdiv-for-signed-divide-operation)。 添加签名分配的指令是一个考虑因素。
## 加载程序loader
程序由运行时加载程序部署并执行,目前有两个受支持的加载程序[BPF加载程序](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/bpf_loader.rs#L17)]和[不建议使用BPF加载程序](https://github.com/solana-labs/solana/blob/7ddf10e602d2ed87a9e3737aa8c32f1db9f909d8/sdk/program/src/bpf_loader_deprecated.rs#L14)
加载程序可能支持不同的应用程序二进制接口,因此开发人员必须为其编写程序并将其部署到同一加载程序中。 如果为一个装载程序编写的程序被部署到另一个装载程序,则由于程序输入参数的反序列化不匹配,结果通常是`AccessViolation`错误。
出于所有实际目的应始终将程序编写为以最新的BPF加载程序为目标并且最新的加载程序是命令行界面和javascript API的默认设置。
有关为特定加载程序实现程序的语言特定信息,请参见:
- [Rust程序入口点](developing-rust.md#program-entrypoint)
- [C程序入口点](developing-c.md#program-entrypoint)
### 部署
BPF程序部署是将BPF共享对象上载到程序帐户的数据中并标记该帐户可执行文件的过程。 客户端将BPF共享对象分成较小的部分并将其作为[`Write`](https://github.com/solana-labs/solana/blob/bc7133d7526a041d1aaee807b80922baa89b6f90/sdk/program/src/loader_instruction.rs#L13)的指令数据发送向加载程序的指令,加载程序在此将数据写入程序的帐户数据。 一旦收到所有片段,客户端就会向加载程序发送[`Finalize`](https://github.com/solana-labs/solana/blob/bc7133d7526a041d1aaee807b80922baa89b6f90/sdk/program/src/loader_instruction.rs#L30)指令然后加载程序将验证BPF数据是否有效并将程序帐户标记为_executable_。 一旦程序帐户被标记为可执行,随后的交易就可以发出该程序要处理的指令。
当指令针对可执行的BPF程序时加载程序将配置程序的执行环境序列化程序的输入参数调用程序的入口点并报告遇到的任何错误。
有关更多信息,请参见[deploying](deploying.md)
### 输入参数序列化
BPF加载程序将程序输入参数序列化为字节数组然后将其传递到程序的入口点由程序负责在链上反序列化它。 不赞成使用的加载器和当前加载器之间的变化之一是,输入参数以某种方式序列化,导致各种参数落在对齐字节数组内的对齐偏移量上。 这允许反序列化实现直接引用字节数组并提供指向程序的对齐指针。
有关序列化的特定于语言的信息,请参见:
- [Rust程序参数反序列化](developing-rust.md#parameter-deserialization)
- [C程序参数反序列化](developing-c.md#parameter-deserialization)
最新的加载器按如下方式序列化程序输入参数(所有编码均为小尾数法)
- 8字节无符号帐户数
- 对于每个帐户
- 1个字节指示这是否是重复帐户如果不是重复帐户则值为0xff否则值为与其重复的帐户的索引。
- 填充7个字节
- 如果不重复
- 1个字节的填充
- 1个字节的布尔值如果account是签名者则为true
- 1字节布尔值如果帐户可写则为true
- 1字节布尔值如果帐户可执行则为true
- 4个字节的填充
- 32个字节的帐户公钥
- 该帐户的所有者公共密钥的32个字节
- 该帐户拥有的Lamport的8字节无符号数
- -8个字节的无符号帐户数据字节数
- x字节的帐户数据
- 10k字节的填充用于重新分配
- 足够的填充以将偏移量对齐到8个字节。
- 8个字节的租用纪元
- 8个字节的无符号指令数据
- -x字节的指令数据
- -程序ID的32个字节

View File

@ -1,91 +0,0 @@
---
title: "账户"
---
## 在交易之间存储状态
如果程序需要在交易之间存储状态则可以使用_accounts_进行存储。 帐户类似于Linux等操作系统中的文件。 就像文件一样,帐户可以保存任意数据,并且该数据会在程序的生存期内持续存在。 帐户也像文件一样,包含元数据,该元数据告诉运行时允许谁访问数据以及如何访问数据。
与文件不同,该帐户包含文件生存期内的元数据。 该存在时间用“代币”表示即称为_lamports_的许多局部原生代币。 帐户保存在验证节点的内存中,并支付[“rent”](#rent)留在那里。 每个验证节点都会定期扫描所有帐户并收取租金。 任何掉落到零零花的账户都将被清除。 如果帐户包含足够数量的Lamport也可以标记为[rent-exempt](#rent-exemption)。
与Linux用户使用路径查找文件的方式相同Solana客户端使用_address_查找帐户。 该地址是一个256位公共密钥。
## 签名者
交易可以包括与交易所引用的账户的公共密钥相对应的数字[签名](terminology.md#signature)。 当存在相应的数字签名时它表示该帐户的私钥持有人已签名并因此“授权”了该交易因此该帐户称为_signer_。 帐户是否为签名者将作为帐户元数据的一部分传达给程序。 然后,程序可以使用该信息来制定权限决策。
## 只读
事务可以[指示](transactions.md#message-header-format)它引用的某些帐户被视为_只读帐户_以便能够在事务之间进行并行帐户处理。 运行时允许多个程序同时读取只读帐户。 如果程序尝试修改只读帐户,则运行时将拒绝该事务。
## 可执行
如果某个帐户在其元数据中被标记为“可执行”,则将其视为可以通过将帐户的公钥包含在指令的[程序ID](transactions.md#program-id)中来执行的程序。 在成功的程序部署过程中,拥有该帐户的加载程序将帐户标记为可执行文件。 例如在BPF程序部署期间一旦加载程序确定帐户数据中的BPF字节码有效则加载程序会将程序帐户永久标记为可执行文件。 一旦可执行,运行时将强制该帐户的数据(程序) 是不可变的。
## 创建
为了创建一个帐户客户端生成一个_keypair_并使用`SystemProgram::CreateAccount`指令注册其公共密钥,并预先分配了固定的存储大小(以字节为单位)。 当前帐户数据的最大大小为10MB。
帐户地址可以是任意的256位值并且高级用户可以使用一些机制来创建派生地址(`SystemProgram::CreateAccountWithSeed`[`Pubkey::CreateProgramAddress`](calling-between-programs.md#program-derived-addresses))。
从未通过系统程序创建的帐户也可以传递到程序。 当指令引用以前尚未创建的帐户时程序将通过系统程序拥有的帐户该帐户具有0个Lamport和0个数据。 但是,该帐户将反映它是否是该交易的签名者,因此可以用作授权。 在这种情况下,授权机构向程序传达与帐户的公共密钥相关联的私有密钥的持有者对交易进行了签名。 该程序可能知道该帐户的公钥,也可能将其记录在另一个帐户中,并表示对该程序控制或执行的资产或操作具有某种所有权或授权。
## 程序的所有权和分配
创建的帐户由称为System程序的内置程序初始化为_owned_并适当地称为_system account_。 帐户包含“所有者”元数据。 所有者是一个程序ID。 如果运行时的ID与所有者匹配则运行时将授予该程序对该帐户的写访问权限。 对于System程序运行时允许客户端转移Lamport并且重要的是_转移_帐户所有权这意味着将所有者更改为其他程序ID。 如果某个帐户不属于某个程序,则仅允许该程序读取其数据并将该帐户记入贷方。
## 承租
使帐户在Solana上保持活动状态会产生称为_rent_的存储成本因为集群必须积极维护数据以处理其上的任何将来的事务。 这与比特币和以太坊不同,在比特币和以太坊中,存储帐户不会产生任何费用。
租金是在当前时期通过事务在第一次访问(包括初始帐户创建) 时通过运行时从帐户余额中扣除的,如果没有交易,则在每个时期一次。 该费用目前是固定费率,以字节乘以时期为单位。 该费用将来可能会更改。
为了简化租金计算,租金始终是在一个完整的时期内收取的。 租金不是按比例分配的,这意味着部分时期既不收费也不退款。 这意味着,在创建帐户时,收取的首笔租金不是针对当前的部分时期,而是针对下一个完整的时期而预先收取的租金。 随后的租金收取是未来的进一步时期。 另一方面,如果一个已出租的帐户的余额降到另一个租金费用的中间时期以下,则该帐户将在当前时期继续存在,并在即将到来的时期开始时立即被清除。
如果帐户保持最低余额,则可以免交租金。 此免租金描述如下。
### 租金计算
注意:租金率将来可能会改变。
在撰写本文时在testnet和mainnet-beta群集上固定租金为每字节纪元19.055441478439427兰特。 一个[epoch](terminology.md#epoch)的目标是2天(对于devnet租金为每字节纪元 0.3608183131797095 lamports长度为54m36s长)。
计算得出该值的目标是每兆字节天0.01 SOL(与每兆字节年3.56SOL完全匹配)
```text
租金19.055441478439427=10_000_000(0.01SOL)*365(一年中大约一天)/(1024*1024)(1MiB)/(365.25/2)(一年中的纪元)
```
租金计算以`f64`精度完成最终结果在Lamports中被截断为`u64`。
租金计算包括帐户大小的帐户元数据(地址、所有者、lamports等)。 因此用于租金计算的最小帐户为128字节。
例如创建的帐户初始转移了10,000 lamports并且没有其他数据。 租金会在创建时立即从中扣除从而产生7,561 lamports的余款
```text
租金2,439=19.055441478439427(租金)*128字节(最小帐户大小)*1(纪元)
帐户余额7,561=10,000(转让的兰特)-2,439(此帐户的时期租金)
```
即使没有活动帐户余额也将在下一个时期减少到5,122 lamports
```text
帐户余额5,122=7,561(当前余额) -2,439(该帐户的租金,用于某个时期)
```
因此如果转移的兰特小于或等于2439则最小尺寸帐户将在创建后立即删除。
### 免租金
另外通过存入至少2年的租金可以使一个帐户完全免收租金。 每次帐户余额减少时都会进行检查,一旦余额低于最低金额,便会立即从租金中扣除。
运行时要求程序可执行帐户免租金,以免被清除。
注意:请使用[`getMinimumBalanceForRentExemption`RPC端点](developing/clients/jsonrpc-api.md#getminimumbalanceforrentexemption)计算特定帐户大小的最小余额。 以下计算仅是说明性的。
例如一个程序可执行文件的大小为15,000字节则需要105,290,880 lamports(=〜0.105SOL) 的余额才能免租:
```text
105,290,880=19.055441478439427(手续费率)*(128+15_000)(包括元数据的帐户大小)*((365.25/2)*2)(以2年为周期)
```

View File

@ -1,198 +0,0 @@
---
title: 程序之间的调用
---
## 跨程序调用 {#cross-program-invocations}
Solana运行时允许程序通过称为跨程序调用的机制相互调用。 程序之间的调用是通过一个程序调用另一个程序的指令来实现的。 调用程序将暂停,直到被调用的程序完成对指令的处理为止。
例如,客户可以创建一个交易来修改两个帐户,每个帐户都由单独的链上程序拥有:
```rust,ignore
let message = Message::new(vec![
token_instruction::pay(&alice_pubkey),
acme_instruction::launch_missiles(&bob_pubkey),
]);
client.send_and_confirm_message(&[&alice_keypair, &bob_keypair], &message);
```
客户可以代之以允许`acme`程序代表客户方便地调用`token`指令:
```rust,ignore
let message = Message::new(vec![
acme_instruction::pay_and_launch_missiles(&alice_pubkey, &bob_pubkey),
]);
client.send_and_confirm_message(&[&alice_keypair, &bob_keypair], &message);
```
给定两个链上程序`token`和`acme`,每个程序分别执行指令`pay()`和`launch_missiles()`,可以通过调用`token`模块中定义的函数来实现acme跨程序调用
```rust,ignore
mod acme {
use token_instruction;
fn launch_missiles(accounts: &[AccountInfo]) -> Result<()> {
...
}
fn pay_and_launch_missiles(accounts: &[AccountInfo]) -> Result<()> {
let alice_pubkey = accounts[1].key;
let instruction = token_instruction::pay(&alice_pubkey);
invoke(&instruction, accounts)?;
launch_missiles(accounts)?;
}
```
Solana的运行时内置了`invoke()`,它负责通过指令的`program_id`字段将给定指令路由到`token`程序。
请注意,`invoke` 要求调用者传递被调用指令所需的所有帐户。 这意味着可执行帐户(与指令的程序ID匹配的帐户) 和传递给指令处理器的帐户都可以。
在调用`pay()`之前,运行时必须确保`acme`没有修改`token`拥有的任何帐户。 它通过在`acme`调用`invoke`时将运行时策略应用于帐户的当前状态,而不是在`acme`指令开始时将帐户的初始状态应用到帐户的当前状态。 在`pay()`完成之后,运行时必须再次通过应用运行时的策略来确保`token`不会修改`acme`拥有的任何帐户,但是这次使用`token`程序ID。 最后,在`pay_and_launch_missiles()`完成之后运行时必须再次使用runtime 策略,这通常是正常的时间,但要使用所有更新的`pre_ *`变量。 如果执行直到`pay()`为止的`pay_and_launch_missiles()`没有任何无效的帐户更改,`pay()`没有任何无效的更改,并且从`pay()`一直执行到`pay_and_launch_missiles()`返回则没有无效的更改然后runtime可以过渡性地假设`pay_and_launch_missiles()`总体上没有进行无效的帐户更改,因此可以提交所有这些帐户修改。
### 需要特权的指令
运行时使用授予调用者程序的特权来确定可以扩展给被调用者的特权。 在这种情况下,特权是指签名者和可写帐户。 例如,如果调用者正在处理的指令包含签名者或可写帐户,则调用者可以调用也包含该签名者和/或可写帐户的指令。
此特权扩展依赖于程序是不可变的这一事实。 对于`acme`程序,运行时可以安全地将事务签名视为`token`指令签名。 当运行时看到`token`指令引用`alice_pubkey`时,它将在`acme`指令中查找密钥,以查看该密钥是否与已签名的帐户相对应。 在这种情况下,它会这样做并因此授权`token`程序修改Alice的帐户。
### 程序签名帐户
程序可以使用[程序派生地址](#program-derived-addresses)发出包含未在原始交易中签名的已签名帐户的指令。
为了用程序派生的地址签署一个帐户,程序可以`invoke_signed()`。
```rust,ignore
invoke_signed(
&instruction,
accounts,
&[&["First addresses seed"],
&["Second addresses first seed", "Second addresses second seed"]],
)?;
```
### 调用深度
跨程序调用允许程序直接调用其他程序但当前深度限制为4。
### 可重入
目前,可重入仅限于以固定深度为上限的直接自递归。 此限制可防止程序可能在不知道稍后会被调用回状态的情况下从中间状态调用另一个程序的情况。 直接递归可以使程序在被调用时完全控制其状态。
## 程序派生地址
程序派生的地址允许在[程序之间调用](#cross-program-invocations)时使用以编程方式生成的签名。
使用程序派生的地址,可以向某个程序授予某个帐户的权限,然后再将该权限转移给另一个帐户。 这是可能的,因为该程序可以在授予权限的事务中充当签名者。
例如如果两个用户想要对Solana中的游戏结果押注则他们每个人都必须将其下注的资产转移到将遵守协议的某些中介机构上。 当前在Solana中尚无办法将此中介程序作为程序来实现因为该中介程序无法将资产转让给获胜者。
对于许多DeFi应用程序来说此功能是必需的因为它们要求将资产转移到托管代理直到发生确定新所有者的事件为止。
- 去中心化交易所,可在匹配的买价和卖价之间转移资产。
- 将资产转移给获胜者的拍卖。
- 收集奖品并将其重新分配给获奖者的游戏或预测市场。
程序派生地址:
1. 允许程序控制特定的地址(称为程序地址),以使任何外部用户都无法生成带有这些地址签名的有效交易。
2. 允许程序以编程方式签名通过[跨程序调用](#cross-program-invocations)调用的指令中存在的程序地址。
在这两个条件下,用户可以安全地将链上资产的权限转移或分配给程序地址,然后程序可以自行决定在其他地方分配该权限。
### 程序地址的私钥
程序地址不在ed25519曲线上因此没有与之关联的有效私钥因此无法生成签名。 虽然它没有自己的私钥,但是程序可以使用它来发布包含程序地址作为签名者的指令。
### 基于哈希的生成程序地址
程序地址是使用256位抗映像前哈希函数从种子和程序ID的集合中确定性地得出的。 程序地址一定不能位于ed25519曲线上以确保没有关联的私钥。 如果发现地址位于曲线上,则在生成过程中将返回错误。 对于给定的种子和程序ID集合这种情况大约发生50/50的变化。 如果发生这种情况,可以使用另一组种子或种子凹凸(附加的8位种子) 来查找曲线外的有效程序地址。
程序的确定性程序地址遵循与使用`system_instruction::create_address_with_seed`实现的用`SystemInstruction::CreateAccountWithSeed`创建的帐户类似的派生路径。
作为参考,该实现如下:
```rust,ignore
pub fn create_address_with_seed(
base: &Pubkey,
seed: &str,
program_id: &Pubkey,
) -> Result<Pubkey, SystemError> {
if seed.len() > MAX_ADDRESS_SEED_LEN {
return Err(SystemError::MaxSeedLengthExceeded);
}
Ok(Pubkey::new(
hashv(&[base.as_ref(), seed.as_ref(), program_id.as_ref()]).as_ref(),
))
}
```
程序可以使用种子确定性地派生任意数量的地址。 这些种子可以象征性地标识地址的使用方式。
来自`Pubkey`::
```rust,ignore
///生成派生程序地址
/// *种子,用于派生密钥的符号关键字
/// * program_id为该地址派生的程序
pub fn create_program_address(
seeds: &[&[u8]],
program_id: &Pubkey,
) -> Result<Pubkey, PubkeyError>
```
### 使用程序地址
客户可以使用`create_program_address`函数生成目标地址。
```rust,ignore
//确定性地导出托管密钥
let escrow_pubkey = create_program_address(&[&["escrow"]], &escrow_program_id);
//使用该密钥构造传输消息
let message = Message::new(vec![
token_instruction::transfer(&alice_pubkey, &escrow_pubkey, 1),
]);
//处理将一个1令牌传输到托管的消息
client.send_and_confirm_message(&[&alice_keypair], &message);
```
程序可以使用相同的函数来生成相同的地址。 程序在下面的功能中从程序地址发出`token_instruction::transfer`,就好像它具有用于签署交易的私钥一样。
```rust,ignore
fn transfer_one_token_from_escrow(
program_id: &Pubkey,
keyed_accounts: &[KeyedAccount]
) -> Result<()> {
//用户提供目的地
let alice_pubkey = keyed_accounts[1].unsigned_key();
//确定性派生托管公钥。
let escrow_pubkey = create_program_address(&[&["escrow"]], program_id);
//创建转移指令
let instruction = token_instruction::transfer(&escrow_pubkey, &alice_pubkey, 1);
//运行时确定性地从当前
//执行程序ID和提供的关键字。
//如果派生地址与指令中标记为已签名的键匹配
//然后该密钥被接受为已签名。
invoke_signed(&instruction, &[&["escrow"]])?
}
```
### 需要签名者的说明
用`create_program_address`生成的地址与任何其他公钥都没有区别。 运行时验证地址是否属于程序的唯一方法是使程序提供用于生成地址的种子。
运行时将在内部调用`create_program_address`,并将结果与指令中提供的地址进行比较。
## 示例
请参阅 [使用Rust开发](developing/on-chain-programs/developing-rust.md#examples) 和 [使用C开发](developing/on-chain-programs/developing-c.md#examples)以获取有关如何使用跨程序调用的示例。

View File

@ -1,7 +0,0 @@
---
title: "概述"
---
一个[app](terminology.md#app)通过向Solana集群发送带有一个或多个[指令](transactions.md#instructions)的[事务](transactions.md)来与之交互。 Solana [runtime](runtime.md) 会将这些指令传递给应用程序开发人员事先部署的[程序](terminology.md#program)。 例如,一条指令可能告诉程序将[lamports](terminology.md#lamports)从一个[账户](accounts.md)转移到另一个或创建一个交互协议来管理Lamport的转移方式。 指令针对每个交易顺序地和原子级地执行。 如果任何指令无效,则交易中的所有帐户更改都将被丢弃。
要立即开始开发,您可以构建,部署和运行[示例](developing/on-chain-programs/examples.md)之一。

View File

@ -1,86 +0,0 @@
---
title: "运行时runtime"
---
## 程序功能
运行时仅允许所有者程序借记帐户或修改其数据。 然后,程序为客户是否可以修改其拥有的帐户定义其他规则。 在系统程序的情况下,它允许用户通过识别交易签名来转移灯饰。 如果看到客户端使用密钥对的_private key_签署了交易则表明客户端已授权代币传输。
换句话说,给定程序拥有的整个帐户集可以视为键值存储,其中键是帐户地址,值是特定于程序的任意二进制数据。 程序作者可以决定如何管理尽可能多的帐户的整个程序状态。
运行时执行每个事务的指令后,它将使用帐户元数据来验证是否违反了访问策略。 如果程序违反了该策略,则运行时会丢弃交易中所有指令所做的所有帐户更改,并将交易标记为失败。
### 政策
程序处理完指令后,运行时将验证程序仅执行了允许的操作,并且结果符合运行时策略。
该策略如下:
- 只有帐户所有者可以更改所有者。
- 仅在帐户可写时。
- 并且仅在该帐户不可执行时
- 并且仅当数据为零初始化或为空时。
- 未分配给该程序的帐户的余额不能减少。
- 只读帐户和可执行帐户的余额可能不会更改。
- 只有系统程序拥有该帐户,只有系统程序才能更改数据大小。
- 只有所有者可以更改帐户数据。
- 如果该帐户可写。
- 并且该帐户不可执行。
- 可执行文件是单向的(false->true),只有帐户所有者可以设置。
- 没有与此帐户相关联的rent_epoch的修改。
## 计算预算 {#compute-budget}
为了防止程序滥用计算资源,必须为事务中的每个指令分配一个计算预算。 预算由计算单元组成,在程序执行各种操作和程序可能不会超出的范围时会消耗这些计算单元。 当程序消耗了全部预算或超出界限时,运行时将暂停程序并返回错误。
以下操作会产生计算成本:
- 执行BPF指令
- 呼叫系统电话
- 记录
- 创建程序地址
- 跨程序调用
- ...
对于跨程序调用,所调用的程序将继承其父级的预算。 如果被调用的程序消耗了预算或超出了限制,则整个调用链和父级都将停止。
可以在Solana SDK中找到当前的[计算预算](https://github.com/solana-labs/solana/blob/d3a3a7548c857f26ec2cb10e270da72d373020ec/sdk/src/process_instruction.rs#L65)。
例如,如果当前预算是:
```rust
max_units: 200,000,
log_units: 100,
log_u64_units: 100,
create_program address units: 1500,
invoke_units: 1000,
max_invoke_depth: 4,
max_call_depth: 64,
stack_frame_size: 4096,
log_pubkey_units: 100,
```
然后程序
- 如果不执行其他操作则可以执行200,000条BPF指令
- 可以记录2,000条日志消息
- 堆栈使用量不能超过4k
- 不能超过BPF通话深度64
- 不能超过4个级别的跨程序调用。
由于计算预算在程序执行时会逐渐消耗,因此总预算消耗将是其执行的各种操作成本的组合。
在运行时,程序可以记录剩余多少计算预算。 有关更多信息,请参见[debugging](developing/on-chain-programs/debugging.md#monitoring-compute-budget-consumption)。
预算值取决于功能启用,请查看计算预算的[new](https://github.com/solana-labs/solana/blob/d3a3a7548c857f26ec2cb10e270da72d373020ec/sdk/src/process_instruction.rs#L97)函数列出预算的编制方式。 需要了解[功能](runtime.md#features)的工作方式以及正在使用的群集上启用的功能才能确定当前预算的值。
## 新的功能
随着Solana的发展可能会引入新功能或补丁这些新功能或补丁会更改集群的行为以及程序的运行方式。 行为的变化必须在群集的各个节点之间进行协调,如果节点不协调,则这些变化可能会导致共识破裂。 Solana支持一种称为运行时功能的机制以方便平滑地采用更改。
运行时功能是历时协调的事件,将在集群中发生一个或多个行为更改。 对Solana的新更改将更改行为使用功能门进行包装并且默认情况下处于禁用状态。 然后使用Solana工具激活功能将其标记为未决一旦标记为未决该功能将在下一个时期激活。
要确定激活了哪些功能,请使用[Solana命令行工具](cli/install-solana-cli-tools.md)
```bash
solana功能状态
```
如果首先遇到问题请确保您使用的Solana工具版本与`solana cluster-version`返回的版本相匹配。 如果它们不匹配,请[安装正确的工具套件](cli/install-solana-cli-tools.md)。

View File

@ -1,113 +0,0 @@
---
title: "交易"
---
程序执行从将[transaction](terminology.md#transaction)提交到集群开始。 Solana运行时将执行一个程序以按顺序和原子方式处理事务中包含的每个[指令](terminology.md#instruction)。
## 交易剖析
本节介绍事务的二进制格式
### 交易格式
事务包含签名的[compact-array](#compact-array-format),然后是[message](#message-format)。 签名数组中的每个项目都是给定消息的[数字签名](#signature-format)。 Solana运行时验证签名数是否与[message heade](#message-header-format)的前8位中的数字匹配。 它还验证每个签名是否由与邮件帐户地址数组中相同索引处的公钥相对应的私钥签名。
#### 签名格式
每个数字签名均为ed25519二进制格式占用64个字节。
### 邮件格式
一条消息包含[header](#message-header-format),然后是[account address](#account-addresses-format)的紧凑数组,然后是最近的[blockhash](#blockhash-format),紧接着是[指令](#instruction-format)的紧凑数组。
#### 邮件标题格式
消息头包含三个无符号的8位值。 第一个值是包含交易中所需签名的数量。 第二个值是那些对应的只读帐户地址的数量。 邮件标题中的第三个值是不需要签名的只读帐户地址的数量。
#### 帐户地址格式
要求签名的地址出现在帐户地址数组的开头,其地址首先请求写访问权限,然后请求只读帐户。 不需要签名的地址跟在需要签名的地址之后,再次是先读写帐户,然后是只读帐户。
#### Blockhash区块链哈希值格式
区块哈希包含一个32字节的SHA-256哈希。 它用于指示客户最后一次观察分类帐的时间。 当区块哈希值太旧时,验证程序将拒绝交易。
### 指令格式
一条指令包含一个程序ID索引后跟一个帐户地址索引的紧凑数组然后是一个不透明的8位数据的紧凑数组。 程序ID索引用于标识可以解释不透明数据的链上程序。 程序ID索引是消息的帐户地址数组中帐户地址的无符号8位索引。 帐户地址索引是同一数组中的无符号8位索引。
### 紧凑数组格式
紧凑数组被序列化为数组长度,随后是每个数组项。 数组长度是一种特殊的多字节编码称为compact-u16。
#### Compact-u16格式
一个compact-u16是16位的多字节编码。 第一个字节在其低7位中包含该值的低7位。 如果该值大于0x7f则设置高位并将该值的后7位放入第二个字节的低7位。 如果该值大于0x3fff则设置高位并将该值的其余2位放入第三个字节的低2位。
### 帐户地址格式
帐户地址是32字节的任意数据。 当地址需要数字签名时运行时会将其解释为ed25519密钥对的公钥。
## 指示
每个[instruction](terminology.md#instruction)都指定一个程序,应传递给该程序的交易帐户的子集以及一个传递给该程序的数据字节数组。 该程序解释数据数组并在指令指定的帐户上运行。 该程序可以成功返回,或者带有错误代码。 错误返回会导致整个事务立即失败。
程序通常提供帮助程序功能来构造它们支持的指令。 例如系统程序提供了以下Rust助手来构建[`SystemInstruction::CreateAccount`](https://github.com/solana-labs/solana/blob/6606590b8132e56dab9e60b3f7d20ba7412a736c/sdk/program/src/system_instruction.rs#L63)指令:
```rust
pub fn create_account(
from_pubkey: &Pubkey,
to_pubkey: &Pubkey,
lamports: u64,
space: u64,
owner: &Pubkey,
) -> Instruction {
let account_metas = vec![
AccountMeta::new(*from_pubkey, true),
AccountMeta::new(*to_pubkey, true),
];
Instruction::new(
system_program::id(),
&SystemInstruction::CreateAccount {
lamports,
space,
owner: *owner,
},
account_metas,
)
}
```
可以在这里找到:
https://github.com/solana-labs/solana/blob/6606590b8132e56dab9e60b3f7d20ba7412a736c/sdk/program/src/system_instruction.rs#L220
### 程序ID
指令的[程序ID](terminology.md#program-id)指定将处理该指令的程序。 程序帐户的所有者指定应使用哪个加载程序来加载和执行程序,并且数据包含有关运行时应如何执行程序的信息。
对于[已部署的BPF程序](developing/on-chain-programs/overview.md)所有者是BPF加载程序帐户数据包含BPF字节码。 一旦成功部署,程序帐户便会被加载程序永久标记为可执行文件。 运行时将拒绝指定不可执行程序的事务。
与已部署的程序不同,[runtime facilities](developing/runtime-facilities/programs.md)的处理方式有所不同因为它们直接内置在Solana运行时中。
### 帐户
指令引用的帐户代表链上状态,并且既作为程序的输入又作为输出。 有关帐户的更多信息,请参见[帐户](accounts.md)章节。
### 指令数据
每个指令都带有一个通用字节数组,该字节数组与帐户一起传递给程序。 指令数据的内容是特定于程序的,通常用于传达程序应执行的操作以及这些操作在帐户所包含的内容之外可能需要的任何其他信息。
程序可以自由指定如何将信息编码到指令数据字节数组中。 数据编码方式的选择应考虑到解码的开销,因为该步骤是由链上程序执行的。 据观察,一些常见的编码(例如Rust的bincode) 效率很低。
[Solana程序库的代币程序](https://github.com/solana-labs/solana-program-library/tree/master/token)提供了一个示例,说明如何有效地对指令数据进行编码,但是请注意,这种方法仅支持固定大小的类型。 代币利用[Pack](https://github.com/solana-labs/solana/blob/master/sdk/program/src/program_pack.rs)特征来对代币指令和代币的指令数据进行编码/解码帐户状态。
## 签名
每笔交易都明确列出了交易指令所引用的所有帐户公钥。 这些公钥的子集每个都带有交易签名。 这些签名向链上程序发出信号,表明帐户持有人已授权交易。 通常,程序使用授权来允许借记帐户或修改其数据。 有关如何将授权传达给程序的更多信息,请参见[帐户](accounts.md#signers)。
## 最近的区块链哈希值
事务包括最近的[blockhash](terminology.md#blockhash),以防止重复并赋予事务生命周期。 任何与上一个交易完全相同的交易都会被拒绝,因此添加一个更新的区块哈希可以使多个交易重复完全相同的操作。 事务还具有由Blockhash定义的生存期因为Blockhash太旧的任何事务都将被拒绝。

View File

@ -1,95 +0,0 @@
---
title: "构建程序"
---
Solana包含少量内置程序这些程序是运行验证程序节点所必需的。 与第三方程序不同,内置程序是验证程序实现的一部分,可以作为群集升级的一部分进行升级。 可能会进行升级以添加功能,修复错误或提高性能。 个别指令的界面更改很少(如果有的话)发生。 相反,当需要更改时,将添加新指令,并且将先前的指令标记为已弃用。 应用程序可以在自己的时间表上进行升级,而无需担心升级过程中的中断。
对于每个内置程序将提供每个支持的指令的程序ID和说明。 事务可以混合和匹配来自不同程序的指令,也可以包括来自已部署程序的指令。
## 系统程序
创建帐户并在它们之间转移Lamport
- 程序ID`11111111111111111111111111111111`
- 说明:[SystemInstruction](https://docs.rs/solana-sdk/VERSION_FOR_DOCS_RS/solana_sdk/system_instruction/enum.SystemInstruction.html)
## 配置程序
将配置数据添加到链和允许对其进行修改的公钥列表中
- 程序ID`Config1111111111111111111111111111111111111111`
- 说明:[config_instruction](https://docs.rs/solana-config-program/VERSION_FOR_DOCS_RS/solana_config_program/config_instruction/index.html)
与其他程序不同Config程序未定义任何单独的指令。 它只有一条隐式指令,即“存储”指令。 它的指令数据是一组密钥,用于控制对帐户的访问以及存储在其中的数据。
## 权益计划
创建权益账户并将其委托给验证者
- 程序ID`Stake11111111111111111111111111111111111111`
- 说明: [StakeInstruction](https://docs.rs/solana-stake-program/VERSION_FOR_DOCS_RS/solana_stake_program/stake_instruction/enum.StakeInstruction.html)
## 投票程序
创建投票账户并对区块进行投票
- 程序ID`Vote111111111111111111111111111111111111111`
- 说明:[VoteInstruction](https://docs.rs/solana-vote-program/VERSION_FOR_DOCS_RS/solana_vote_program/vote_instruction/enum.VoteInstruction.html)
## BPF加载程序
将程序添加到链中并执行它们。
- 程序ID`BPFLoader11111111111111111111111111111111111`
- 说明:[LoaderInstruction](https://docs.rs/solana-sdk/VERSION_FOR_DOCS_RS/solana_sdk/loader_instruction/enum.LoaderInstruction.html)
BPF加载程序将其自身标记为它创建的用于存储程序的可执行帐户的“所有者”。 当用户通过程序ID调用指令时Solana运行时将同时加载您的可执行帐户及其所有者BPF Loader。 然后运行时将您的程序传递给BPF加载程序以处理指令。
## Secp256k1程序
验证secp256k1公钥恢复操作(ecrecover)。
- 程序ID`KeccakSecp256k11111111111111111111111111111111`
- 说明:[new_secp256k1_instruction](https://github.com/solana-labs/solana/blob/c1f3f9d27b5f9534f9a37704bae1d690d4335b6b/programs/secp256k1/src/lib.rs#L18)
Secp256k1程序处理一条指令该指令将在指令数据中序列化的以下结构的计数作为第一个字节
```
struct Secp256k1SignatureOffsets {
secp_signature_key_offset: u16, // offset to [signature,recovery_id,etherum_address] of 64+1+20 bytes
secp_signature_instruction_index: u8, // instruction index to find data
secp_pubkey_offset: u16, // offset to [signature,recovery_id] of 64+1 bytes
secp_signature_instruction_index: u8, // instruction index to find data
secp_message_data_offset: u16, // offset to start of message data
secp_message_data_size: u16, // size of message data
secp_message_instruction_index: u8, // index of instruction data to get message data
}
```
伪代码的操作:
```
process_instruction() {
for i in 0..count {
// i'th index values referenced:
instructions = &transaction.message().instructions
signature = instructions[secp_signature_instruction_index].data[secp_signature_offset..secp_signature_offset + 64]
recovery_id = instructions[secp_signature_instruction_index].data[secp_signature_offset + 64]
ref_eth_pubkey = instructions[secp_pubkey_instruction_index].data[secp_pubkey_offset..secp_pubkey_offset + 32]
message_hash = keccak256(instructions[secp_message_instruction_index].data[secp_message_data_offset..secp_message_data_offset + secp_message_data_size])
pubkey = ecrecover(signature, recovery_id, message_hash)
eth_pubkey = keccak256(pubkey[1..])[12..]
if eth_pubkey != ref_eth_pubkey {
return Error
}
}
return Success
}
```
这允许用户在事务中指定用于签名和消息数据的任何指令数据。 通过指定一种特殊的指令sysvar也可以从事务本身接收数据。
交易成本将计算要验证的签名数乘以签名成本验证乘数。
### 优化注意事项
该操作将必须在(至少部分) 反序列化之后进行但是所有输入都来自交易数据本身这使得它相对于交易处理和PoH验证并行执行相对容易。

View File

@ -1,80 +0,0 @@
---
title: Sysvar群集数据
---
Solana通过[`sysvar`](terminology.md#sysvar)帐户向程序公开了各种群集状态数据。 这些帐户填充在[`solana-program`开发工具](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/sysvar/index.html)中发布的已知地址以及帐户布局中,并在下面概述。
要将sysvar数据包括在程序操作中请在事务处理的帐户列表中传递sysvar帐户地址。 可以像其他任何帐户一样在您的指令处理器中读取该帐户。 始终以*只读方式*访问sysvars帐户。
## 时钟
Clock sysvar包含有关群集时间的数据包括当前时间段时期和估计的Wall-clock Unix时间戳。 它在每个插槽中更新。
- 地址:`SysvarC1ock11111111111111111111111111111111`
- 布局:[时钟](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/clock/struct.Clock.html)
- 栏位:
- `slot`:当前的插槽
- `epoch_start_timestamp`此epoch中第一个插槽的Unix时间戳。 在纪元的第一个时隙中,此时间戳与`unix_timestamp`(如下所示) 相同。
- `epoch`:当前纪元
- `leader_schedule_epoch`:已经为其生成了领导者时间表的最新纪元
- `unix_timestamp`此插槽的Unix时间戳。
每个插槽都有一个基于历史证明的估计持续时间。 但实际上,时隙的流逝可能比此估计更快或更慢。 结果将基于投票验证程序的oracle输入生成插槽的Unix时间戳。 此时间戳的计算方式为投票提供的时间戳估计的赌注加权中位数,以自纪元开始以来经过的预期时间为界。
更明确地说:对于每个插槽,每个验证节点提供的最新投票时间戳用于生成当前时隙的时间戳估计(自投票时间戳以来经过的插槽假定为Bank:: ns_per_slot)。 每个时间戳估计都与委派给该投票帐户的股份相关联,以按股份创建时间戳分布。 除非将自`epoch_start_timestamp`以来的经过时间与预期经过时间相差超过25否则将中值时间戳记用作`unix_timestamp`。
## Epoch时间表
这时间段表sysvar包含在创世中设置的时间段常量并允许计算给定时间段中的时隙数给定时隙的时间段等。(注意:时间段时间表与[`leader时间表不同`](terminology.md#leader-schedule))
- 地址:`SysvarEpochSchedu1e111111111111111111111111`
- 布局:[EpochSchedule](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/epoch_schedule/struct.EpochSchedule.html)
## 费用
Fees sysvar包含当前广告位的费用计算器。 它会根据费用调节器在每个时段进行更新。
- 地址:`SysvarFees111111111111111111111111111111111`
- 布局:[费用](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/sysvar/fees/struct.Fees.html)
## 指示
指令sysvar在处理消息时在消息中包含序列化的指令。 这允许程序指令引用同一事务中的其他指令。 阅读有关[指令自省](implemented-proposals/instruction_introspection.md)的更多信息。
- 地址:`` Sysvar1nstructions1111111111111111111111111` ``
- 布局:[指令](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/sysvar/instructions/type.Instructions.html)
## 最近的区块散列值
最近的区块哈希系统变量包含活动的最近区块哈希及其关联的费用计算器。 它在每个插槽中更新。
- 地址:`SysvarRecentB1ockHashes11111111111111111111`
- 布局:[RecentBlockhashes](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/sysvar/recent_blockhashes/struct.RecentBlockhashes.html)
## 承租
Rent sysvar包含租金。 目前,该比率是静态的,并且是根据发生率设定的。 通过手动激活功能可以修改租金燃烧百分比。
- 地址:`SysvarRent111111111111111111111111111111111`
- 赞成:[出租](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/rent/struct.Rent.html)
## 插槽哈希
SlotHashes sysvar包含插槽父库的最新哈希。 它在每个插槽中更新。
- 地址:`SysvarS1otHashes111111111111111111111111111111`
- 布局:[SlotHashes](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/slot_hashes/struct.SlotHashes.html)
## 插槽历史
SlotHistory sysvar包含在最后一个时期出现的插槽的位向量。 它在每个插槽中更新。
- 地址:`SysvarS1otHistory11111111111111111111111111111`
- 布局:[SlotHistory](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/slot_history/struct.SlotHistory.html)
## 权益历史
StakeHistory sysvar包含每个时期群集范围内的权益激活和停用的历史记录。 在每个时间段开始时都会对其进行更新。
- 地址:`` SysvarStakeHistory11111111111111111111111111` ``
- 布局:[StakeHistory](https://docs.rs/solana-program/VERSION_FOR_DOCS_RS/solana_program/stake_history/struct.StakeHistory.html)

View File

@ -1,15 +0,0 @@
---
title: 发展历史
---
2017年11月Anatoly Yakovenko发布了一份白皮书描述了历史证明一种在彼此不信任的计算机之间保持时间同步的技术。 根据Anatoly在Qualcomm、Mesosphere和Dropbox上设计分布式系统的先前经验他知道可靠的时钟可使网络同步变得非常简单。 如果同步很简单,那么生成的网络将很快受到限制,仅受网络带宽限制。
Anatoly深刻地见识了没有时钟的区块链系统例如比特币和以太坊。账本在全球范围内难以扩展到每秒15笔交易以上而Visa等中心化支付系统需要达到65,000账本tps的峰值。 很明显,如果没有一个时钟,永远都不会成就他们梦寐以求的全球支付系统或全球超级计算机。 当Anatoly解决了让彼此不信任的计算机按时达成协议的问题时他拥有的将40年分布式系统研究是带入区块链世界的关键武器。 账本最终的集群将提高速度到10倍100倍或1000倍甚至10,000倍
Anatoly的操作开始于私有代码库并以C编程语言实现。 Greg Fitzgerald以前曾在半导体巨头高通公司Qualcomm Incorporated与Anatoly合作他鼓励他用Rust编程语言重新实现该项目。 Greg致力于LLVM编译器基础结构该基础结构是Clang C/C++编译器和Rust编译器的基础。 Greg声称该语言的安全保证将提高软件生产效率并且缺少垃圾收集器将使该程序能够像用C编写的程序一样完美执行。Anatoly试了一下仅两周后他就将整个代码库迁移到了Rust。 成功了! 这个计划将全球所有交易整合到一个可扩展的单个区块链中Anatoly称其为Loom项目。
在2018年2月13日Greg开始对Anatoly的白皮书的第一个开源执行进行原型设计。 该项目已在织机组织中以Silk的名称发布到GitHub。 2月28日Greg发布了他的第一个版本演示了将在短短半秒钟内完成1万笔已签名交易的验证和处理。 不久之后另一位前高通的研究人员Stephen Akridge展示了通过将签名验证卸载到图形处理器上可以大大提高吞吐量。 Anatoly招募了Greg、Stephen和其他三个人共同创立了一家公司当时名为Loom。
大约在同一时间,基于以太坊的项目织机网络,如雨后春笋般涌现,许多人对于他们是否属于同一项目感到困惑。 织机团队决定将其品牌重塑。 他们选择了Solana这个名字来表示对圣地亚哥北部一个名为Solana Beach的海滩小镇的怀念。AnatolyGreg和Stephen在高通公司工作的时候在那里生活和冲浪了三年。 3月28日该团队创建了Solana Labs GitHub组织并将Greg的原型Silk重命名为Solana。
在2018年6月该团队扩大了该技术的运行范围使其可以基于云网络运行并于7月19日发布了一个50节点经过许可的公共测试网该网络始终支持每秒250,000次交易。 在12月发布的名为v0.10 Pillbox的更高版本中该团队发布了一个许可的测试网该测试网在千兆位网络上运行150个节点并演示了浸泡测试该测试每秒处理_平均_20万笔事务突发次数超过50万笔。 该项目还得到扩展以支持用C编程语言编写的链上程序并在称为BPF的安全执行环境中并行运行。

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm图标 / 沟通 / 地址簿#1</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Communication-/-Adress-book#1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M17,2 L19,2 C20.6568542,2 22,3.34314575 22,5 L22,19 C22,20.6568542 20.6568542,22 19,22 L17,22 L17,2 Z" id="Rectangle-161-Copy" fill="#335EEA" opacity="0.3"/>
<path d="M4,2 L16,2 C17.6568542,2 19,3.34314575 19,5 L19,19 C19,20.6568542 17.6568542,22 16,22 L4,22 C3.44771525,22 3,21.5522847 3,21 L3,3 C3,2.44771525 3.44771525,2 4,2 Z M11.1176481,13.709585 C10.6725287,14.1547043 9.99251947,14.2650547 9.42948307,13.9835365 C8.86644666,13.7020183 8.18643739,13.8123686 7.74131803,14.2574879 L6.2303083,15.7684977 C6.17542087,15.8233851 6.13406645,15.8902979 6.10952004,15.9639372 C6.02219616,16.2259088 6.16377615,16.5090688 6.42574781,16.5963927 L7.77956724,17.0476658 C9.07965249,17.4810276 10.5130001,17.1426601 11.4820264,16.1736338 L15.4812434,12.1744168 C16.3714821,11.2841781 16.5921828,9.92415954 16.0291464,8.79808673 L15.3965752,7.53294436 C15.3725414,7.48487691 15.3409156,7.44099843 15.302915,7.40299777 C15.1076528,7.20773562 14.7910703,7.20773562 14.5958082,7.40299777 L13.0032662,8.99553978 C12.5581468,9.44065914 12.4477965,10.1206684 12.7293147,10.6837048 C13.0108329,11.2467412 12.9004826,11.9267505 12.4553632,12.3718698 L11.1176481,13.709585 Z" id="Combined-Shape" fill="#335EEA"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm图标 / 沟通 / 删除用户</title>
<desc>通过 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Communication-/-Delete-user" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<polygon id="Shape" points="0 0 24 0 24 24 0 24"/>
<path d="M9,11 C6.790861,11 5,9.209139 5,7 C5,4.790861 6.790861,3 9,3 C11.209139,3 13,4.790861 13,7 C13,9.209139 11.209139,11 9,11 Z M21,8 L17,8 C16.4477153,8 16,7.55228475 16,7 C16,6.44771525 16.4477153,6 17,6 L21,6 C21.5522847,6 22,6.44771525 22,7 C22,7.55228475 21.5522847,8 21,8 Z" id="Combined-Shape" fill="#335EEA" opacity="0.3"/>
<path d="M0.00065168429,20.1992055 C0.388258525,15.4265159 4.26191235,13 8.98334134,13 C13.7712164,13 17.7048837,15.2931929 17.9979143,20.2 C18.0095879,20.3954741 17.9979143,21 17.2466999,21 C13.541124,21 8.03472472,21 0.727502227,21 C0.476712155,21 -0.0204617505,20.45918 0.00065168429,20.1992055 Z" id="Mask-Copy" fill="#335EEA"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm图标 / 沟通 / 延迟邮件</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Communication-/-Snoozed-mail" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M12.9835977,18 C12.7263047,14.0909841 9.47412135,11 5.5,11 C4.98630124,11 4.48466491,11.0516454 4,11.1500272 L4,7 C4,5.8954305 4.8954305,5 6,5 L20,5 C21.1045695,5 22,5.8954305 22,7 L22,16 C22,17.1045695 21.1045695,18 20,18 L12.9835977,18 Z M19.1444251,6.83964668 L13,10.1481833 L6.85557487,6.83964668 C6.4908718,6.6432681 6.03602525,6.77972206 5.83964668,7.14442513 C5.6432681,7.5091282 5.77972206,7.96397475 6.14442513,8.16035332 L12.6444251,11.6603533 C12.8664074,11.7798822 13.1335926,11.7798822 13.3555749,11.6603533 L19.8555749,8.16035332 C20.2202779,7.96397475 20.3567319,7.5091282 20.1603533,7.14442513 C19.9639747,6.77972206 19.5091282,6.6432681 19.1444251,6.83964668 Z" id="Combined-Shape" fill="#335EEA"/>
<path d="M8.4472136,18.1055728 C8.94119209,18.3525621 9.14141644,18.9532351 8.89442719,19.4472136 C8.64743794,19.9411921 8.0467649,20.1414164 7.5527864,19.8944272 L5,18.618034 L5,14.5 C5,13.9477153 5.44771525,13.5 6,13.5 C6.55228475,13.5 7,13.9477153 7,14.5 L7,17.381966 L8.4472136,18.1055728 Z" id="Path-85" fill="#335EEA" opacity="0.3"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 烹饪 / 面包工具</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Cooking-/-Baking-glove" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M7.03587629,17.2664036 L2.54094808,14.2345363 C1.62521847,13.6168689 1.38359132,12.373805 2.00125875,11.4580753 C2.61892617,10.5423457 3.86199008,10.3007186 4.7777197,10.918386 L7,12.417333 L7,8 C7,4.6862915 9.6862915,2 13,2 C16.3137085,2 19,4.6862915 19,8 L19,17 C19,17.5522847 18.5522847,18 18,18 L8,18 C7.53996718,18 7.15248755,17.6893628 7.03587629,17.2664036 Z" id="Combined-Shape" fill="#335EEA"/>
<rect id="Rectangle-2" fill="#335EEA" opacity="0.3" x="6" y="20" width="14" height="2" rx="1"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 烹饪 / 平底锅</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Cooking-/-Saucepan" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M2,9 L12,9 C12.5522847,9 13,9.44771525 13,10 L13,13 C13,15.209139 11.209139,17 9,17 L5,17 C2.790861,17 1,15.209139 1,13 L1,10 C1,9.44771525 1.44771525,9 2,9 Z" id="Rectangle-203" fill="#335EEA"/>
<path d="M14.9984604,9.44452998 L21.5023095,9.08320503 C22.2847837,9.03973424 22.9543445,9.63881491 22.9978153,10.4212892 C22.9992715,10.4475009 23,10.4737479 23,10.5 L23,10.5 C23,11.2836808 22.3647011,11.9189797 21.5810203,11.9189797 C21.5547682,11.9189797 21.5285212,11.9182512 21.5023095,11.916795 L14.9984604,11.55547 C14.4382868,11.5243493 14,11.0610373 14,10.5 L14,10.5 C14,9.93896268 14.4382868,9.47565073 14.9984604,9.44452998 Z" id="Rectangle-206" fill="#335EEA" opacity="0.3"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 设计 / 多边形</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Design-/-Polygon" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M8.08113883,20 L15.9188612,20 C16.5645068,20 17.137715,19.5868549 17.3418861,18.9743416 L19.6721428,11.9835717 C19.8694432,11.3916705 19.6797482,10.7394436 19.1957765,10.3456849 L12.9561839,5.26916104 C12.4053757,4.82102426 11.6158052,4.82050247 11.0644052,5.26791085 L4.80622561,10.345825 C4.32117072,10.7394007 4.13079092,11.3923728 4.32832067,11.984962 L6.65811388,18.9743416 C6.86228495,19.5868549 7.43549322,20 8.08113883,20 Z" id="Path-4" fill="#335EEA"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1012 B

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 设备 / 安卓</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Devices-/-Android" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M7.5,4 L7.5,19 L16.5,19 L16.5,4 L7.5,4 Z M7.71428571,2 L16.2857143,2 C17.2324881,2 18,2.8954305 18,4 L18,20 C18,21.1045695 17.2324881,22 16.2857143,22 L7.71428571,22 C6.76751186,22 6,21.1045695 6,20 L6,4 C6,2.8954305 6.76751186,2 7.71428571,2 Z" id="Combined-Shape" fill="#335EEA"/>
<polygon id="Combined-Shape" fill="#335EEA" opacity="0.3" points="7.5 4 7.5 19 16.5 19 16.5 4"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 932 B

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 文件 / 云上传</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Files-/-Cloud-upload" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<polygon id="Shape" points="0 0 24 0 24 24 0 24"/>
<path d="M5.74714567,13.0425758 C4.09410362,11.9740356 3,10.1147886 3,8 C3,4.6862915 5.6862915,2 9,2 C11.7957591,2 14.1449096,3.91215918 14.8109738,6.5 L17.25,6.5 C19.3210678,6.5 21,8.17893219 21,10.25 C21,12.3210678 19.3210678,14 17.25,14 L8.25,14 C7.28817895,14 6.41093178,13.6378962 5.74714567,13.0425758 Z" id="Combined-Shape" fill="#335EEA" opacity="0.3"/>
<path d="M11.1288761,15.7336977 L11.1288761,17.6901712 L9.12120481,17.6901712 C8.84506244,17.6901712 8.62120481,17.9140288 8.62120481,18.1901712 L8.62120481,19.2134699 C8.62120481,19.4896123 8.84506244,19.7134699 9.12120481,19.7134699 L11.1288761,19.7134699 L11.1288761,21.6699434 C11.1288761,21.9460858 11.3527337,22.1699434 11.6288761,22.1699434 C11.7471877,22.1699434 11.8616664,22.1279896 11.951961,22.0515402 L15.4576222,19.0834174 C15.6683723,18.9049825 15.6945689,18.5894857 15.5161341,18.3787356 C15.4982803,18.3576485 15.4787093,18.3380775 15.4576222,18.3202237 L11.951961,15.3521009 C11.7412109,15.173666 11.4257142,15.1998627 11.2472793,15.4106128 C11.1708299,15.5009075 11.1288761,15.6153861 11.1288761,15.7336977 Z" id="Shape" fill="#335EEA" transform="translate(11.959697, 18.661508) rotate(-90.000000) translate(-11.959697, -18.661508) "/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 文件 / 编辑</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Files-/-Compilation" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M3.5,21 L20.5,21 C21.3284271,21 22,20.3284271 22,19.5 L22,8.5 C22,7.67157288 21.3284271,7 20.5,7 L10,7 L7.43933983,4.43933983 C7.15803526,4.15803526 6.77650439,4 6.37867966,4 L3.5,4 C2.67157288,4 2,4.67157288 2,5.5 L2,19.5 C2,20.3284271 2.67157288,21 3.5,21 Z" id="Combined-Shape" fill="#335EEA" opacity="0.3"/>
<rect id="Rectangle-41" fill="#335EEA" opacity="0.3" transform="translate(8.984240, 14.127098) rotate(-45.000000) translate(-8.984240, -14.127098) " x="7.41281179" y="12.5556689" width="3.14285714" height="3.14285714" rx="0.75"/>
<rect id="Rectangle-41-Copy" fill="#335EEA" opacity="0.3" transform="translate(15.269955, 14.127098) rotate(-45.000000) translate(-15.269955, -14.127098) " x="13.6985261" y="12.5556689" width="3.14285714" height="3.14285714" rx="0.75"/>
<rect id="Rectangle-41-Copy-3" fill="#335EEA" transform="translate(12.127098, 17.269955) rotate(-45.000000) translate(-12.127098, -17.269955) " x="10.5556689" y="15.6985261" width="3.14285714" height="3.14285714" rx="0.75"/>
<rect id="Rectangle-41-Copy-4" fill="#335EEA" transform="translate(12.127098, 10.984240) rotate(-45.000000) translate(-12.127098, -10.984240) " x="10.5556689" y="9.41281179" width="3.14285714" height="3.14285714" rx="0.75"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 文件 / 文件夹</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Files-/-Folder" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M3.5,21 L20.5,21 C21.3284271,21 22,20.3284271 22,19.5 L22,8.5 C22,7.67157288 21.3284271,7 20.5,7 L10,7 L7.43933983,4.43933983 C7.15803526,4.15803526 6.77650439,4 6.37867966,4 L3.5,4 C2.67157288,4 2,4.67157288 2,5.5 L2,19.5 C2,20.3284271 2.67157288,21 3.5,21 Z" id="Combined-Shape" fill="#335EEA" opacity="0.3"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 860 B

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 食物 / 芝士</title>
<desc>通过 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Food-/-Cheese" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M22,13.9146471 L22,19 C22,20.1045695 21.1045695,21 20,21 L14,21 C14,19.8954305 13.1045695,19 12,19 C10.8954305,19 10,19.8954305 10,21 L4,21 C2.8954305,21 2,20.1045695 2,19 L2,7 L22,7 L22,11.0853529 C21.8436105,11.0300771 21.6753177,11 21.5,11 C20.6715729,11 20,11.6715729 20,12.5 C20,13.3284271 20.6715729,14 21.5,14 C21.6753177,14 21.8436105,13.9699229 22,13.9146471 Z M9,17 C11.209139,17 13,15.209139 13,13 C13,10.790861 11.209139,9 9,9 C6.790861,9 5,10.790861 5,13 C5,15.209139 6.790861,17 9,17 Z M18,18 C18.5522847,18 19,17.5522847 19,17 C19,16.4477153 18.5522847,16 18,16 C17.4477153,16 17,16.4477153 17,17 C17,17.5522847 17.4477153,18 18,18 Z M5,21 C5.55228475,21 6,20.5522847 6,20 C6,19.4477153 5.55228475,19 5,19 C4.44771525,19 4,19.4477153 4,20 C4,20.5522847 4.44771525,21 5,21 Z" id="Combined-Shape" fill="#335EEA"/>
<path d="M19.5954729,5.83476152 L4.60883918,4.07162814 C4.23525261,4.02767678 3.86860536,4.19709197 3.65994764,4.51007855 L2,7 C15.3333333,7 22,7 22,7 C22,7 22,7 22,7 L22,7 C21.352294,6.35229396 20.5051936,5.94178748 19.5954729,5.83476152 Z" id="Path-98" fill="#335EEA" opacity="0.3"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 通用 / 素材</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-General-/-Clip" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M14,16 L12,16 L12,12.5 C12,11.6715729 11.3284271,11 10.5,11 C9.67157288,11 9,11.6715729 9,12.5 L9,17.5 C9,19.4329966 10.5670034,21 12.5,21 C14.4329966,21 16,19.4329966 16,17.5 L16,7.5 C16,5.56700338 14.4329966,4 12.5,4 L12,4 C10.3431458,4 9,5.34314575 9,7 L7,7 C7,4.23857625 9.23857625,2 12,2 L12.5,2 C15.5375661,2 18,4.46243388 18,7.5 L18,17.5 C18,20.5375661 15.5375661,23 12.5,23 C9.46243388,23 7,20.5375661 7,17.5 L7,12.5 C7,10.5670034 8.56700338,9 10.5,9 C12.4329966,9 14,10.5670034 14,12.5 L14,16 Z" id="Path-16" fill="#335EEA" transform="translate(12.500000, 12.500000) rotate(-315.000000) translate(-12.500000, -12.500000) "/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 通用 / 半心</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-General-/-Half-heart" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<polygon id="Shape" points="0 0 24 0 24 24 0 24"/>
<path d="M16.5,4.5 C14.8905,4.5 13.00825,6.32463215 12,7.5 C10.99175,6.32463215 9.1095,4.5 7.5,4.5 C4.651,4.5 3,6.72217984 3,9.55040872 C3,12.6834696 6,16 12,19.5 C18,16 21,12.75 21,9.75 C21,6.92177112 19.349,4.5 16.5,4.5 Z" id="Shape" fill="#335EEA" opacity="0.3"/>
<path d="M12,19.5 C6,16 3,12.6834696 3,9.55040872 C3,6.72217984 4.651,4.5 7.5,4.5 C9.1095,4.5 10.99175,6.32463215 12,7.5 L12,19.5 Z" id="Combined-Shape" fill="#335EEA"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 980 B

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 通用 / 隐藏</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-General-/-Hidden" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M19.2078777,9.84836149 C20.3303823,11.0178941 21,12 21,12 C21,12 16.9090909,18 12,18 C11.6893441,18 11.3879033,17.9864845 11.0955026,17.9607365 L19.2078777,9.84836149 Z" id="Combined-Shape" fill="#335EEA"/>
<path d="M14.5051465,6.49485351 L12,9 C10.3431458,9 9,10.3431458 9,12 L5.52661464,15.4733854 C3.75006453,13.8334911 3,12 3,12 C3,12 5.45454545,6 12,6 C12.8665422,6 13.7075911,6.18695134 14.5051465,6.49485351 Z" id="Combined-Shape" fill="#335EEA"/>
<rect id="Rectangle" fill="#335EEA" opacity="0.3" transform="translate(12.524621, 12.424621) rotate(-45.000000) translate(-12.524621, -12.424621) " x="3.02462111" y="11.4246212" width="19" height="2"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 主页 / 洗脸台#2</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Home-/-Сommode#2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M5.5,2 L18.5,2 C19.3284271,2 20,2.67157288 20,3.5 L20,6.5 C20,7.32842712 19.3284271,8 18.5,8 L5.5,8 C4.67157288,8 4,7.32842712 4,6.5 L4,3.5 C4,2.67157288 4.67157288,2 5.5,2 Z M11,4 C10.4477153,4 10,4.44771525 10,5 C10,5.55228475 10.4477153,6 11,6 L13,6 C13.5522847,6 14,5.55228475 14,5 C14,4.44771525 13.5522847,4 13,4 L11,4 Z" id="Combined-Shape" fill="#335EEA" opacity="0.3"/>
<path d="M5.5,9 L18.5,9 C19.3284271,9 20,9.67157288 20,10.5 L20,13.5 C20,14.3284271 19.3284271,15 18.5,15 L5.5,15 C4.67157288,15 4,14.3284271 4,13.5 L4,10.5 C4,9.67157288 4.67157288,9 5.5,9 Z M11,11 C10.4477153,11 10,11.4477153 10,12 C10,12.5522847 10.4477153,13 11,13 L13,13 C13.5522847,13 14,12.5522847 14,12 C14,11.4477153 13.5522847,11 13,11 L11,11 Z M5.5,16 L18.5,16 C19.3284271,16 20,16.6715729 20,17.5 L20,20.5 C20,21.3284271 19.3284271,22 18.5,22 L5.5,22 C4.67157288,22 4,21.3284271 4,20.5 L4,17.5 C4,16.6715729 4.67157288,16 5.5,16 Z M11,18 C10.4477153,18 10,18.4477153 10,19 C10,19.5522847 10.4477153,20 11,20 L13,20 C13.5522847,20 14,19.5522847 14,19 C14,18.4477153 13.5522847,18 13,18 L11,18 Z" id="Combined-Shape" fill="#335EEA"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 主页 / 鲜花#2</title>
<desc>通过 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Home-/-Flower#2" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<polygon id="bound" points="0 0 24 0 24 24 0 24"/>
<circle id="Oval-32" fill="#335EEA" opacity="0.3" cx="15" cy="17" r="5"/>
<circle id="Oval-32-Copy" fill="#335EEA" opacity="0.3" cx="9" cy="17" r="5"/>
<circle id="Oval-32-Copy-2" fill="#335EEA" opacity="0.3" cx="7" cy="11" r="5"/>
<circle id="Oval-32-Copy-3" fill="#335EEA" opacity="0.3" cx="17" cy="11" r="5"/>
<circle id="Oval-32-Copy-4" fill="#335EEA" opacity="0.3" cx="12" cy="7" r="5"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 948 B

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 媒体 / 播放视频</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Media-/-Airplay-video" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M7,15 C7.55228475,15 8,15.4477153 8,16 C8,16.5522847 7.55228475,17 7,17 L6,17 C4.34314575,17 3,15.6568542 3,14 L3,7 C3,5.34314575 4.34314575,4 6,4 L18,4 C19.6568542,4 21,5.34314575 21,7 L21,14 C21,15.6568542 19.6568542,17 18,17 L17,17 C16.4477153,17 16,16.5522847 16,16 C16,15.4477153 16.4477153,15 17,15 L18,15 C18.5522847,15 19,14.5522847 19,14 L19,7 C19,6.44771525 18.5522847,6 18,6 L6,6 C5.44771525,6 5,6.44771525 5,7 L5,14 C5,14.5522847 5.44771525,15 6,15 L7,15 Z" id="Combined-Shape" fill="#335EEA"/>
<polygon id="Path-15" fill="#335EEA" opacity="0.3" points="8 20 16 20 12 15"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 导航 / 双箭头</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Navigation-/-Angle-double-up" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<polygon id="Shape" points="0 0 24 0 24 24 0 24"/>
<path d="M8.2928955,10.2071068 C7.90237121,9.81658249 7.90237121,9.18341751 8.2928955,8.79289322 C8.6834198,8.40236893 9.31658478,8.40236893 9.70710907,8.79289322 L15.7071091,14.7928932 C16.085688,15.1714722 16.0989336,15.7810586 15.7371564,16.1757246 L10.2371564,22.1757246 C9.86396402,22.5828436 9.23139665,22.6103465 8.82427766,22.2371541 C8.41715867,21.8639617 8.38965574,21.2313944 8.76284815,20.8242754 L13.6158645,15.5300757 L8.2928955,10.2071068 Z" id="Path-94" fill="#335EEA" transform="translate(12.000003, 15.500003) scale(-1, 1) rotate(-90.000000) translate(-12.000003, -15.500003) "/>
<path d="M6.70710678,12.2071104 C6.31658249,12.5976347 5.68341751,12.5976347 5.29289322,12.2071104 C4.90236893,11.8165861 4.90236893,11.1834211 5.29289322,10.7928968 L11.2928932,4.79289682 C11.6714722,4.41431789 12.2810586,4.40107226 12.6757246,4.76284946 L18.6757246,10.2628495 C19.0828436,10.6360419 19.1103465,11.2686092 18.7371541,11.6757282 C18.3639617,12.0828472 17.7313944,12.1103502 17.3242754,11.7371577 L12.0300757,6.88414142 L6.70710678,12.2071104 Z" id="Path-94" fill="#335EEA" opacity="0.3" transform="translate(12.000003, 8.500003) scale(-1, 1) rotate(-360.000000) translate(-12.000003, -8.500003) "/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 导航/箭头-v</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Navigation-/-Arrows-v" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<polygon id="Bound" points="0 0 24 0 24 24 0 24"/>
<rect id="Rectangle-15" fill="#335EEA" opacity="0.3" transform="translate(12.000000, 12.000000) rotate(-90.000000) translate(-12.000000, -12.000000) " x="7" y="11" width="10" height="2" rx="1"/>
<path d="M6.70710678,8.70710678 C6.31658249,9.09763107 5.68341751,9.09763107 5.29289322,8.70710678 C4.90236893,8.31658249 4.90236893,7.68341751 5.29289322,7.29289322 L11.2928932,1.29289322 C11.6714722,0.914314282 12.2810586,0.90106866 12.6757246,1.26284586 L18.6757246,6.76284586 C19.0828436,7.13603827 19.1103465,7.76860564 18.7371541,8.17572463 C18.3639617,8.58284362 17.7313944,8.61034655 17.3242754,8.23715414 L12.0300757,3.38413782 L6.70710678,8.70710678 Z" id="Path-94" fill="#335EEA"/>
<path d="M6.70710678,22.7071068 C6.31658249,23.0976311 5.68341751,23.0976311 5.29289322,22.7071068 C4.90236893,22.3165825 4.90236893,21.6834175 5.29289322,21.2928932 L11.2928932,15.2928932 C11.6714722,14.9143143 12.2810586,14.9010687 12.6757246,15.2628459 L18.6757246,20.7628459 C19.0828436,21.1360383 19.1103465,21.7686056 18.7371541,22.1757246 C18.3639617,22.5828436 17.7313944,22.6103465 17.3242754,22.2371541 L12.0300757,17.3841378 L6.70710678,22.7071068 Z" id="Path-94-Copy" fill="#335EEA" transform="translate(12.000003, 18.999999) rotate(-180.000000) translate(-12.000003, -18.999999) "/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 导航 / 等待中</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Navigation-/-Waiting" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M19.5,10.5 L21,10.5 C21.8284271,10.5 22.5,11.1715729 22.5,12 C22.5,12.8284271 21.8284271,13.5 21,13.5 L19.5,13.5 C18.6715729,13.5 18,12.8284271 18,12 C18,11.1715729 18.6715729,10.5 19.5,10.5 Z M16.0606602,5.87132034 L17.1213203,4.81066017 C17.7071068,4.22487373 18.6568542,4.22487373 19.2426407,4.81066017 C19.8284271,5.39644661 19.8284271,6.34619408 19.2426407,6.93198052 L18.1819805,7.99264069 C17.5961941,8.57842712 16.6464466,8.57842712 16.0606602,7.99264069 C15.4748737,7.40685425 15.4748737,6.45710678 16.0606602,5.87132034 Z M16.0606602,18.1819805 C15.4748737,17.5961941 15.4748737,16.6464466 16.0606602,16.0606602 C16.6464466,15.4748737 17.5961941,15.4748737 18.1819805,16.0606602 L19.2426407,17.1213203 C19.8284271,17.7071068 19.8284271,18.6568542 19.2426407,19.2426407 C18.6568542,19.8284271 17.7071068,19.8284271 17.1213203,19.2426407 L16.0606602,18.1819805 Z M3,10.5 L4.5,10.5 C5.32842712,10.5 6,11.1715729 6,12 C6,12.8284271 5.32842712,13.5 4.5,13.5 L3,13.5 C2.17157288,13.5 1.5,12.8284271 1.5,12 C1.5,11.1715729 2.17157288,10.5 3,10.5 Z M12,1.5 C12.8284271,1.5 13.5,2.17157288 13.5,3 L13.5,4.5 C13.5,5.32842712 12.8284271,6 12,6 C11.1715729,6 10.5,5.32842712 10.5,4.5 L10.5,3 C10.5,2.17157288 11.1715729,1.5 12,1.5 Z M12,18 C12.8284271,18 13.5,18.6715729 13.5,19.5 L13.5,21 C13.5,21.8284271 12.8284271,22.5 12,22.5 C11.1715729,22.5 10.5,21.8284271 10.5,21 L10.5,19.5 C10.5,18.6715729 11.1715729,18 12,18 Z M4.81066017,4.81066017 C5.39644661,4.22487373 6.34619408,4.22487373 6.93198052,4.81066017 L7.99264069,5.87132034 C8.57842712,6.45710678 8.57842712,7.40685425 7.99264069,7.99264069 C7.40685425,8.57842712 6.45710678,8.57842712 5.87132034,7.99264069 L4.81066017,6.93198052 C4.22487373,6.34619408 4.22487373,5.39644661 4.81066017,4.81066017 Z M4.81066017,19.2426407 C4.22487373,18.6568542 4.22487373,17.7071068 4.81066017,17.1213203 L5.87132034,16.0606602 C6.45710678,15.4748737 7.40685425,15.4748737 7.99264069,16.0606602 C8.57842712,16.6464466 8.57842712,17.5961941 7.99264069,18.1819805 L6.93198052,19.2426407 C6.34619408,19.8284271 5.39644661,19.8284271 4.81066017,19.2426407 Z" id="Oval" fill="#335EEA" opacity="0.3"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 文本 / 编辑文本</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Text-/-Edit-text" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M3,19 L5,19 L5,21 L3,21 L3,19 Z M8,19 L10,19 L10,21 L8,21 L8,19 Z M13,19 L15,19 L15,21 L13,21 L13,19 Z M18,19 L20,19 L20,21 L18,21 L18,19 Z" id="Combined-Shape" fill="#335EEA" opacity="0.3"/>
<path d="M10.504,3.256 L12.466,3.256 L17.956,16 L15.364,16 L14.176,13.084 L8.65000004,13.084 L7.49800004,16 L4.96000004,16 L10.504,3.256 Z M13.384,11.14 L11.422,5.956 L9.42400004,11.14 L13.384,11.14 Z" id="A" fill="#335EEA"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 975 B

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 天气 / 雪#1</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Weather-/-Snow#1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<polygon id="Shape" points="0 0 24 0 24 24 0 24"/>
<path d="M5.74714567,14.0425758 C4.09410362,12.9740356 3,11.1147886 3,9 C3,5.6862915 5.6862915,3 9,3 C11.7957591,3 14.1449096,4.91215918 14.8109738,7.5 L17.25,7.5 C19.3210678,7.5 21,9.17893219 21,11.25 C21,13.3210678 19.3210678,15 17.25,15 L8.25,15 C7.28817895,15 6.41093178,14.6378962 5.74714567,14.0425758 Z" id="Combined-Shape" fill="#335EEA"/>
<path d="M8.90586666,16.1192596 C9.43933276,16.2622014 9.75591525,16.8105384 9.61297344,17.3440045 C9.47003163,17.8774706 8.92169467,18.1940531 8.38822857,18.0511113 C7.85476246,17.9081694 7.53817997,17.3598325 7.68112179,16.8263664 C7.8240636,16.2929003 8.37240055,15.9763178 8.90586666,16.1192596 Z M16.9058667,16.1192596 C17.4393328,16.2622014 17.7559153,16.8105384 17.6129734,17.3440045 C17.4700316,17.8774706 16.9216947,18.1940531 16.3882286,18.0511113 C15.8547625,17.9081694 15.53818,17.3598325 15.6811218,16.8263664 C15.8240636,16.2929003 16.3724006,15.9763178 16.9058667,16.1192596 Z M12.9058667,16.1192596 C13.4393328,16.2622014 13.7559153,16.8105384 13.6129734,17.3440045 C13.4700316,17.8774706 12.9216947,18.1940531 12.3882286,18.0511113 C11.8547625,17.9081694 11.53818,17.3598325 11.6811218,16.8263664 C11.8240636,16.2929003 12.3724006,15.9763178 12.9058667,16.1192596 Z M14.9058667,19.1192596 C15.4393328,19.2622014 15.7559153,19.8105384 15.6129734,20.3440045 C15.4700316,20.8774706 14.9216947,21.1940531 14.3882286,21.0511113 C13.8547625,20.9081694 13.53818,20.3598325 13.6811218,19.8263664 C13.8240636,19.2929003 14.3724006,18.9763178 14.9058667,19.1192596 Z M10.9058667,19.1192596 C11.4393328,19.2622014 11.7559153,19.8105384 11.6129734,20.3440045 C11.4700316,20.8774706 10.9216947,21.1940531 10.3882286,21.0511113 C9.85476246,20.9081694 9.53817997,20.3598325 9.68112179,19.8263664 C9.8240636,19.2929003 10.3724006,18.9763178 10.9058667,19.1192596 Z" id="Combined-Shape" fill="#335EEA" opacity="0.3"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24px" height="24px" viewBox="0 0 24 24" version="1.1">
<!-- Generator: Sketch 52.2 (67145) - http://www.bohemiancoding.com/sketch -->
<title>Stockholm-图标 / 天气 / 太阳</title>
<desc>用 Sketch 创建。</desc>
<g id="Stockholm-icons-/-Weather-/-Sun" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<rect id="bound" x="0" y="0" width="24" height="24"/>
<path d="M12,15 C10.3431458,15 9,13.6568542 9,12 C9,10.3431458 10.3431458,9 12,9 C13.6568542,9 15,10.3431458 15,12 C15,13.6568542 13.6568542,15 12,15 Z" id="Oval-8" fill="#335EEA"/>
<path d="M19.5,10.5 L21,10.5 C21.8284271,10.5 22.5,11.1715729 22.5,12 C22.5,12.8284271 21.8284271,13.5 21,13.5 L19.5,13.5 C18.6715729,13.5 18,12.8284271 18,12 C18,11.1715729 18.6715729,10.5 19.5,10.5 Z M16.0606602,5.87132034 L17.1213203,4.81066017 C17.7071068,4.22487373 18.6568542,4.22487373 19.2426407,4.81066017 C19.8284271,5.39644661 19.8284271,6.34619408 19.2426407,6.93198052 L18.1819805,7.99264069 C17.5961941,8.57842712 16.6464466,8.57842712 16.0606602,7.99264069 C15.4748737,7.40685425 15.4748737,6.45710678 16.0606602,5.87132034 Z M16.0606602,18.1819805 C15.4748737,17.5961941 15.4748737,16.6464466 16.0606602,16.0606602 C16.6464466,15.4748737 17.5961941,15.4748737 18.1819805,16.0606602 L19.2426407,17.1213203 C19.8284271,17.7071068 19.8284271,18.6568542 19.2426407,19.2426407 C18.6568542,19.8284271 17.7071068,19.8284271 17.1213203,19.2426407 L16.0606602,18.1819805 Z M3,10.5 L4.5,10.5 C5.32842712,10.5 6,11.1715729 6,12 C6,12.8284271 5.32842712,13.5 4.5,13.5 L3,13.5 C2.17157288,13.5 1.5,12.8284271 1.5,12 C1.5,11.1715729 2.17157288,10.5 3,10.5 Z M12,1.5 C12.8284271,1.5 13.5,2.17157288 13.5,3 L13.5,4.5 C13.5,5.32842712 12.8284271,6 12,6 C11.1715729,6 10.5,5.32842712 10.5,4.5 L10.5,3 C10.5,2.17157288 11.1715729,1.5 12,1.5 Z M12,18 C12.8284271,18 13.5,18.6715729 13.5,19.5 L13.5,21 C13.5,21.8284271 12.8284271,22.5 12,22.5 C11.1715729,22.5 10.5,21.8284271 10.5,21 L10.5,19.5 C10.5,18.6715729 11.1715729,18 12,18 Z M4.81066017,4.81066017 C5.39644661,4.22487373 6.34619408,4.22487373 6.93198052,4.81066017 L7.99264069,5.87132034 C8.57842712,6.45710678 8.57842712,7.40685425 7.99264069,7.99264069 C7.40685425,8.57842712 6.45710678,8.57842712 5.87132034,7.99264069 L4.81066017,6.93198052 C4.22487373,6.34619408 4.22487373,5.39644661 4.81066017,4.81066017 Z M4.81066017,19.2426407 C4.22487373,18.6568542 4.22487373,17.7071068 4.81066017,17.1213203 L5.87132034,16.0606602 C6.45710678,15.4748737 7.40685425,15.4748737 7.99264069,16.0606602 C8.57842712,16.6464466 8.57842712,17.5961941 7.99264069,18.1819805 L6.93198052,19.2426407 C6.34619408,19.8284271 5.39644661,19.8284271 4.81066017,19.2426407 Z" id="Oval" fill="#335EEA" opacity="0.3"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1,96 +0,0 @@
---
title: Solana ABI管理流程
---
本文件提出了Solana ABI管理流程。 ABI管理流程是一种工程实践和一个支持性的技术框架以避免引入意外的不兼容的ABI变化。
# 面临的问题
Solana ABI(集群的二进制接口) 目前仅由实现隐式定义,需要非常仔细的观察才能注意到破坏性的变化。 这使得在不重启账本的情况下,在现有集群上升级软件非常困难。
# 需求和目标
- 意外的ABI变化可以被机械地检测为CI故障
- 新的实现必须能够处理最老的数据(世纪以来),一旦我们进入主网。
- 这个建议的目的是保护ABI同时通过选择机械过程而不是非常长的人为驱动的审计过程来维持相当快速的发展。
- 一旦经过加密签名数据blob必须是相同的所以无论在线系统的入站和出站都不可能进行原地数据格式更新。 另外,考虑到我们要处理的交易量,最好不要进行追溯性的就地更新。
# 解决方案
我们需要一个系统性的保证,在修改源码时不破坏集群,而不是自然的人为尽职,因为人为尽职被假定为经常发生故障。
为此我们引入了一种机制在源代码中对每一个与ABI相关的事物(`struct`s, `enum`s) 用新的`#[frozen_abi]`属性进行标记。 它通过`ser::Serialize`从其字段的类型中提取硬编码的摘要值。 而且该属性会自动生成一个单元测试试图检测任何未经批准的对标记的ABI相关事物的更改。
但是检测不可能是完全的无论我们如何努力静态分析源代码仍然有可能破坏ABI。 例如,这包括非`派生`的手写`ser::Serialize`、底层库的实现变化(例如`bincode`)、CPU架构差异。 对这些可能的ABI不兼容的检测不在这个ABI管理的范围之内。
# 定义
ABI项目/类型用于序列化的各种类型共同构成任何系统组件的整个ABI。 例如,这些类型包括`struct`s和`enum`s。
ABI项目摘要。从ABI项的字段的类型信息导出的一些固定的哈希值。
# 示例
```patch
+#[frozen_abi(digest="eXSMM7b89VY72V...")]
#[derive(Serialize, Default, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Vote {
/// A stack of votes starting with the oldest vote
pub slots: Vec<Slot>,
/// signature of the bank's state at the last slot
pub hash: Hash,
}
```
# 开发者的工作流程
为了知道新的ABI项目的摘要开发人员可以用一个随机的摘要值添加`frozen_abi`,然后运行单元测试,并从断言测试错误信息中用正确的摘要来替换它。
一般来说,一旦我们添加了`frozen_abi`,并且它的变化被发布在稳定版本频道中,它的摘要应该永远不会改变。 如果需要这样的改变,我们应该选择定义一个新的`struct`,比如`FooV1`。 而特殊的发布流程,比如硬分叉,则应该类似。
# 实施说明
我们使用某种程度的宏机制来自动生成单元测试并从ABI项目中计算出一个摘要。 通过巧妙地使用`serde::Serialize`(`[1]`)和`any::type_name`(`[2]`)可以实现这一点。 对于类似的实现先例Parity Technologies`[3]`中的`ink`可以作为参考。
# 实现细节
本实施方案的目标是尽可能自动检测ABI的意外变化。 为此结构性ABI信息的摘要是以最大努力的准确性和稳定性计算的。
当ABI摘要检查运行时通过重复使用`serde`的序列化功能、过程宏和通用特殊化功能对ABI项的字段的ABI进行递归摘要动态计算出ABI摘要。 然后,检查`assert!`其最终的摘要值与`frozen_abi`属性中指定的相同。
为了实现这一点,它创建了一个该类型的实例和一个自定义的`Serializer`实例,为`serde`递归遍历它的字段,就像真正的序列化实例一样。 这种遍历必须通过`serde`来完成,才能真正捕捉到什么样的数据实际上会被`serde`序列化,即使考虑到定制的非`派生`的`Serialize`接口实现。
# ABI摘要过程
这一部分有点复杂。 有三个相互依赖的部分:`AbiExample`、`AbiDigester`和`AbiEnumVisitor`。
首先,生成的测试会创建一个摘要类型的实例,这个实例有一个叫做`AbiExample`的接口,它应该像`Serialize`一样为所有的摘要类型实现,并像`Default`接口一样返回`Self`。 通常情况下,它是通过通用的接口特殊化来提供给大多数常见类型的。 也可以为`struct`和`enum`进行`派生`,如果需要,也可以手工编写。
自定义的`serializer`被称为`AbiDigester`。 而当它被`serde`调用来序列化一些数据时它会尽可能地递归收集ABI信息。 `AbiDigester`根据数据类型的不同对ABI摘要的内部状态进行不同的更新。 这个逻辑是通过一个名为`AbiEnumVisitor`的接口为每个`enum`类型专门重定向的。 顾名思义,没有必要为其他类型实现`AbiEnumVisitor`。
总结一下这种相互作用,`serde`与`AbiDigester`串联处理递归序列化控制流。 测试中的初始入口点和子`AbiDigester`使用`AbiExample`递归地创建一个示例对象层次图。 而`AbiDigester`使用`AbiEnumVisitor`使用构建的样本查询实际的ABI信息。
`Default`对于`AbiExample`来说是不够的。 多种集合的`::default()`是空的,但我们想用实际的项目来摘要它们。 而且ABI摘要不能只用`AbiEnumVisitor`来实现。 需要`AbiExample`是因为需要一个实际的类型实例来通过`serde`实际遍历数据。
另一方面ABI摘要也不能只用`AbiExample`来完成。 需要`AbiEnumVisitor`,因为一个`enum`的所有变体不能只用它的一个变体作为ABI实例来遍历。
可摘要的信息:
- rust的类型名称
- `serde`的数据类型名称。
- `struct`中的所有字段
- `enum`中的所有变体。
- `struct`:正常(`struct {...}`) 和元组式(`struct(...)`)
- `enum`:正常变体和`struct`-和`tuple`-风格。
- 属性:`serde(serialize_with=...)`和`serde(skip)`
不可摘要的信息:
- `AbiExample`提供的样本未触及的任何自定义序列化代码路径。 (技术上不可能)
- 属(必须是具体类型;对具体类型别名使用`frozen_abi`)
# 参考文献
1. [(De)Serialization with type info · Issue #1095 · serde-rs/serde](https://github.com/serde-rs/serde/issues/1095#issuecomment-345483479)
2. [`std::any::type_name` - Rust](https://doc.rust-lang.org/std/any/fn.type_name.html)
3. [Parity's ink to write smart contracts](https://github.com/paritytech/ink)

View File

@ -1,37 +0,0 @@
---
title: 银行时间戳更正
---
每家银行都有一个时间戳存放在时钟sysvar中用来评估基于时间的质押账户锁定情况。 然而,自创世纪以来,这个值一直是基于理论上的每秒插槽数而不是现实,所以它是相当不准确的。 这给锁仓带来了问题,因为在锁仓设定到期的日期(或临近的任何时候),账户都不会登记为无锁仓。
块时间已经被估计缓存在Blockstore和长期存储中使用[验证者时间戳预言机](validator-timestamp-oracle.md);这个数据提供了一个机会,使银行时间戳与现实世界的时间更接近。
所提出的实施方案的总体轮廓如下。
- 插槽使用验证者提供的时间戳来修正每个银行时间戳。
- 插槽更新验证者提供的时间戳计算,使用质押加权中值,而不是质押加权平均值。
- 插槽对时间戳校正进行约束,使其不能偏离预期的理论估计值太远。
## 时间戳更正
在每一个新的银行上,使用验证者时间戳预言机数据实时计算一个现实的时间戳估计值。 如果银行的时间戳大于或等于前一个银行的时间戳,那么银行的时间戳就会被修正到这个值。 也就是说,时间不应该倒退,所以被锁住的账户可能会因为修正而被释放,但一旦被释放,账户永远不能因为时间修正而被重新锁住。
### 计算质押加权中值时间戳。
为了计算特定银行的估计时间戳,运行时首先需要从活动验证者集中获取最近的投票时间戳。 `Bank::vote_accounts()`方法提供了投票账户的状态这些账户可以被过滤到所有账户其最近的时间戳是在上一个epoch内提供的。
从每个投票时间戳中使用epoch的目标ns_per_slot计算出当前银行的估计值用于银行插槽和时间戳插槽之间的任何差 。每个时间戳估计值都与委托给该投票账户的质押相关联,所有时间戳都被收集起来,以创建一个质押加权的时间戳分布。
从这组时间戳中,选择质押加权中值时间戳--即50%的质押人估计时间戳较大或相等50%的质押人估计时间戳较小或相等的时间戳--作为潜在的更正时间戳。
这种质押加权的中值时间戳比质押加权的平均值更可取,因为在平均值计算中,质押乘以提议的时间戳,使得质押很小的节点仍然可以通过提议一个很大或很小的时间戳对结果的时间戳有很大的影响。 例如,使用之前的`calculate_stake_weighted_timestamp()`方法一个拥有0.00003%质押的节点提出一个`i64::MAX`的时间戳就可以将时间戳前移97000年!
### 边界时间戳
除了防止时间倒退,我们还可以通过将修正后的时间戳与理论上的预期时间的偏差限制在一个可接受的水平上来防止恶意活动。
这个提议建议允许每个时间戳从纪元开始后与预期时间的偏差最多为25%。
为了计算时间戳的偏差每个银行需要在时钟sysvar中记录`epoch_start_timestamp`。 这个值被设置为每个epoch的第一个插槽的`Clock::unix_timestamp`。
然后,运行时根据修正后的时间戳,比较自纪元开始以来的预期经过时间和建议的经过时间。 如果修正后的经过时间在预期的+/插槽25%以内,则接受修正后的时间戳。 否则,它将被限制在可接受的偏差范围内。

View File

@ -1,68 +0,0 @@
---
title: 承诺
---
承诺度量旨在为客户提供一个衡量特定区块上的网络确认和利益水平的标准。 然后,客户可以使用这些信息来推导出自己的承诺度量。
# 计算远程调用
客户端可以通过`get_block_commitment(s: Signature) -> BlockCommitment`,使用远程调用向验证节点请求签名`s`的承诺指标。 `BlockCommitment`结构包含一个u64`[u64MAX_CONFIRMATIONS]`的数组。 这个数组表示验证节点投票的最后一个区块`M`时,包含签名`s`的特定区块`N`的承诺度量。
`BlockCommitment`数组中索引`i`处的条目`s`意味着验证节点观察到`s`在某一区块`M`中观察到的集群中的总质押达到`i`个确认的区块`N`。 这个数组中会有`MAX_CONFIRMATIONS`元素代表从1到`MAX_CONFIRMATIONS`的所有可能的确认数。
# 承诺度量的计算
建立这个`BlockCommitment`结构利用了为建立共识而进行的计算。 `consensus.rs`中的`collect_vote_lockouts`函数建立了一个HashMap其中每个条目的形式是`(b, s)`,其中`s`是银行`b`的质押数量。
对可投票候选银行`b`的计算如下。
```text
let output: HashMap<b, Stake> = HashMap::new();
for vote_account in b.vote_accounts {
for v in vote_account.vote_stack {
for a in ancestors(v) {
f(*output.get_mut(a), vote_account, v);
}
}
}
```
其中`f`是一些累积函数,它用一些可从投票`v`和`vote_account`派生的数据(stake、lockout等) 来修改插槽`a`的`stake`条目。 这里注意,这里的`ancestors`只包括当前状态缓存中存在的插槽。 比状态缓存中存在的更早的银行的签名无论如何也查询不到,所以这里的承诺计算中不包括这些银行。
现在,我们自然可以通过以下方法来增强上述计算,为每一个银行`b`也建立一个`BlockCommitment`数组。
1. 增加一个`ForkCommitmentCache`来收集`BlockCommitment`结构
2. 用`f`代替`f'`,使上述计算也为每一个银行`b`建立这个`BlockCommitment`。
由于1) 是不是很重要所以我们将继续讨论2) 的细节。
在继续之前,值得注意的是,对于某个验证节点的投票账户`a`,该验证节点在插槽`s`上的本地确认数为`v.num_confirmations`,其中`v`是投票堆栈`a.vails`中最小的一票,这样`v.slot >= s`(即不需要查看任何大于v的投票>,因为确认数会更低)。
现在更具体的说,我们把上面的计算增强为。
```text
let output: HashMap<b, Stake> = HashMap::new();
let fork_commitment_cache = ForkCommitmentCache::default();
for vote_account in b.vote_accounts {
// vote stack is sorted from oldest vote to newest vote
for (v1, v2) in vote_account.vote_stack.windows(2) {
for a in ancestors(v1).difference(ancestors(v2)) {
f'(*output.get_mut(a), *fork_commitment_cache.get_mut(a), vote_account, v);
}
}
}
```
其中`f'`被定义为:
```text
fn f`(
stake: &mut Stake,
some_ancestor: &mut BlockCommitment,
vote_account: VoteAccount,
v: Vote, total_stake: u64
){
f(stake, vote_account, v);
*some_ancestor.commitment[v.num_confirmations] += vote_account.stake;
}
```

View File

@ -1,84 +0,0 @@
---
title: 持久交易编号Nonces
---
## 问题
为了防止重花Solana 交易包含一个有“最近”块哈希值的非空值。 包含太久的(撰文时为 ~2分钟) 区块哈希交易被网络拒绝为无效。 很不幸,某些情况下(例如托管服务),需要更多时间来生成交易的签名。 需要一种机制来支持这些潜在的线下网络参与者。
## 需求
1. 交易签名必须包括编号值
2. 即使是在签名密钥披露的情况下nonce 也不可以重复使用。
## 基于合约的解决办法
这里我们描述了一种基于合约的解决方法,其中客户端可以在最近一次交易的 `recent_blockhash` 字段中“存放”一个未来可以使用的 nonce 值。 这个方法类似于通过一些 CPU ISA 实现的比较和交换原子指令。
当使用后续的 nonce 时,客户端必须首先从账户数据查询它的值。 现在的交易是正常的,但需要满足以下附加要求:
1. 后续的 nonce 值用于 `recent_blockhash` 字段
2. `AdvanceNonceAccount` 指令是交易中第一次发出的
### 合约机制
未完成工作svgbob 将其变成一个流程图
```text
Start
Create Account
state = Uninitialized
NonceInstruction
if state == Uninitialized
if account.balance < rent_exempt
error InsufficientFunds
state = Initialized
elif state != Initialized
error BadState
if sysvar.recent_blockhashes.is_empty()
error EmptyRecentBlockhashes
if !sysvar.recent_blockhashes.contains(stored_nonce)
error NotReady
stored_hash = sysvar.recent_blockhashes[0]
success
WithdrawInstruction(to, lamports)
if state == Uninitialized
if !signers.contains(owner)
error MissingRequiredSignatures
elif state == Initialized
if !sysvar.recent_blockhashes.contains(stored_nonce)
error NotReady
if lamports != account.balance && lamports + rent_exempt > account.balance
error InsufficientFunds
account.balance -= lamports
to.balance += lamports
success
```
客户端想要使用该功能,首先需要在系统程序下创建一个 nonce 帐户。 此帐户将处于 `Uninitialized` 状态,且没有存储哈希,因此无法使用。
要初始化该新创建的帐户,必须发出 `InitializeNonceAccount` 指令。 该指令需要一个 `Pubkey`参数,它位于账户 [授权](../offline-signing/durable-nonce.md#nonce-authority) 中。 Nonce 帐户必须是 [rent-exempt](rent.md#two-tiered-rent-regime) 状态,才能满足数据持续性功能的要求, 因此它要求初始化之前先存入足够的 lamports。 初始化成功后,集群最近的区块哈希与指定 nonce 授权的 `Pubkey` 将一同存储。
`AdvanceNonceAccount` 指令用于管理帐户存储的 nonce 值。 它在账户的状态数据中存储集群最新的区块哈希,如果与已存储的值相匹配,那么会提示失败。 这个检查可以防止在同一个区块内重新广播交易。
由于 nonce 帐户的 [免租](rent.md#two-tiered-rent-regime) 要求,一个自定义提现指令用于将资产从帐户中移出。 `WithdrawNonceAccount` 指令需要一个单一参数,提示取款信号,强制免除租金,防止账户余额下降至低于免租金的最低值。 该检查的一个例外情况是,最终余额是零,从而让账户能够删除。 这个账户关闭详细信息还有一个额外要求,即存储的 nonce 值必须与集群最近的区块不匹配, 正如 `AdvanceNonceAccount`
账户的 [nonce authority](../offline-signing/durable-nonce.md#nonce-authority) 可以通过 `AuthorizeNonceAccount` 说明进行更改。 它需要传入一个参数,新授权的 `Pubkey`。 执行该指令将把完全的帐户及其余额控制权转移给新的授权。
> `AdvanceNonceAccount``WithdrawNonceAccount` 和 `AuthorizeNonceAccount` 都需要当前 [nonce authority](../offline-signing/durable-nonce.md#nonce-authority) 才能签署交易。
### 运行时Runtime支持
合约本身并不足以实现这个功能。 为了在交易上强制执行一个现有的`recent_blockhash`并防止通过失败的交易重放来窃取费用runtime的修改是必要的。
任何未能通过通常的`check_hash_age`验证的交易将被测试为持久交易Nonce。 这是由包括一个`AdvanceNonceAccount`指令作为交易中的第一条指令发出的信号。
如果runtime确定使用了一个持久事务Nonce它将采取以下额外的操作来验证事务
1. 加载`Nonce`指令中指定的`NonceAccount`。
2. `NonceAccount` 的数据字段反序列化`NonceState`,并确认其处于`Initialized`状态。
3. 存储在`NonceAccount`中的nonce值与交易的`recent_blockhash`字段中指定的nonce值进行匹配测试。
如果上述三项检查都成功,则允许交易继续验证。
由于以`InstructionError`失败的交易会被收取费用,并且其状态的改变会被回滚,所以如果`AdvanceNonceAccount`指令被回滚,则费用可能会被盗。 恶意验证者可以重放失败的交易直到存储的nonce被成功推进。 Runtime的更改可以防止这种行为。 当一个持久的nonce事务失败时除了`AdvanceNonceAccount`指令外,还有一个`InstructionError`nonce账户会像往常一样被回滚到执行前的状态。 然后runtime将其nonce值和高级nonce账户存储起来就像已经成功了。

View File

@ -1,9 +0,0 @@
---
title: 经济可持续性
---
**规则有可能发生变化。**
长期经济可持续性是Solana经济设计的指导原则之一。 虽然不可能预测去中心化经济将如何随着时间的推移而发展,特别是具有灵活的去中心化治理的经济体,但我们可以安排经济组成部分,以便在某些条件下,一个可持续的经济可能在长期内形成。 在Solana网络的情况下这些组成部分采取代币发行(通过通货膨胀) 和代币销毁的形式。
Solana 矿池的主要汇兑是验证节点奖励。 反通货膨胀是一个统一的、协议规定的、经过调整的、每笔交易费用的百分比。

View File

@ -1,14 +0,0 @@
---
title: 经济设计MVP
---
**规则有可能发生变化。**
前面几节在[经济设计概述](ed_overview.md)中概述了一个可持续发展的Solana经济的长期愿景。 当然,我们并不期望最终的实施能与上面描述的内容完全一致。 我们打算在整个实施阶段(即预试网、试网、主网)与网络利益相关者充分接触,以确保该系统支持并代表各网络参与者的利益。 然而实现这一目标的第一步是概述一些期望的MVP经济功能以便为早期的预试验网和试验网参与者提供。 下面是一个粗略的草图,概述了基本的经济功能,从中可以开发出一个更完整的功能系统。
## MVP 经济功能
- 水龙头向验证节点提供测试网SOL以便进行挖矿和应用开发。
- 通过网络膨胀来奖励验证节点的机制。
- 能够将代币委托给验证节点节点。
- 验证节点设置委托代币利息的佣金费用。

View File

@ -1,19 +0,0 @@
---
title: 集群经济学
---
**根据实际情况,规则可能发生变化。 请在Solana论坛上关注最近的经济讨论https://forums.Solana.com。**
Solana的加密经济系统旨在促进健康、长期的自给经济参与者的激励机制与网络的安全性和去中心化保持一致。 这个经济的主要参与者是验证客户端。 下面讨论他们对网络、状态验证的贡献及其必要的激励机制。
参与者汇兑的主要渠道被称为基于协议(通货膨胀) 的奖励和交易费用。 基于协议的奖励是由一个全球的、协议定义的、通货膨胀率发行的。 这些奖励将构成交付给验证客户的总奖励,剩余的来源于交易费用。 在网络的早期,基于协议的奖励很可能会根据预定义的发行时间表进行部署,将推动大多数参与者参与网络的动机。
这些基于协议的奖励将在网络上的主动质押代币中分配将是全球供应膨胀率的结果按Solana纪元计算并在活跃的验证者集中分配。 如下文所讨论的那样,每年的通货膨胀率是基于一个预先确定的去通货膨胀时间表。 这为网络提供了货币供应的可预测性,支持长期的经济稳定和安全。
交易费用是以市场为基础的参与者之间的转移,附加在网络互动中,作为纳入和执行一项拟议交易的必要动机和补偿。 下文还将讨论通过对每笔交易费进行部分销毁来实现长期经济稳定和分叉保护的机制。
下文**图1**为Solana加密经济设计的高层示意图。 验证客户端经济的具体细节在以下章节中作了介绍: [验证客户端经济学](ed_validation_client_economics/ed_vce_overview.md)[通货膨胀计划](ed_validation_client_economics/ed_vce_state_validation_protocol_based_rewards.md) 以及 [交易费](ed_validation_client_economics/ed_vce_state_validation_transaction_fees.md)。 另外,在标题为[验证质押委托](ed_validation_client_economics/ed_vce_validation_stake_delegation.md)的章节,最后还讨论了验证节点委托的机会和市场。 此外,在[存储租金经济学](ed_storage_rent_economics.md)中,我们描述了存储租金的实现,以说明维持账本活跃状态的外部性成本。 [经济设计MVP](ed_mvp.md)章节讨论了MVP经济设计的特征概要。
![](/img/economic_design_infl_230719.png)
**图1**: Solana 经济奖励设计的简明概述。

View File

@ -1,7 +0,0 @@
---
title: 参考文献
---
1. [https://blog.eferum.org/2016/07/27/inflation-transaction-fees-cryptocurrency-monetary-policy/](https://blog.ethereum.org/2016/07/27/inflation-transaction-fees-cryptocurrency-monetary-policy/)
2. [https://med.com/solana-labs/how to create-decentralized-storage-for-a-multi-petabyte-digital-ledger-2499a3a8c281](https://medium.com/solana-labs/how-to-create-decentralized-storage-for-a-multi-petabyte-digital-ledger-2499a3a8c281)
3. [https://med.com/solana-labs/how to create-decentralized-storage-for-a-multi-petabyte-digital-ledger-2499a3a8c281](https://medium.com/solana-labs/how-to-create-decentralized-storage-for-a-multi-petabyte-digital-ledger-2499a3a8c281)

View File

@ -1,17 +0,0 @@
---
title: 存储租赁经济
---
提交 Solana 账本的每笔交易都要支付费用。 理论上,验证和添加数据到账本的交易费应当由提交人支付,验证节点进行收费。 在此过程中未计入的活跃账本状态的中期存储,则需要由轮换的验证节点所维护。 这种类型的储存不仅给验证节点带来费用,而且也给更广泛的网络带来费用,因为活跃状态的增加会导致数据传输和验证间接费用的增加。 为了计算这些费用,此处介绍储存租金的初步设计和实施。
存储租金可以通过下述两种方法支付:
方法1设置然后忽略它
采用这种办法,有两年租金保证金的账户将免收网络租金费用。 保持这个最低平衡以后,网络整体上将受益于流动性降低,账户持有人可以相信他们的 `Account::data` 将被保留以便获得持续的存取/使用。
方法2按字节支付
如果一个帐户存入的租金不到两年,网络在每个 epoch 收取租金,并在下一个 epoch 继续计算租金。 这笔租金按初始规定的费率扣除,每千字节以 lamports 扣除。
关于此设计的技术实现详细信息,请参阅 [Rent](../rent.md) 章节。

View File

@ -1,9 +0,0 @@
---
title: 验证节点经济模型
---
**规则一直在改变 关注Solana论坛最近的经济模型讨论https://forums.solana.com**
验证节点质押代币以获取大量的的佣金收入 这是对验证节点的补偿,验证节点提供 (CPU+GPU) 计算资源来验证并投票给定的 PoH 状态。 这些基于协议的奖励是通过算法实现通货紧缩计划来决定的,作为代币总量的一部分用途。 预计网络将启动初期年通货膨胀率约为8%每年减少15%直到长期稳定在1.5% 然而,这些参数尚待社区最后确定。 这些发行将被分割并分发给参与验证者。 发放令牌中约有95%分配给验证者作为初始奖励(其余5%保留给基金会作为业务费用)。 因为该网络将在定量的通货膨胀奖励中分配给质押代币的验证节点, 质押的收益和质押的总量成一个比例函数。
此外,验证节点可以通过状态验证交易赚取费用收入。 为了明确起见,我们分别描述了这些收入分配的设计和动机:基于状态-验证协议的奖励以及状态-验证交易费用和租金。

View File

@ -1,63 +0,0 @@
---
title: 通货膨胀规划
---
**规则有可能发生变化。 请在Solana论坛上关注最近的经济讨论https://forums.Solana.com。**
验证节点客户端在Solana网络中具有两种功能作用。
- 验证节点对他们观察到的PoH的当前全球状态进行\(投票\)。
- 验证节点在利益加权的循环计划中被选为 "领导者"在此期间他们负责收集未完成的交易并将其纳入其观察到的PoH中从而更新网络的全球状态并提供区块链的连续性。
验证节点客户端对这些服务的奖励将在每个Solana纪元结束时分配。 如前所述,验证节点-客户的报酬是通过基于协议的年度通货膨胀率收取的佣金来提供的,该佣金按照每个验证节点节点的质押权重比例分配(见下文),同时还包括每次领导者轮换期间可用的领导者主张的交易费用。 举例说明 即在给定的验证节点-客户端被选为领导者期间,它有机会保留每笔交易费的一部分,减去协议规定的被销毁的金额 (见[验证节点-客户端状态交易费](ed_vce_state_validation_transaction_fees.md))。
验证节点客户端收到的协议级别有效质押收益率/(%/),每一个纪元将是以下因素的函数:
- 验证节点当前的全局通货膨胀率,由预先确定的去通货膨胀发行计划表推导出来的(见[验证客户端经济学](ed_vce_overview.md))。
- 验证节点当前总循环供应量中质押的SOL占比。
- 验证节点服务收取的佣金。
- 验证节点在上一个纪元中,给定验证节点的在线/参与\[的投票 %\]。
第一个因素仅是协议参数的函数\(即独立于验证节点在给定纪元中的行为\),其结果是设计了一个膨胀时间表,以激励早期参与,提供明确的货币稳定性,并在网络中提供最佳的安全性。
作为理解*通胀计划*对Solana经济的第一个影响我们模拟了在当前研究的通胀时间表参数范围内代币发行随时间推移可能出现的上下限范围。
具体而言:
- *初始通货膨胀率*: 7-9%
- *通货膨胀率降低比例*: -14-16%
- *长期通货膨胀率*: 1-2%
使用这些范围来模拟一些可能的通货膨胀表,我们可以探索一段时间内的通货膨胀:
![](/img/p_inflation_schedule_ranges_w_comments.png)
在上图中,确定了范围的平均值,以说明每个参数的贡献。 从这些模拟的*通货膨胀表*中,我们还可以推算出一段时间内代币发行的范围。
![](/img/p_total_supply_ranges.png)
最后,如果我们引入一个额外的参数,也就是之前讨论过的*质押SOL百分比*我们就可以估算出质押SOL的*质押收益*。
%~\text{SOL Staked} = \frac{\text{Total SOL Staked}}{\text{Total Current Supply}} CONTEXT
在这种情况下,由于*质押SOL百分比*是一个必须估计的参数(不同于*通胀表*参数),所以使用具体的*通胀表*参数,探索*质押SOL百分比*的范围比较容易。 在下面的例子,我们选择了上面探讨的参数范围的中间值:
- *初始通货膨胀率*: 8%
- *通货膨胀率降低比例*: -15%
- *长期通货膨胀率*: 1.5%
根据投资者和验证节点社区的反馈,以及在类似权益证明协议中观察到的情况,*质押SOL百分比*的范围在60%-90%之间,我们认为这涵盖了我们预期观察到的可能范围。
![](/img/p_ex_staked_yields.png)
同样上面显示的是Solana网络的一个例子在指定的*通货膨胀时间表*下,一个质押者可能会期望随着时间的推移而获得的*质押收益*。 这是一个理想化的*质押收益*,因为它忽略了验证节点正常运行时间对奖励的影响,验证节点佣金,潜在的收益率节流和潜在的罚没事件。 此外,它还忽略了*质押SOL百分比*是动态设计的——它的经济激励由*通货膨胀表*设置。
### 调整后的质押收益
质押代币潜力盈利的完整评估应考虑到质押*代币稀释*及其对质押收益率的影响。 为此,我们将*调整后的质押收益*定义为:由于通货膨胀发行量的分布而导致的质押代币在流通量占比的变化。 即 通货膨胀的正向稀释效应。
我们可以将*调整后的质押收益*作为通货膨胀率和网络上的质押代币百分比的函数来考察。 我们可以在这里看到各种质押占比的情况。
![](/img/p_ex_staked_dilution.png)

View File

@ -1,20 +0,0 @@
---
title: 状态验证交易费
---
**规则有可能发生变化。**
网络发送的每一笔交易,如果要由当前的领导者验证客户端处理并确认为全球状态交易,必须包含一笔交易费。 交易费在Solana经济设计中提供了许多好处例如
- 为验证者网络提供处理状态交易所需的CPU/GPU资源的单位补偿。
- 通过引入真实的交易成本来减少网络垃圾信息。
- 为交易市场开辟渠道,以激励验证客户端在其作为领导者的职能中收集和处理提交的交易。
- 并通过协议捕获的每笔交易的最低费用金额为网络提供潜在的长期经济稳定性,如下所述。
当前许多区块链经济体(如比特币、以太坊),在短期内依靠基于协议的奖励来支持经济,并假设通过交易费产生的收入将在长期内支持经济,当协议衍生的奖励到期时。 为了通过基于协议的奖励和交易费创造一个可持续发展的经济,每笔交易费中固定的部分被销毁,剩余的费用将归当前处理交易的领导者所有。 一个预定的全球通货膨胀率为通过上述过程分配给验证客户端的奖励提供了来源。
交易费由网络集群根据最近的历史吞吐量来设置,参见[拥堵驱动费用](../../transaction-fees.md#congestion-driven-fees)。 每个交易费的最低价格可以根据历史gas费动态调整。 通过这种方式,协议可以使用最低费用来锁定所需的硬件利用率。 通过监控协议指定的gas使用量与期望的、目标使用量之间的关系可以提高/降低最低费用,这反过来应该降低/提高每个区块的实际gas使用量直到它达到目标量。 这个调整过程可以被认为是类似于比特币协议中的难度调整算法,不过在这种情况下,它是在调整最低交易费用,以引导交易处理硬件使用量达到预期水平。
如前所述,每笔交易费中都有固定比例要被销毁。 这种设计的意图是保留领导激励,在领导槽时间内包含尽可能多的交易,同时提供一个限制通货膨胀的机制,以防止 "逃税 "攻击\(即侧通道费用支付)[1](../ed_references.md)。
此外,费用销毁也可以作为分叉选择的一个考虑因素。 在 PoH 分叉有一个恶意的、审查的领导者的情况下,由于审查所损失的费用,我们希望被破坏的总费用比可比的诚实分叉要少。 如果审查领导者要补偿这些损失的协议费,他们就必须自己替换掉自己分叉的费用销毁,从而有可能降低首先进行审查的动机。

View File

@ -1,28 +0,0 @@
---
title: 验证节点质押委托
---
**可作出修改。**
运行一个 Solana 验证客户端需要的预先硬件投入成本相对较小。 **表 2** 提供了一个支持 ~1M tx/s 的示例硬件配置,预计其“现货”费用为:
| 组件: | 示例: | 费用估计 |
|:---------- |:------------------------------------------------ |:--------- |
| GPU | 2x 2080 Ti | \$2500 |
| 或者 | 4x 1080 Ti | \$2800 |
| 操作系统/账本存储 | Samsung 860 Evo 2TB | \$370 |
| 账户存储 | 2x Samsung 970 Pro M.2 512GB | \$340 |
| RAM | 32 Gb | \$300 |
| 主板 | AMD x399 | \$400 |
| CPU | AMD Threadriper 2920x | \$650 |
| 案例: | | \$100 |
| 电量 | EVGA 1600W | \$300 |
| 网络 | &gt; 500 mbps | |
| 网络 \(1\) | Google webpass business bay area 1gbps unlimited | \$5500/月 |
| 网络 \(2\) | Hurricane Electric bay area colo 1gbps | \$500/月 |
**表 2** 示例了用于运行 Solana 客户端的高端硬件安装程序。
尽管从资本投资的角度来看搭建一个验证节点的门槛很低在任何发展中国家经济中都是如此节点可靠性、UX/UI、API和其他软件可访问工具证明的可信验证服务存在着许多机会和需要。 此外虽然与类似的网络相比Solana 的验证节点启动成本是名义上的,但对某些潜在的参与者来说仍然可能有些限制。 本着发展真正去中心化、无需许可网络的精神, 这些感兴趣的参与者可以通过授权使用可靠的验证节点获取部分利息,从而参与到 Solana 网络/经济活动中。
将代币委托给验证节点是其中一种方法,让被动的 Solana 代币持有者成为活跃 Solana 经济的一部分,并获得与委托验证节点一样的收益。 此外,该功能旨在创建一个健康的验证节点市场,潜在的验证服务节点相互竞争,从而打造可靠、透明和可持续发展的委托服务。

View File

@ -1,5 +0,0 @@
---
title: 已实现的设计提议
---
Solana团队已经接受并实施了以下架构方案。 任何可能在未来发生变化的设计都将在具体的提案页中注明。 已被接受但尚未实施的设计方案可在 [ 已接受的方案](../proposals/accepted-design-proposals.md) 中找到。

View File

@ -1,216 +0,0 @@
---
title: 集群软件安装和更新
---
目前用户需要自己从git仓库中构建Solana集群软件并手动更新容易出错且不方便。
本文档提出了一个简单易用的软件安装和更新程序,可以用来为支持的平台部署预建的二进制文件。 用户可以选择使用由Solana或任何其他他们信任的方提供的二进制文件。 更新的部署是通过链上更新清单程序来管理的。
## 激励的例子
### 使用bootstrap curl/shell脚本获取并运行一个预构建的安装程序。
支持的平台上最简单的安装方法。
```bash
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v1.0.0/install/solana-install-init.sh | sh
```
这个脚本将检查github以获取最新的标签版本并从那里下载并运行`Solana-install-init`二进制文件。
如果在安装过程中需要指定额外的参数可以使用下面的shell语法。
```bash
$ init_args=.... # arguments for `solana-install-init ...`
$ curl -sSf https://raw.githubusercontent.com/solana-labs/solana/v1.0.0/install/solana-install-init.sh | sh -s - ${init_args}
```
### 从Github发布的版本中获取并运行一个预构建的安装程序。
通过知名的发布URL可以获得支持平台的预构建二进制文件。
```bash
$ curl -o solana-install-init https://github.com/solana-labs/solana/releases/download/v1.0.0/solana-install-init-x86_64-apple-darwin
$ chmod +x ./solana-install-init
$ ./solana-install-init --help
```
### 从源代码构建并运行安装程序。
如果预制的二进制文件不能用于特定的平台,那么从源码中构建安装程序始终是一种选择。
```bash
$ git clone https://github.com/solana-labs/solana.git
$ cd solana/install
$ cargo run -- --help
```
### 向集群部署新的更新。
如果Solana发布的tarball\(由`ci/publish-tarball.sh`创建\) 已经上传到一个可公开访问的URL中以下命令将部署更新。
```bash
$ solana-keygen new -o update-manifest.json # <-- only generated once, the public key is shared with users
$ solana-install deploy http://example.com/path/to/solana-release.tar.bz2 update-manifest.json
```
### 运行一个自动更新的验证器节点。
```bash
$ solana-install init --pubkey 92DMonmBYXwEMHJ99c9ceRSpAmk9v6i3RdvDdXaVcrfj # <-- pubkey is obtained from whoever is deploying the updates
$ export PATH=~/.local/share/solana-install/bin:$PATH
$ solana-keygen ... # <-- runs the latest solana-keygen
$ solana-install run solana-validator ... # <-- runs a validator, restarting it as necesary when an update is applied
```
## 链上更新清单
更新清单用于在 Solana 集群上宣传部署新版本的 tarballs。 更新清单使用 `config` 程序存储,每个更新清单账户描述了一个给定目标三倍的逻辑更新通道(例如,`x86_64-apple-darwin`)。 账户公钥在部署新更新的实体和消费这些更新的用户之间是众所周知的。
更新的压缩包本身在其他地方托管,不在链上,可以从指定的 `download_url` 获取。
```text
use solana_sdk::signature::Signature;
/// Information required to download and apply a given update
pub struct UpdateManifest {
pub timestamp_secs: u64, // When the release was deployed in seconds since UNIX EPOCH
pub download_url: String, // Download URL to the release tar.bz2
pub download_sha256: String, // SHA256 digest of the release tar.bz2 file
}
/// Data of an Update Manifest program Account.
#[derive(Serialize, Deserialize, Default, Debug, PartialEq)]
pub struct SignedUpdateManifest {
pub manifest: UpdateManifest,
pub manifest_signature: Signature,
}
```
请注意,`manifest` 字段本身包含一个相应的签名\(`manifest_signature`\),以防止 `solana-install` 工具和 Solana 集群 RPC API 之间的中间人攻击。
为了防止回滚攻击,`solana-install` 将拒绝安装比当前安装的 `timestamp_secs` 更早的更新。
## 版本存档内容
一个发行版的归档文件应该是一个用bzip2压缩的tar文件其内部结构如下 /version. yml - 一个简单的YAML文件包含"target"字段。
- `/version.yml` - 一个简单的YAML文件包含 `"target"` -
目标元组。 任何额外的字段将被忽略。
- `/bin/` -- 发行版中包含可用程序的目录。
`solana-install` 会将这个目录以符号链接的方式连接到
`~/.local/share/Solana-install/bin``PATH` 环境变量使用。
变量。
- `...` -- 允许有任何其他文件和目录。
## solana-install 工具
用户使用 `solana-install` 工具来安装和更新他们的集群软件。
它在用户的主目录中管理以下文件和目录: ~/. config/Solana/install/config. yml -- 用户配置和当前集群软件的信息。
- `~/.config/Solana/install/config.yml` - 用户配置和当前安装的软件版本信息。
- `~/.local/share/solana/install/bin` - 当前版本的符号链接, 例如,`~/.local/share/Solana-update/<update-pubkey>-<manifest_signature>/bin`。
- `~/.local/share/Solana/install/releases/<download_sha256>/` - 版本内容。
### 命令行界面
```text
solana-install 0.16.0
The solana cluster software installer
USAGE:
solana-install [OPTIONS] <SUBCOMMAND>
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-c, --config <PATH> Configuration file to use [default: .../Library/Preferences/solana/install.yml]
SUBCOMMANDS:
deploy deploys a new update
help Prints this message or the help of the given subcommand(s)
info displays information about the current installation
init initializes a new installation
run Runs a program while periodically checking and applying software updates
update checks for an update, and if available downloads and applies it
```
```text
solana-install-init
initializes a new installation
USAGE:
solana-install init [OPTIONS]
FLAGS:
-h, --help Prints help information
OPTIONS:
-d, --data_dir <PATH> Directory to store install data [default: .../Library/Application Support/solana]
-u, --url <URL> JSON RPC URL for the solana cluster [default: http://api.devnet.solana.com]
-p, --pubkey <PUBKEY> Public key of the update manifest [default: 9XX329sPuskWhH4DQh6k16c87dHKhXLBZTL3Gxmve8Gp]
```
```text
solana-install info
displays information about the current installation
USAGE:
solana-install info [FLAGS]
FLAGS:
-h, --help Prints help information
-l, --local only display local information, don't check the cluster for new updates
```
```text
solana-install deploy
deploys a new update
USAGE:
solana-install deploy <download_url> <update_manifest_keypair>
FLAGS:
-h, --help Prints help information
ARGS:
<download_url> URL to the solana release archive
<update_manifest_keypair> Keypair file for the update manifest (/path/to/keypair.json)
```
```text
solana-install update
checks for an update, and if available downloads and applies it
USAGE:
solana-install update
FLAGS:
-h, --help Prints help information
```
```text
solana-install run
Runs a program while periodically checking and applying software updates
USAGE:
solana-install run <program_name> [program_arguments]...
FLAGS:
-h, --help Prints help information
ARGS:
<program_name> program to run
<program_arguments>... arguments to supply to the program
The program will be restarted upon a successful software update
```

View File

@ -1,22 +0,0 @@
---
title: 指令反省
---
## 面临的问题
一些智能合约程序可能想要验证另一个指令是否存在于给定的消息中,因为该指令可能是在预编译函数中执行某些数据的验证。 (参见secp256k1/_instruction的例子)。
## 解决方案
增加一个新的sysvar Sysvar1nstructions1111111111111111111111111程序可以在里面引用和接收消息的指令数据也可以引用当前指令的索引。
可以使用两个辅助函数来提取这些数据:
```
fn load_current_index(instruction_data: &[u8]) -> u16;
fn load_instruction_at(instruction_index: usize, instruction_data: &[u8]) -> Result<Instruction>;
```
运行时将识别这条特殊的指令为其序列化消息指令数据同时写入当前的指令索引然后bpf程序就可以从中提取必要的信息。
注意使用自定义序列化指令是因为二进制码在原生代码中的速度要慢10倍左右而且超过了当前bpf指令的限制。

View File

@ -1,55 +0,0 @@
---
title: 领导者之间的过渡
---
这个设计描述了领导者如何在每个领导者产生自己的插槽时相互之间过渡PoH账本的生产。
## 挑战
当前领先者和下一个领先者都在竞相产生当前插槽的最后一个刻度。 下一个领导者可能会在处理当前领导者的条目时到达该插槽。
理想的情况是,下一个领导者在能够为当前领导者投票之后,马上生成自己的插槽。 很有可能在当前领导者完成整个区块的广播之前下一个领导者就会到达自己的PoH插槽高度。
下一任领导者必须做出决定,是将自己的区块附加到最后完成的区块上,还是等待最后确定待播区块。 下一个领导者有可能会产生一个提出当前领导者失败的区块,即使网络的其他部分观察到该区块成功。
当前领导者有激励机制来尽早启动其区块以获取经济奖励。 这些激励因素需要与领导者需要将其区块附加到一个网络其余部分承诺最多的区块上进行平衡。
## 领导者超时
当一个领导者正在积极地接收前一个插槽的条目时,领导者可以实时延迟广播其块的开始。 延时时间由每个领队在本地配置,可以根据前一个领队的行为进行动态配置。 如果在超时之前前一个领队的区块被领队的TVU确认则PoH被重置为该插槽的开始这个领队立即产生其区块。
缺点是
-  领导者延迟了自己的时间,可能让下一个领导者有更多的时间追赶。
.
优点是:
- 一个区块中所有的空间都被用于输入。
- 超时不固定。
- 超时是领导者的局部,因此可以很聪明。 领导者的启发式可以考虑涡轮性能。
- 这种设计不需要账本硬分叉更新。
- 上一个领导者可以将区块中的最后一个条目冗余地传送给下一个领导者,下一个领导者可以推测性地决定信任它来生成它的区块,而不需要验证上一个区块。
-  领导者可以推测性地从最后一个接收到的条目中生成最后一个行情。
-  领导者可以投机地处理交易,猜测哪些交易不会被上一个领导者编码。 这也是一种审查攻击向量。 当前的领导者可能会扣留从客户端收到的交易,这样它就可以将它们编码到自己的插槽中。 一旦处理完毕条目就可以快速重放到PoH中。
## 其他设计方案
### 警卫在插槽的末端打勾
一个领导者在_倒数第二个tick_之后不会在其区块中产生条目这是下一个插槽的第一个tick之前的最后一个tick。 网络会对_最后一个tick_进行投票所以_倒数第二个tick_和_最后一个tick_之间的时间差是整个网络的强制延迟也是下一个领导者在产生新的插槽之前的延迟。 网络可以从_倒数第二个tick_产生_最后一个 tick_。
如果下一个领导者在它产生自己的_第一个tick_之前收到了_倒数第二的tick_它将重置它的PoH并从上一个领导者的_倒数第二个tick_产生_第一个tick_。 其余的网络也会重置自己的PoH产生_最后一个tick_作为投票的id。
弊端:
- 每次投票和确认都会有固定的超时时间。 1 tick也就是100ms左右。
- 平均一个交易的案例确认时间至少会差50ms。
- 这是账本定义的一部分,所以要改变这种行为需要硬分叉。
- 并非所有可用空间都用于条目。
与领导者超时相比,其优点是:
- 下一个领导者已经收到了之前所有的条目所以它可以开始处理交易而不需要将它们记录到PoH中。
- 上一个领导者可以将包含_倒数第二个tick_的最后一个条目冗余地传输给下一个领导者。 下一个领导可以在收到_倒数第二个tick_后立即推测生成_最后一个tick_甚至在验证它之前。

View File

@ -1,52 +0,0 @@
---
title: 领导者到验证节点的过渡
---
验证节点通常把时间花在验证区块上。 但是如果一个质押者将自己的质押委托给一个验证节点那么它偶尔会被选为_插槽领导者_。 作为一个插槽领导者验证节点负责在指定的_插槽_内产生区块。 一个插槽有一定数量的预先配置的_出块_持续时间。 这些ticks的持续时间是通过本文后面描述的_PoH记录器_来估计的。
## BankFork
BankFork跟踪银行状态在特定插槽上的变化。 一旦最后一次勾选被登记,状态就会被冻结。 任何写入的尝试都会被拒绝。
## 验证节点
验证节点对银行状态的许多不同的并发分叉进行操作直到生成一个高度在其领导插槽内的PoH哈希。
## 插槽领导者
一个插槽领导者只在一个分叉上搭积木,也就是它最后投票的那个分叉。
## PoH记录器
插槽领导者和验证员使用PoH记录仪来估计插槽高和记录交易。
### 验证时的PoH记录器
PoH记录器在验证时充当一个简单的VDF。 它告诉验证节点何时需要切换到插槽领导者角色。 每次验证节点对一个分叉进行投票时,它应该使用分叉最新的[blockhash](../terminology.md#blockhash)来重新播种VDF。 重新播种解决了两个问题。 首先它将自己的VDF与leader的VDF同步让它更准确地确定自己的leader插槽开始的时间。 第二如果上一个领导者倒下了所有的挂钟时间都会被计入下一个领导者的PoH流中。 例如如果领导者开始时少了一个区块那么它产生的区块应该有两个区块的PoH持续时间。 较领导者的持续时间可以确保下一个领导不会试图从上一个领导的插槽中抢走所有的事务。
### 领导时的PoH记录器
插槽领导者使用PoH记录器记录交易及时锁定其位置。 PoH散列必须来自于前一个领导者的最后一个区块。 如果不是其区块将无法通过PoH验证并被集群拒绝。
PoH记录器的作用还在于当其插槽结束时通知插槽领导者。 如果记录交易会在其指定的插槽之外产生PoH高度领导者需要注意不要修改其库。 因此领导者在生成条目的PoH哈希值之前不应承诺修改账户。 当PoH高度落在其插槽之外时其管道中的任何交易可能会被放弃或转发到下一个领导者。 转发是优选的因为它将最大限度地减少网络拥堵允许集群宣传更高的TPS容量。
## 验证节点循环
PoH记录器管理模式之间的过渡。 一旦账本被重放,验证节点可以运行,直到记录器指示它应该成为插槽领导者。 作为插槽领导者,该节点就可以执行和记录交易。
循环与PoH同步并做插槽领导者功能的同步启动和停止。 停止后验证节点的TVU应该发现自己的状态和不同的领导者给它发送相同的块一样。 以下是该循环的伪代码。
1. 查询LeaderScheduler的下一个分配的插槽。
2. 在所有的分叉上运行TVU。 1. TVU将把投票发给它认为是 "最好的 "分叉。 2. 每次投票后重启PoH记录器直到下一个指定的分叉。
插槽。
3. 做插槽领导者的时候启动TPU。 将其指向最后一个分叉的
TVU投了赞成票。
4. 制作作品,直至档期结束。 1. 在档期内TVU不得对其他分叉进行投票。 2. 插槽期结束后TPU冻结其BankFork。 冻结后,
该TVU可以恢复投票。
5. 回到第一点。

View File

@ -1,95 +0,0 @@
---
title: 持久账户存储
---
## 持久账户存储
账户集代表了验证节点处理过的所有交易的当前计算状态。 每个验证节点都需要维护这整个集合。 网络提出的每一个区块都代表着对这个集合的改变,由于每个区块都是一个潜在的回滚点,所以改变需要是可逆的。
NVME等持久性存储比DDR便宜20到40倍。 持久性存储的问题是写和读的性能比DDR慢很多必须注意数据的读写方式。 读取和写入都可以在多个存储驱动器之间分割,并行访问。 本设计提出了一种数据结构,允许存储的并发读取和并发写入。 写入通过使用AppendVec数据结构进行优化允许单个写入者进行追加同时允许多个并发读取者访问。 账户索引维护一个指针,指向每次分叉追加账户的位置,从而消除了对状态的显式检查点的需求。
## AppendVec
AppendVec是一个数据结构它允许随机读取与单一的纯追加写入同时进行。 增长或调整AppendVec的容量需要独占访问。 这是用一个原子`offset`来实现的,它在一个完成的追加结束时更新。
AppendVec的底层内存是一个内存映射的文件。 内存映射文件允许快速的随机访问,分页由操作系统处理。
## 账户索引
账户索引的设计是为了支持所有当前分叉账户的单一索引。
```text
type AppendVecId = usize;
type Fork = u64;
struct AccountMap(Hashmap<Fork, (AppendVecId, u64)>);
type AccountIndex = HashMap<Pubkey, AccountMap>;
```
该索引是账户公钥的映射到分叉的映射以及AppendVec中账户数据的位置。 要想获得一个特定分叉的账户版本。
```text
/// Load the account for the pubkey.
/// This function will load the account from the specified fork, falling back to the fork's parents
/// * fork - a virtual Accounts instance, keyed by Fork. Accounts keep track of their parents with Forks,
/// the persistent store
/// * pubkey - The Account's public key.
pub fn load_slow(&self, id: Fork, pubkey: &Pubkey) -> Option<&Account>
```
通过指向存储偏移量的`AppendVecId`中的内存映射位置来满足读取。 可以返回一个没有拷贝的引用。
### 验证节点根分叉
[塔式BFT](tower-bft.md)最终选择一个分叉作为根分叉,分叉被压扁。 被压扁的/根分叉不能回滚。
当一个分叉被压扁时,它的父账户中所有还没有出现在分叉中的账户都会通过更新索引被拉升到分叉中。 被压扁的分叉中余额为零的账户会通过更新索引从分叉中移除。
当一个账户被_压扁_导致无法访问时可以将其垃圾回收。
有三种可能的选择。
- 维护一个HashSet的根分叉。 预计每秒钟创建一个。 整个树可以在以后被垃圾回收。 另外,如果每个分叉都保持一个账户的引用计数,那么在更新索引位置时,垃圾收集可能会发生。
- 从索引中删除任何修剪过的分叉。 任何剩余的比根号低的分叉都可以被认为是根号。
- 扫描索引,将任何旧的根迁移到新的索引中。 任何比新根数低的剩余分叉都可以在以后删除。
## 只写附录
所有对账户的更新都是以纯追加更新的方式进行的。 每一次账户更新AppendVec中都会存储一个新版本。
可以通过在一个分叉中返回一个已经存储的账户的可变引用来优化单个分叉内的更新。 银行已经跟踪账户的并发访问,并保证对特定账户分叉的写与对该分叉中的账户的读不会同时发生。 为了支持这个操作AppendVec应该实现这个函数。
```text
fn get_mut(&self, index: u64) -> &mut T;
```
该API允许对`index`的内存区域进行并发的可变更访问。 它依靠银行保证对该索引的独家访问。
## 垃圾收集
随着账户的更新它们会移动到AppendVec的末尾。 一旦容量用完可以创建一个新的AppendVec并将更新的内容存储在那里。 最终对旧的AppendVec的引用将消失因为所有的账户都已更新旧的AppendVec可以被删除。
为了加快这个过程,可以将最近没有更新的账户移到新的 AppendVec 的前面。 这种形式的垃圾收集可以在不需要对任何数据结构进行独占锁的情况下完成,除了索引更新。
垃圾收集的初始实现是一旦AppendVec中的所有账户成为陈旧版本它就会被重用。 账户一旦被追加,就不会被更新或移动。
## 索引回收
在追加过程中,每个银行线程都有对账户的独占访问权,因为在数据提交之前,账户锁不能被释放。 但是在独立的AppendVec文件之间没有明确的写入顺序。 为了创建一个顺序,索引维护了一个原子写版本计数器。 每一次对AppendVec的追加都会在AppendVec中账户的条目中记录该追加的索引写版本号。
为了恢复索引所有的AppendVec文件可以以任何顺序读取并且每次分叉的最新写版本应该存储在索引中。
## 快照
要进行快照需要将AppendVec中的底层内存映射文件刷新到磁盘。 索引也可以写到磁盘上。
## 性能
- 只进行追加写入的速度很快。 SSD和NVME以及所有操作系统级别的内核数据结构都允许在PCI或NVMe带宽允许的情况下以最快的速度运行追加(2,700 MB/s)。
- 每个重放和银行线程都会同时写入自己的AppendVec。
- 每个AppendVec可能会被托管在一个单独的NVMe上。
- 每个重放和银行线程都可以并发读取所有AppendVec而不会阻止写入。
- 索引需要一个专属的写锁进行写入。 HashMap更新的单线程性能在每秒10m左右。
- Banking和Replay阶段应该使用每个NVMe的32个线程。 NVMe使用32个并发读取器或写入器具有最佳性能。

View File

@ -1,25 +0,0 @@
---
title: 只读账户
---
这个设计涵盖了[runtime](../validator/runtime.md)对只读和可写账户的处理。 修改同一账户的多个事务必须串行处理,以便它们总是以相同的顺序重放。 否则,这可能会给账本引入非确定性。 然而,有些事务只需要读取,而不需要修改特定账户的数据。 由于重放顺序并不重要,因此可以并行处理多个只读取同一账户的事务,从而提供性能优势。
为了识别只读账户事务MessageHeader结构包含`num_readonly_signed_accounts`和`num_readonly_unsigned_accounts`。 指令`program_ids`作为只读、无符号账户包含在账户向量中,因为可执行账户同样不能在指令处理过程中被修改。
## Runtime处理
Runtime的交易处理规则需要稍微更新。 程序仍然不能写入或花费不属于自己的账户。 但新的runtime规则保证了只读账户不能被修改即使是拥有这些账户的程序也不能修改。
只读账户具有以下属性。
- 只读访问所有账户字段包括lamports(不能贷记或借记) 和账户数据。
贷记、借记或修改只读账户的指令将失败。
## 账户锁定优化
账户模块在runtime跟踪当前锁定的账户从而将只读账户和可写账户分开。 默认的账户锁给一个账户赋予了 "可写 "的称号,并且一次只能由一个处理线程访问。 只读账户由一个单独的机制锁定,允许并行读取。
虽然还没有实现,但只读账户可以缓存在内存中,并由所有执行事务的线程共享。 一个理想的设计是当一个只读账户被任何在runtime移动的事务引用时保持这个缓存并在最后一个事务退出runtime释放缓存。
只读账户也可以作为引用传入处理器,保存一个额外的副本。

View File

@ -1,60 +0,0 @@
---
title: 可靠的投票传输
---
验证节点投票是对网络的共识和持续运行具有关键功能的信息。 因此,可靠地传递这些信息并将其编码到账本中是至关重要的。
## 挑战
1. 领导者轮换是由PoH触发的PoH是具有高漂移的时钟。 所以很多节点很可能对下一个领导者是否实时活跃有一个错误的看法。
2. 下一个领导者可能很容易被淹没。 因此DDOS不仅会阻碍常规事务的传递也会阻碍共识消息的传递。
3. UDP是不可靠的我们的异步协议要求任何传输的消息都要重传直到在账本中观察到它。 重传有可能会对有大量验证节点的领导者造成无意的_过大流量冲击_。 最坏的情况下,冲击量会达到`(num_nodes * num_retransmits)`。
4. 通过账本跟踪投票是否已经传送,并不能保证它将出现在确认块中。 当前观察到的区块可能会被解卷。 验证节点需要为每一次投票和分叉保持状态。
## 设计
1. 通过gossip以推送信息的方式送票。 这样可以保证将票数传递给所有下一届领导,而不仅仅是下一届未来的领导。
2. 领导者将读取Crds表以获取新的投票并将任何新收到的投票编码到他们提出的区块中。 这样就可以让验证节点的投票被所有未来的领导者纳入回滚分叉中。
3. 在账本中收到投票的验证节点将把它们添加到他们的本地crds表中而不是作为推送请求而是简单地将它们添加到表中。 这样就缩短了推送消息协议,所以验证消息不需要在网络上重传两次。
4. 投票的CrdsValue应该是这样的`Votes(<Transaction>)`
每个投票事务都应该在其数据中保持一个`wallclock`。 投票的合并策略将保留本地客户端配置的最后N组投票。 对于push/pull向量是递归遍历的每个Transaction被视为一个单独的CrdsValue有自己的本地wallclock和签名。
Gossip被设计为高效的状态传播。 通过gossip-push发送的消息被分批发送并以最小的生成树传播到网络的其他部分。 树上的任何部分故障都会通过gossip-pull协议主动修复同时将任何节点之间的数据传输量降到最低。
## 这种设计如何解决挑战。
1. 因为在领导的 "活跃 "状态验证节点没有简单的方法与领导同步所以无论在什么状态下gossip都可以最终交付。
2. Gossip会将消息传递给所有后续的领导者所以如果当前领导者被淹没下一个领导者就会已经收到这些票数并且能够对其进行编码。
3. Gossip通过维护一个高效的生成树并使用bloom过滤器来修复状态从而最大限度地减少通过网络的请求次数。 所以重传回退是没有必要的,消息是分批的。
4. 读取crds表进行投票的领导会对表中出现的所有新的有效投票进行编码。 即使这个领导的区块被取消,下一个领导也会尝试添加同样的票数,而不需要验证节点做任何额外的工作。 因此,不仅保证了最终的交付,也保证了最终编码到账本中。
## 性能
1. 最坏情况下传播到下一个领导者的时间是Log/(N/) 跳基数取决于fanout。 以我们目前默认的fanout为6到20k节点约为6跳。
2. 领导者应该收到20k张验证票通过gossip-push汇总成MTU大小的碎片。 这样可以将20k网络的数据包数量减少到80个碎片。
3. 每个验证节点的票数都会在全网复制。 为了维持5个以前投票的队列Crds表将增长25兆字节。 `(20000节点*256字节*5)`
## 两步实施推出
初期网络只需通过网络传输和维护1个投票就可以可靠地执行当前的投票实施。 对于小型网络6个fanout就足够了。 对于小型网络,内存和推送开销很小。
### 1k验证节点子网络
1. Crds只是维护验证节点最新的投票。
2. 无论票数是否出现在分类账中,都会被推送和重传。
3. 6的fanout。
4. 最坏情况下每个节点256kb的内存开销。
5. 最坏情况下4跳传播到每个节点。
6. 领导者应该在4个推送消息碎片中收到整个验证节点投票集。
### 20k子网络
在上述子网加上以下几点。
1. CRDS表维护着5个最新验证节点投票的向量。
2. 投票编码一个挂钟。 CrdsValue::Votes是一个类型它递归到所有Gossip协议的事务向量中。
3. 将fanout增加到20。
4. 最坏情况下每个节点的内存开销为25mb。
5. 在最坏的情况下用4个跳来传送到整个网络。
6. 领队收到的所有验证机信息80个碎片。

View File

@ -1,69 +0,0 @@
---
title: 出租
---
Solana 上的帐户可能处于所有者控制的状态 \(`Account::data`\) 该帐户独立于帐户的余额 \(`Account::lamports`\)。 因为网络上的验证节点需要在内存中保留此状态的工作副本,因此网络需要收取基于时间空间的一部分费用,也称为租金。
## 二级租金制度
那些账户最低余额相当于2年租金的账户可以免税。 _2 年_ 是因为硬件成本每2年会下降一半的事实以及由于几何序列引起的收敛。 余额低于此阈值的账户按起始规定的费率收取租金,按每个年份的灯塔收取。 网络基于每个轮次收取租金,同时计入下一个轮次的租金,`Account:::rent_epoch` 也会保存下次应该从帐户中收集的租金。
目前,租金费用从一开始就已经固定了。 然而,我们预计它是动态的,反映了当下的硬件储存成本。 因此,随着技术进步以及硬件成本的下降,价格一般都会逐渐降低。
## 收取租金的时间
从帐户收取租金的时间有 2 次:\(1\) 进行交易,\(2\) 每个轮次定期收一次。 \(1\) 包括创建新账户本身的交易,而且它作为银行加载阶段,进行正常交易期间的一个步骤,。 \(2\) 该步骤是为了确保从旧账户中收取租金,这些账户在最新的轮次中基本都没有被引用。 \(2\) 需要扫描整个帐户,并以帐户地址前缀为基础将其分布到一个轮次,以避免由于收取租金而导致的加载波动。
相反,收取租金不适用于任何协议级账簿程序直接操纵的账户,其中包括:
- 收租本身的分配(以往,这可能导致重复收租的问题)
- 在每个新轮次开始的时候,质押奖励的分配 (尽量减少在新轮次开始时的波动幅度)
- 每个slot结束时的交易费分配
即使这些过程超出了收取租金的范围,所有被操纵的账户最终都将由 \(2\) 机制处理。
## 收集租金的实际过程
租金到期的期限为一个 epoch取决于租金制度帐户有 `current_epoch``current_epoch + 1``Account::rent_epoch`
如果帐户处于免责状态, `Account::rent_epoch` 就只更新到 `current_epoch`
如果帐户不处于免责状态,下一个 epoch 和 `Account::rent_epoch` 之间的差额用于计算此账户所欠的租金金额\(通过 `Rent::due()`\)。 计算中的任何分数端口灯都是被截断的。 已从 `Account::lamport``Account::rent_epoch` 更新到 `current_epoch + 1` (= 下一个 epoch)。 如果到期租金少于一个 lamport则不对该账户作任何更改。
余额不足以支付租金的账户仅仅会无法加载。
一定比例的租金被销毁。 其余部分(即交易费)按质押权重,在每个 slot 结束的时候分配给验证节点账号。
最后,根据协议级别的帐户更新进行租金收取(就像向验证节点分发租金),这意味着没有相应的租金扣减交易。 因此,租金收取的过程其实非常隐秘,只能通过最近的交易或其账户地址前缀预先确定的时间来观察到。
## 设计考虑因素
### 当前设计依据
根据前面的设计,不可能有帐户处于遗漏、不会交互或者不支付租金的状态。 除了免租金、系统服务和可执行账户其他账户在每个epoch都需要支付一次租金。
这就是设计上的考虑。 否则, 如果任何人可能不公平地获得租金(当前的领导者) 或因预期的浮动租金费用去节省费用,就有可能通过 `Noop` 指令启动未经授权的租金收取。
这个设计还有另一个副作用:我们注意到这种定期收取租金的做法有效地迫使验证节点不将陈旧帐户变成冷存储,从而节省储存费用,这一点不利于账户所有者,可能导致他们的交易停顿时间比其他人更长。 但是在另一方面,它防止了恶意用户累积大量垃圾帐户,加重验证节点的负担。
该设计的总体思路为:所有账户具有相同的性能特征,作为验证节点的工作集存储起来,直观地反映出统一的租金定价结构。
### 特别收藏
考虑按需要收取租金\(即当帐户被加载/访问的时候\)。 采取这种办法的问题是:
- 某笔交易加载为“信用额度”的帐户可能会很合理地指望存在一个租金期限,
但是任何这类交易都无法写入
- "打败忙碌”的机制\(即寻找需要支付租金的帐户\) 是可取的,
不经常加载的帐户可以获得一些免费的机会
### 收取租金的系统说明
通过系统指示收取租金时需要注意,它会自然把租金分配给活跃和质押权重的节点,并且可以逐步进行。 然而:
- 它会对网络流量产生不利影响
- 该过程需要在运行时间之前进行特殊的套件处理,因为非系统程序所有者的帐户可能会被此指示扣除。
- 必须有人发布一笔交易

View File

@ -1,58 +0,0 @@
---
title: 维修服务
---
## 维修服务
修复服务负责检索未能通过Turbine等主要通信协议传送的遗失碎片。 它负责管理下面`维修协议`部分中描述的协议。
## 挑战。
1\) 验证节点可能会因为网络故障而无法接收特定的碎片。
2\) 考虑一个场景blockstore包含一组插槽{1, 3, 5}。 那么Blockstore接收到一些插槽7的碎片其中对于每一个碎片bb.parent == 6那么父子关系6 -&gt; 7就存储在blockstore中。 但是没有办法将这些插槽链到Blockstore中任何一个现有的库中因此`碎片修复`协议不会修复这些插槽。 如果这些插槽恰好是主链的一部分,这将停止该节点上的重放进度。
## 修复相关基元
纪元插槽: 每个验证节点都在Gossip上分别广播`纪元插槽`的各个部分。
- `储藏`:一个以纪元为单位的所有已完成插槽的压缩集。
- `缓存`:最新的`N`个已完成的插槽的运行长度编码(RLE),从某个插槽`M`开始,其中`N`是一个MTU大小的数据包所能容纳的插槽数。
gossip中的`Epoch Slots`每当验证节点收到一个在epoch内的完整插槽时就会更新。 已完成的插槽由blockstore检测并通过通道发送到维修服务。 需要注意的是,我们知道当一个插槽`X`完成的时候,包含插槽`X`的纪元必须存在纪元时间表因为WindowService会拒绝未确认的纪元的碎片。
每完成一个`N/2`插槽,最老的`N/2`插槽就会从`缓存`移到`stash`中。 RLE的基值`M`也要更新。
## 修复请求协议
修复协议为进步Blockstore的分叉结构做了最好的尝试。
不同的协议策略来解决上述难题。
1. Shred Repair\(解决挑战\#1\):这是最基本的修复协议,目的是检测和填补账本中的 "漏洞"。 Blockstore会跟踪最新的根插槽。 然后RepairService会从根插槽开始定期迭代blockstore中的每一个分叉向验证节点发送修复请求以获取任何缺失的碎片。 它每次迭代最多会发送一些`N`修复请求。 碎片修复应该根据领导者的分叉重量来优先修复分叉。 验证节点应该只向在其EpochSlots中标记该插槽已完成的验证节点发送修复请求。 验证节点应该优先修复他们负责通过涡轮重传的每个插槽中的碎片。 验证节点可以计算出他们负责重传的碎片因为turbine的种子是基于leader id、slot和碎片 index的。
注意验证节点只接受当前可验证的epoch (验证节点有领袖时间表的epoch) 内的碎纸片。
2. Preemptive Slot Repair\(解决挑战 \#2\):这个协议的目标是发现 "孤儿 "插槽的链路关系,这些插槽目前没有链到任何已知的分叉。 碎片修复应该根据领导者的分叉权重来优先修复孤儿插槽。
- Blockstore将在一个单独的列族中跟踪 "孤儿 "插槽的集合。
- 修复服务将定期为blockstore中的每个孤儿提出`Orphan`请求。
`Orphan(orphan)`请求--`orphan`是请求者想知道的孤儿插槽的父母`Orphan(orphan)`响应--请求的`orphan`的前`N`个父母的最高分叉量。
在收到响应`p`时,其中`p`是父插槽中的一些碎片,验证节点将:
- 如果它还不存在,那么就在区块存储中为`p.slot`插入一个空的`SlotMeta`。
- 如果`p.slot`确实存在,根据`父辈`更新`p`的父插槽。
注意:一旦这些空插槽被添加到区块存储中,`Shred Repair`协议应该尝试填补这些插槽。
注意验证节点只会接受包含当前可验证的epoch (验证节点有领导时间表的epoch) 内的碎片的响应。
验证节点应该尝试将孤儿请求发送给在其EpochSlots中已将该孤儿标记为已完成的验证节点。 如果不存在这样的验证节点,那么就以利害关系加权的方式随机选择一个验证节点。
## 修复响应协议
当验证节点收到一个碎片`S`的请求时,如果他们有碎片,他们就会响应。
当验证节点通过修复响应收到碎纸片时,他们会检查`EpochSlots`,看看是否有 <= `1/3` 的网络已经将这个插槽标记为完成。 如果是这样,他们就会通过其相关的涡轮路径重新提交这个碎片,但前提是这个验证节点之前没有重传过这个碎片。

View File

@ -1,66 +0,0 @@
# 长期RPC事务历史
RPC需要提供至少6个月的交易历史。 当前的历史记录,以天为单位,对于下游用户来说是不够的。
6个月的交易数据实际上无法存储在验证节点的rocksdb账本中所以需要一个外部数据存储。 验证节点的rocksdb账本将继续作为主要数据源然后将回落到外部数据存储中。
受影响的RPC端点是 * [getFirstAvailableBlog]。
* [getFirstAvailableBlock](developing/clients/jsonrpc-api.md#getfirstavailableblock)
* [getConfirmedBlock](developing/clients/jsonrpc-api.md#getconfirmedblock)
* [getConfirmedBlocks](developing/clients/jsonrpc-api.md#getconfirmedblocks)
* [getConfirmedSignaturesForAddress](developing/clients/jsonrpc-api.md#getconfirmedsignaturesforaddress)
* [getConfirmedTransaction](developing/clients/jsonrpc-api.md#getconfirmedtransaction)
* [getSignatureStatuses](developing/clients/jsonrpc-api.md#getsignaturestatuses)
需要注意的是,不支持[getBlockTime](developing/clients/jsonrpc-api.md#getblocktime)因为一旦https://github.com/Solana-labs/Solana/issues/10089 被修复,那么`getBlockTime`就可以被删除。
一些系统设计限制。
* 需要存储和搜索的数据量可以快速的跳到TB级并且是不可改变的。
* 系统应该尽可能的轻量化以满足SRE的要求。 例如一个SQL数据库集群需要SRE不断地监控和重新平衡节点是不可取的。
* 数据必须可以实时搜索--花几分钟或几小时运行的批量查询是不可接受的。
* 易于在全球范围内复制数据以便与将利用数据的RPC端点共同定位。
* 与外部数据存储的接口应该是容易的,不需要依赖风险较小的社区支持的代码库。
基于这些约束条件选择Google的BigTable产品作为数据存储。
## 表模式
一个BigTable实例用来保存所有的交易数据分成不同的表以便快速搜索。
新数据可以随时复制到实例中,而不影响现有数据,所有数据都是不可改变的。 一般情况下,人们期望当前一个纪元完成后就会上传新数据,但对数据转储的频率没有限制。
通过适当配置实例表的数据保留策略,旧数据的清理是自动的,只是消失了。 因此数据添加的时间顺序就变得很重要。 例如如果在N-1纪元的数据之后添加了N纪元的数据那么旧纪元的数据就会比新数据的寿命更长。 然而除了在查询结果中产生_holes_之外这种无序删除不会有任何不良影响。 请注意,这种清理方法有效地允许存储无限量的事务数据,只是受限于这样做的货币成本。
表布局s只支持现有的RPC端点。 未来新的RPC端点可能需要对模式进行添加并有可能对所有事务进行迭代以建立必要的元数据。
## 访问BigTable
BigTable有一个gRPC端点可以使用[tonic](https://crates.io/crates/crate)] 和原始protobuf API进行访问因为目前还没有针对BigTable的更高级别的Rust crate存在。 实际上这使得BigTable查询结果的解析变得更加复杂但并不是一个重要的问题。
## 数据群
通过使用新的`solana-ledger-tool`命令将给定插槽范围的rocksdb数据转换为实例模式实例数据的持续填充将以一个纪元的节奏进行。
同样的过程将被手动运行一次,以回填现有的账本数据。
### 区块表格:`block`
此表包含了给定插槽的压缩块数据。
行键是通过取插槽的16位小写十六进制表示来生成的以确保当行被列出时具有已确认块的最老的插槽总是排在第一位。 例如插槽42的行键是00000000000000002a。
行数据是一个压缩的`StoredConfirmedBlock`结构。
### 账户地址交易签名查询表: `tx-by-addr`
该表包含了影响给定地址的事务。
行的键是`<base58
address>/<slot-id-one's-compliment-hex-slot-0-prefixed-to-16-digits>`。 行数据是一个压缩的`TransactionByAddrInfo`结构。
取插槽的一的补码允许列出插槽,确保最新的插槽与影响地址的事务总是会先列出。
Sysvar地址是没有索引的。 然而,经常使用的程序,如 Vote 或 System 是有索引的,并且很可能为每个确认的插槽有一行。
### 事务签名查询表: `tx`
该表将交易签名映射到其确认的区块,以及该区块中的索引。
行键是base58编码的交易签名。 行数据是一个压缩的`TransactionInfo`结构。

View File

@ -1,48 +0,0 @@
---
title: 快照核查
---
## 面临的问题
当验证节点从快照启动时,它需要一种方法来验证账户集与网络其他部分所看到的快速匹配。 潜在的攻击者可以给验证节点一个不正确的状态,然后试图说服它接受一个本来会被拒绝的交易。
## 解决方案
目前,银行哈希值是通过对一个插槽中账户的差异状态进行哈希,然后与之前的银行哈希值相结合而得出的。 这样做的问题是,哈希值列表将按链处理的插槽数顺序增长,成为传输和验证成功的负担。
另一种原始的方法可以是创建一个账户状态的哈希树。 这样做的缺点是,每次账户更新,都要从系统中所有活账户的整个账户状态中重新计算出哈希树。
为了验证快照,我们进行以下工作:
在非零lamport账户的账户存储中我们对以下数据进行哈希处理
- 帐户所有者
- 账户数据
- 帐户公钥
- 账户余额
- 帐户存储分叉
使用这个产生的哈希值作为一个扩展函数的输入,该函数将哈希值扩展为一个图像值。 该函数将创建一个440字节的数据块其中前32个字节是哈希值接下来的440-32个字节由Chacha RNG以哈希值为种子生成。
然后用xor结合账户图像。 前一个账户值将被xored到状态新的账户值也被xored到状态。
投票和sysvar哈希值与产生的完整映像值的哈希值一起发生。
在验证节点启动时,当它从快照加载时,它会用设置的账户验证哈希值。 然后它将使用SPV来显示投票支持所给哈希值的网络百分比。
由此产生的值可以被验证节点验证为所有当前账户状态一起xoring的结果。
在创建之前和验证过程中必须清除零lamport账户的快照因为零lamport账户不会影响哈希值但可能会导致验证节点银行读到一个账户不存在而它确实应该存在。
可以对xor状态进行攻击来影响它的值
因此440字节的图像大小来自于这篇论文避免xor与0的碰撞(或因此任何其他给定的比特模式)\[[https://link.springer.com/content/pdf/10.1007%2F3-540-45708-9_19.pdf](https://link.springer.com/content/pdf/10.1007%2F3-540-45708-9_19.pdf)\]
在这种情况下数学提供了128位的安全性。
```text
O(k * 2^(n/(1+lg(k)))
k=2^40 accounts
n=440
2^(40) * 2^(448 * 8 / 41) ~= O(2^(128))
```

View File

@ -1,57 +0,0 @@
---
title: 质押奖励
---
这里概述了一个权益证明/(PoS)/(即使用协议内资产SOL提供安全共识/)设计。 Solana实现了集群中验证节点节点的权益证明奖励/安全方案。 其目的有三点。
- 通过风险的游戏中的皮肤存款,使验证节点的激励措施与大群的激励措施相一致。
.
- 通过实施旨在促进分叉收敛的削减规则,避免 "事不关己 "的分叉投票问题。
.
- 为验证节点提供一个作为验证节点参与集群的功能而提供的验证节点奖励渠道。
.
虽然目前很多具体实施的细节还在考虑中预计会通过Solana测试网的具体建模研究和参数探索成为焦点但我们在这里概述一下我们目前对PoS系统主要组成部分的思考。 这个思路大部分是基于Casper FFG的现状在Solana的历史证明 (PoH) 区块链数据结构允许的情况下进行优化和具体属性修改。
## 总体概述
Solana的账本验证设计是基于一个轮流的、经过质押加权的选定的领导者在PoH数据结构中向验证节点广播交易。 这些节点在收到领导者的广播后有机会通过签署交易到PoH流中对当前状态和PoH高度进行投票。
要成为Solana验证节点必须在合约中存入/锁定一定数量的SOL。 这个SOL在特定的时间段内是无法使用的。 押金锁定期的确切时间还没有确定。 但是我们可以考虑这段时间的三个阶段,这三个阶段将需要特定的参数。
- _预热期_SOL质押在哪个节点哪个节点便无法进入
然而PoH交易验证还没有开始。 最有可能的顺序是
天到周
- _验证期_交存的SOL的最短期限。
无法进入,有可能被罚没(见下文罚没规则),并赚取。
对审定者的参与给予奖励。 可能持续的时间为数月至一年。
.
- _冷却期_:提交“取款“业务后的一段时间。
. 在此期间,验证责任已被删除,
资金仍无法进入。 累积奖励
应在这一时期结束时交付,同时归还首期存款;
.
Solana的PoH数据结构所提供的无信任的时间感和秩序感以及其[turbine](https://www.youtube.com/watch?v=qt_gDRXHrHQ&t=1s)数据广播和传输设计,应该提供亚秒级的交易确认时间,其规模与集群中节点数量的对数相一致。 这意味着我们不应该用一个令人望而却步的 "最低存款 "来限制验证节点的数量并期望节点能够以名义上的SOL质押量成为验证节点。 同时Solana对高吞吐量的关注应该为验证客户端提供高性能和可靠的硬件创造动力。 再加上潜在的最低网络速度门槛,作为验证客户加入,我们预计一个健康的验证委托市场将会出现。 为此Solana的testnet将导致 "Tour de SOL "验证客户竞争重点是吞吐量和正常运行时间对testnet验证节点进行排名和奖励。
## 处罚
如[经济设计](ed_overview/ed_overview.md)一节所述,验证节点的年利率将被规定为已质押的流通供应总量百分比的函数。 集群对在整个_验证期_内在线并积极参与验证过程的验证节点进行奖励。 对于在此期间离线/未能验证交易的验证节点,他们的年度奖励会有效减少。
同样,我们可以考虑在验证节点离线的情况下,通过算法降低其活跃金额质押金额。 例如, 如果一个验证节点在一段时间内不活跃,无论是由于分区还是其他原因,他们被认为是 "活跃 "的质押金额(有资格获得奖励) 可能会减少。 这种设计的结构将有助于长期存在的分区最终达到各自链的最终性,因为随着时间的推移,无投票权的总股权比例会减少,直到每个分区的活跃验证节点可以达到超级多数。 同样,在重新参与时,"活跃 "的股权数量将以某种确定的速度重新上线。 根据分区/活跃集的大小,可以考虑不同的质押减少率。

View File

@ -1,53 +0,0 @@
---
title: 测试程序
---
应用程序将交易发送到Solana集群和查询验证者以确认交易被处理并检查每个交易的结果。 当集群的行为与预期不符时,可能有多种原因。
- 程序有问题
- BPF加载器拒绝了一条不安全的程序指令。
- 这笔交易太大
- 交易无效
- 运行时试图执行交易时,另一个人正在访问
同一个账户
- 网络放弃了交易
- 集群回滚账目
- 一个验证者恶意回应查询
## AsyncClient和SyncClient特征
为了排除故障,应用程序应该重新瞄准一个较低级的组件,因为在那里可能出现的错误较少。 重定向可以通过AsyncClient和SyncClient特性的不同实现来完成。
组件实现了以下主要方法。
```text
trait AsyncClient {
fn async_send_transaction(&self, transaction: Transaction) -> io::Result<Signature>;
}
trait SyncClient {
fn get_signature_status(&self, signature: &Signature) -> Result<Option<transaction::Result<()>>>;
}
```
用户发送交易并异步和同步等待结果。
### 集群的轻量级客户端
最高级别的实现ThinClient以Solana集群为目标它可能是一个已部署的测试网也可能是一个在开发机器上运行的本地集群。
### 针对 TPU 的 TpuClient
下一个层次是TPU的实现目前还没有实现。 在TPU层面应用程序通过Rust通道发送交易在这个通道中不会出现来自网络队列或丢包的意外。 TPU实现了所有 "正常 "的交易错误。 它进行签名验证,可能会报告账户使用中的错误,否则就会在分类账中产生结果,并完成历史哈希证明。
## 低级测试
### 银行的银行客户端
TPU下面是银行。 银行不进行签名验证,也不生成账本。 银行是一个方便的层,可以测试新的链上程序。 它允许开发者在本地程序实现和 BPF 编译的变体之间切换。 这里不需要Transact特性。 银行的API是同步的。
## 使用runtime进行单元测试
Bank下面是Runtime。 Runtime是单元测试的理想测试环境。 通过将Runtime静态地链接到本地程序实现中开发者可以获得尽可能短的编辑-编译-运行循环。 在没有任何动态链接的情况下,堆栈痕迹包括调试符号,程序错误也很容易排除。

View File

@ -1,138 +0,0 @@
---
title: Tower BFT
---
本设计介绍了Solana的_塔式BFT_算法。 它解决了以下问题。
- 一些分叉可能最终不会被集群的超级多数接受,投票者需要从对这些分叉的投票中恢复过来。
- 许多分叉可能会被不同的投票者投票,而每个投票者可能会看到一组不同的可投票分叉。 被选中的分叉最终应该为集群收敛。
- 基于奖励的投票有相关的风险。 投票者应该有能力配置他们承担多少风险。
- [回滚成本](tower-bft.md#cost-of-rollback)需要是可计算的。 这对于依赖某种可衡量形式的一致性的客户来说很重要。 破坏一致性的成本需要是可计算的,对于老票来说,成本会超线性增加。
- 节点之间的ASIC速度是不同的攻击者可以采用比集群其他部分快很多的历史证明ASIC。 共识需要抵御利用历史证明ASIC速度差异性的攻击。
为了简明扼要,本设计假设一个有质押的投票者被部署为集群中的单个验证节点。
## 时间
Solana集群通过可验证的延迟函数生成时间源我们调用[历史证明](../cluster/synchronization.md)。
历史证明用于为所有活跃的领导者创建一个确定性的循环赛时间表。 在任何给定的时间只有1个领导者可以提出分叉这可以从账本本身计算出来。 更多细节,请参见[分叉生成](../cluster/fork-generation.md)和[领袖轮换](../cluster/leader-rotation.md)。
## 锁定
锁定的目的是迫使验证节点对特定的分叉承诺机会成本。 锁定是以时隙来衡量的,因此代表了验证节点在打破对一个分叉的承诺之前需要等待的实时强制延迟。
违反锁定时间并在锁定时间内投票给分歧的分叉的验证节点应该受到惩罚。 建议的惩罚措施是,如果能向集群证明在锁定期内同时投票给非下级分叉,则罚没验证节点的股权。
## 算法
这种方法的基本思路是堆叠共识投票和双重锁定。 栈中的每一票都是对一个分叉的确认。 每一个确认的分叉都是它上面的分叉的祖先。 每一票都有一个以时隙为单位的`锁定`,然后验证节点才能提交一个不包含确认的分叉作为祖先的投票。
当一个投票被添加到堆栈中时,堆栈中所有前一个投票的锁定会翻倍(更多内容请参见[Rollback](tower-bft.md#Rollback))。 每一次新的投票,验证节点都会将之前的投票投入到一个不断增加的锁定中。 在32票时我们可以认为投票处于`最大锁定`的任何锁定等于或高于`1<<32`的投票都会被dequeued\(FIFO\) 去排队投票是奖励的触发器 如果一个投票在去排队之前就过期了那么它和它上面的所有投票都会从投票堆栈中被弹出\(LIFO\) 验证节点需要从这一点开始重建栈
### 回滚
在投票被推送到堆栈之前,投票前所有锁定时间低于新投票的票数都会被弹出。 回滚后锁定时间不会翻倍,直到验证节点追上票数的回滚高度。
例如,一个投票栈的状态如下。
| 票数 | 票数时间 | 锁定时间 | 锁定到期时间 |
| --:| ----:| ----:| ------:|
| 4 | 4 | 2 | 6 |
| 3 | 3 | 4 | 7 |
| 2 | 2 | 8 | 10 |
| 1 | 1 | 16 | 17 |
_第5票_是在时间9结果状态是
| 票数 | 票数时间 | 锁定时间 | 锁定到期时间 |
| --:| ----:| ----:| ------:|
| 5 | 9 | 2 | 11 |
| 2 | 2 | 8 | 10 |
| 1 | 1 | 16 | 17 |
_第6票_在第10时
| 票数 | 票数时间 | 锁定时间 | 锁定到期时间 |
| --:| ----:| ----:| ------:|
| 6 | 10 | 2 | 12 |
| 5 | 9 | 4 | 13 |
| 2 | 2 | 8 | 10 |
| 1 | 1 | 16 | 17 |
在10时新的票数赶上了之前的票数。 但是_投票2_在10时到期所以在11时应用_投票7_时包括_投票2_以上的投票将被弹出。
| 票数 | 票数时间 | 锁定时间 | 锁定到期时间 |
| --:| ----:| ----:| ------:|
| 7 | 11 | 2 | 13 |
| 1 | 1 | 16 | 17 |
第1票的锁定将不会从16票增加直到堆栈包含5票。
### 罚没和奖励
验证节点如果尽可能频繁地选择群组其他成员选择的分叉,就应该得到奖励。 这与当投票堆栈满了,需要对最老的投票进行排队时产生奖励是一致的。 因此每一个成功的dequeue都应该产生一个奖励。
### 回滚的成本
回滚_分叉A_的成本被定义为验证节点确认任何不包括_分叉A_为祖先的其他分叉的锁定时间成本。
在**经济终局性**方面_分叉A_可以计算为_分叉A_及其后裔回滚所带来的所有奖励损失再加上由于确认_分叉A_的投票被锁定而带来的机会成本。
### 门槛
每个验证节点可以在该验证节点提交分叉之前,独立设置一个集群承诺的阈值。 例如在票堆索引7处锁定时间单位为256个。 除非指数7的投票在集群中的承诺度大于50%否则验证节点可以扣留投票让0-7的投票失效。 这使得每个验证节点可以独立控制承诺分叉的风险有多大。 以更高的频率承诺分叉,可以让验证节点获得更多的奖励。
### 算法参数
以下参数需要调整:
- 在dequeue发生之前堆栈中的投票数\(32\)。
- 栈中锁定的增长率 (2x/)。
- 开始的默认锁定\(2\)。
- 最小集群承诺的阈值深度,在承诺分叉之前 (8)。
- 最小集群承诺大小在阈值深度 (50%+)。
### 自由选择
"自由选择 "是一种不可强制执行的验证节点动作。 协议没有办法对这些动作进行编码和强制执行,因为每个验证节点都可以修改代码和调整算法。 一个在所有可能的期货上最大化自我回报的验证节点,其行为应该是系统稳定的,局部贪婪选择的结果应该是在所有可能的期货上贪婪选择。 一组从事破坏协议的选择的验证节点应该受到其利益权重的约束而拒绝服务。 验证节点有两种选择出口。
- 一个验证节点可以在虚拟生成中超越之前的验证节点,并提交一个并发的分叉。
- 验证节点可以不投票,观察多个分叉后再投票
在这两种情况下,集群中的验证节点都有几个分叉可以同时选择,即使每个分叉代表不同的高度。 在这两种情况下,协议不可能检测到验证节点的行为是否是故意的。
### 贪婪地选择并发分叉
当评估多个分叉时,每个验证节点应使用以下规则。
1. Forks必须满足_Threshold_规则。
2. 选取能使所有祖先分叉的总集群锁定时间最大化的分叉。
3. 选取集群交易费用最大的分叉。
4. 选取PoH最晚的分叉。
群集交易费是指存入矿池的费用,详见[质押奖励](staking-rewards.md)章节。
## PoH ASIC抗性
投票数和锁定数呈指数级增长而ASIC的速度是线性增长。 有两种可能的攻击向量涉及更快的ASIC。
### ASIC审查
攻击者产生一个并发的分叉,它的速度超过了之前的领导者,以达到审查的目的。 这个攻击者提出的分叉将与下一个可用的领导者同时出现。 节点要选择这个分叉必须满足_Greedy Choice_规则。
1. Fork必须对祖先分叉有同等数量的投票。
2. 分叉不能是一个头,以至于导致票数过期。
3. Fork必须有更多的群交易费。
这种攻击就仅限于删掉前面的领导费,和个人交易费。 但它不能停止集群,也不能减少验证节点集,相比并发的分叉。 费用审查仅限于访问费用去领导,而不是验证节点。
### ASIC回滚
攻击者从一个旧区块中生成一个并发分叉,试图回滚集群。 在这种攻击中,并发分叉与已经被投票的分叉竞争。 这种攻击受到锁定的指数增长的限制。
- 1票有2个时隙的锁定。 并发分叉必须至少领先2个时隙并在1个时隙内产生。 因此需要快2倍的ASIC。
- 2票有4个时隙的锁存。 并发分叉必须至少领先4个时隙并在2个时隙中产生。 因此需要快2倍的ASIC。
- 3票有8个时隙的锁定。 并发分叉必须至少领先8个时隙并在3个时隙中生产。 因此需要一个ASIC快2.6倍。
- 10票有1024个时隙的锁定。 1024/10即快102.4倍的ASIC。
- 20票有2^20个时隙的锁定。 2^20/20或快52428.8倍的ASIC。

View File

@ -1,32 +0,0 @@
---
title: 确定性交易费用
---
目前,交易包括一个费用字段,表示插槽领导者处理交易时允许收取的最大费用字段。 而集群则商定一个最低费用。 如果网络拥挤,插槽领导者可能会优先处理提供较高费用的交易。 这意味着在交易被集群确认并检查剩余余额之前,客户端不会知道获得了多少费用。 这闻起来正是我们不喜欢的以太坊"Gas",非确定性的味道。
## 拥堵引起的费用
验证节点使用_signatures per slot_\(SPS\) 来估计网络拥堵然后通过_SPS target_来估计集群的期望处理能力。 验证节点从genesis config中获得SPS target信息而它从最近处理的事务中计算SPS。 Genesis config还定义了一个目标`lamports_per_signature`这是集群在_SPS target_运行时对每个签名收取的费用。
## 费用计算
客户端使用 JSON RPC API 来查询集群的当前收费参数。 这些参数被标记了一个区块哈希,并保持有效,直到该区块哈希足够老,被插槽领导者拒绝。
在向集群发送交易之前客户端可以将交易和收费账户数据提交给一个名为_收费计算器_的SDK模块。 只要客户端的SDK版本与插槽领导者的版本相匹配客户端就可以保证其账户被更改的lamports数量与费用计算器返回的数量完全相同。
## 费用参数
在该设计的第一个实现中,唯一的收费参数是`lamports_per_signature`。 集群需要验证的签名越多,费用越高。 具体的lamports数量由SPS与SPS目标的比例决定。 在每个时段结束时当SPS低于目标值时集群会降低`lamports_per_signature`,当高于目标值时,集群则会相应提高它的值。 `lamports_per_signature`的最小值是目标`lamports_per_signature`的50%最大值是目标lamports_per_signature的10倍。
未来的参数可能包括:
- `lamports_per_pubkey` - 加载一个账户的费用
- `lamports_per_slot_dimit` - 加载非常老的账户费用较高
- `lamports_per_byte` - 按账户大小加载的费用
- `lamports_per_bpf_instruction` - 运行一个程序的成本
## 攻击
### 劫持SPS目标
一群验证节点如果能说服群组将SPS目标提高到其他验证者能跟上的程度就可以将群组集中起来。 提高目标会导致费用下降大概创造更多的需求从而提高TPS。 如果验证者没有硬件可以那么快地处理那么多交易,它的确认票最终会变得很长,以至于集群将被迫启动它。

View File

@ -1,61 +0,0 @@
---
title: 验证节点时间戳预言机
---
Solana的第三方用户有时需要知道一个区块产生的真实时间一般是为了满足外部审计人员或执法部门的合规性要求。 本提案描述了一个验证节点时间戳预言机它将允许Solana集群满足这一需求。
提议的实现的总体轮廓如下:
- 每隔一段时间,每个验证节点记录其在链上已知插槽的观察时间(通过添加到插槽投票中的时间戳)。
- 客户端可以使用`getBlockTimeRPC`方法请求一个根块的块时间。 当客户端请求块N的时间戳时
1. 验证节点通过观察记录在账本上的所有引用该插槽的时间戳投票指令确定块N之前的最近时间戳插槽的 "集群 "时间戳,并确定质押加权平均时间戳。
2. 然后使用该集群建立的插槽持续时间来计算块N的时间戳使用这个最近的平均时间戳来计算块N的时间表
要求:
- 任何验证节点在未来重放账本时,都必须在创世以来的每一个区块中找到相同的时间。
- 在解析到真实世界(预言机) 数据之前,估计的区块时间不应偏移超过一个小时左右。
- 块时间不是由单一的集中式预言机控制的,但理想的情况是基于一个使用所有验证节点输入的函数
- 每个验证节点必须维护一个时间戳预言机
同样的实现可以为尚未生根的区块提供一个时间戳估计。 然而,由于最近的时间戳插槽可能生根,也可能还没有生根,所以这个时间戳将是不稳定的(可能不符合要求1)。 最初的实现将以生根的块为目标但如果有最近的块时间戳的用例在未来添加远程调用API将是小事一桩。
## 记录时间
在对某一插槽位进行投票时,每一个验证节点每隔一段时间就会在其提交的投票指令中加入一个时间戳来记录其观察到的时间。 时间戳对应的插槽位是投票向量中最新的插槽位(`Vote::slots.iter().max()`)。 它和一般的投票一样,由验证节点的身份密钥对签名。 为了实现这个报告需要扩展Vote结构以包含一个时间戳字段`timestamp: Option<UnixTimestamp>`,在大多数投票中,它将被设置为`None`。
从https://github.com/Solana-labs/Solana/pull/10630验证节点每次投票都会提交一个时间戳。 这样就可以实现一个区块时间缓存服务允许节点在区块生根后立即计算出估计的时间戳并将该值缓存在Blockstore中。 这提供了持久的数据和快速查询同时还能满足上面的需求1)。
### 投票账户
验证节点的投票账户将在VoteState中保留其最新的插槽期时间戳。
### 投票程序
链上投票程序需要扩展,以处理验证节点发送的带有投票指令的时间戳。 除了其当前的process_vote功能(包括加载正确的投票账户和验证交易签名者是预期的验证节点),这个过程需要将时间戳和对应的插槽位与当前存储的值进行比较,以验证它们都是单调增加的,并将新的插槽位和时间戳存储在账户中。
## 计算质押加权平均时间戳。
为了计算某一特定区块的估计时间戳,验证节点首先需要确定最近的时间戳插槽。
```text
let timestamp_slot = floor(current_slot / timestamp_interval);
```
然后验证节点需要使用`Blockstore::get_slot_entries()`从账本中收集所有引用该插槽的时间戳投票交易。 由于这些交易可能需要一定的时间才能到达并被领导者处理,因此验证节点需要扫描时间戳插槽之后的几个已完成的区块,以获得一组合理的时间戳。 具体的插槽数量需要调整。更多的插槽位将使更多的集群参与和更多的时间戳数据点;更少的插槽位将加快时间戳过滤所需的时间。
从这个交易集合中,验证节点计算出质押加权的平均时间戳,交叉引用`staking_utils::staked_nodes_at_epoch()`中的纪元质押。
任何验证节点重放账本时,都应该通过处理相同数量的时段的时间戳交易,得出相同的桩号加权平均时间戳。
## 计算特定区块的估计时间
一旦计算出一个已知插槽的平均时间戳计算后续块N的估计时间戳就很简单了。
```text
let block_n_timestamp = mean_timestamp + (block_n_slot_offset * slot_duration);
```
其中`block_n_slot_offset`是区块N的插槽与时间戳插槽之间的差值`slot_duration`是根据集群的`slots_per_year`得出的。

View File

@ -1,552 +0,0 @@
---
title: 添加 Solana 到您的交易所
---
本指南描述了如何将 Solana 的原生代币 SOL 添加到某个加密货币交易所。
## 节点设置
我们强烈建议在高级计算机/云端设置至少两个节点实例, 立即升级到较新的版本,并随时注意自带的监测工具的服务操作。
这样设置可以让您:
- 为 Solana mainnet-beta 集群设置一个可信的网关来获取数据和提交取现交易
- 完全控制保留历史区块数据的多少
- 即使某个节点失败仍然保持您的服务可用性
Solana 节点需要较高的计算力来处理我们的快速区块和高 TPS 。 关于具体要求,请参阅[硬件建议](../running-validator/validator-reqs.md)。
运行一个 api 节点:
1. [安装 Solana 命令行工具](../cli/install-solana-cli-tools.md)
2. 启动验证节点时至少使用以下参数:
```bash
solana-validator \
--ledger <LEDGER_PATH> \
--entrypoint <CLUSTER_ENTRYPOINT> \
--expected-genesis-hash <EXPECTED_GENESIS_HASH> \
--rpc-port 8899 \
--no-voting \
--enable-rpc-transaction-history \
--limit-ledger-size \
--trusted-validator <VALIDATOR_ADDRESS> \
--no-untrusted-rpc
```
自定义 `--ledger` 到您所需的账本存储位置, `--rpc-port` 到您想要显示的端口。
`--entrypoint` and `--experted-genesis-hash` 参数都针对您正在加入的集群。 [主网 Beta 的当前参数](../clusters.md#example-solana-validator-command-line-2)
`--limit-ledger-size` 参数允许您指定保留节点的多少个账本 [shreds](../terminology.md#shred) 在磁盘上。 如果您没有配置该参数,验证节点将保留整个账本直到磁盘空间满了为止。 保持账本磁盘使用量的默认值小于 500GB。 如果需要,可以通过添加参数到 `--limit-ledger-size` 来增加或减少磁盘的使用。 查看 `solana-validator --help` 来配置 `--limit-ledger-size` 所使用的默认限制值。 关于选择一个普通限制值的更多信息请参看 [这里](https://github.com/solana-labs/solana/blob/583cec922b6107e0f85c7e14cb5e642bc7dfb340/core/src/ledger_cleanup_service.rs#L15-L26).
指定一个或多个 `--trusted-validator` 参数可以保护您免遭恶意快照的攻击。 [更多关于使用可信验证程序启动的值](../running-validator/validator-start.md#trusted-validators)
可选参数:
- `--private-rpc` 防止您的 RPC 端口被其他节点发布
- `--rpc-bind-address` 允许您指定一个不同的 IP 地址绑定 RPC 端口
### 自动重启和监测
我们建议将每个节点配置退出时自动重启,以确保尽可能少地丢失数据。 把 Solana 软件运行为一个系统服务是很好的选择。
对于监控,我们提供[`solana-watchtower`](https://github.com/solana-labs/solana/blob/master/watchtower/README.md),它可以监视您的验证节点,并且通过 `solana-validator` 检测节点是否不健康。 它可以直接配置 Slack、Telegram 、Discord 或 Twillio 来提醒您。 详情请运行 `solana-watchtower --help`
```bash
solana-watchtower --validator-identity <YOUR VALIDATOR IDENTITY>
```
#### 新软件发布公告
我们经常发布新软件(大约每周一版)。 有时较新的版本包含不兼容的协议调整,这时候需要及时更新软件,以避免出块产生的错误。
我们发布的所有类型的官方公告(正常的和安全)都是通过一个叫做[`#mb-annound`](https://discord.com/channels/428295358100013066/669406841830244375) (`mb` 表示 `mainnet-beta`)。
就像已质押的验证节点,我们期望任何交易所操作的验证节点在正常版本后的一个或两个工作日内尽早更新。 对于安全相关的信息,可能会采取更紧急的行动。
### 账本持续性
默认情况下,您的每个节点都通过可信验证节点提供的快照启动。 这个快照反映了区块链当前的状态,但不包含完整的历史帐本。 如果您的一个节点退出并且通过新的快照启动,那么该节点上的账本中可能会出现一段缺失。 为了防止该问题, 将 `--no-snapshot-fetch` 参数添加到您的 `solana-validator` 命令,来接收历史账本数据(而不是快照)。
不要在初次启动时通过 `--no-snapshot-fetch` 参数,因为它不可能追溯到创世区块去启动节点。 相反,您需要先启动快照,然后添加 `--no-snapshot-quetch` 参数来重启。
重要的一点是需要注意,在任何时候您的节点从网络其他地方可获取的可用历史账本数量都是有限的。 一旦运行,如果验证节点经历了重大故障,它们可能无法跟上网络,需要从可信的验证节点下载新的快照。 这样做的时候,您的验证节点在它的历史账本数据中将出现一个无法填补的空白。
### 最小化验证节点端口风险
验证节点要求从所有其他的 Solana 验证程序中打开 UDP 和 TCP 端口传入流量。 虽然这是最有效率的操作模式,我们也强烈推荐,但是可以将验证节点限制为只需要从另外一个 Solana 验证节点流量接入。
首先添加 `--restricted-reparir-only-mode` 参数。 这将会让验证节点在受限制的模式下运行,它将不会收到其他验证节点的消息,而是要不断联系其他验证节点获取区块。 验证节点只能使用 *Gossip**ServeR* ("服务修理") 端口传输 UDP 包到其他验证节点,并且只有在其 *Gossip**Repair* 端口上接收 UDP 包。
*Gossip* 端口是双向的,允许您的验证节点保持与其他集群的联系。 因为Turbine 现在已被禁用,因此您的验证节点需要在 *ServerR* 上传输信息,以便提出修理请求,从网络其余部分获取新区块。 然后您的验证节点将收到其他验证节点在 *Repair* 端口上的维修回应。
要进一步限制验证节点只从一个或多个验证器请求区块,您首先确定该验证节点身份的 Pubkey 为每一个 PUBKEY 添加 `--gossip-pull-validator PUBKEY --resurir-validator PUBKEY` 参数。 这将使你的验证节点成为您添加的每个验证节点上的资源流量, 您是可以这样操作的,并且只有在与目标验证节点请求后才能进行。
现在您的验证节点只能与特别指出的验证节点通信并且只能在 *Gossip**Repair* 和 *ServeR* 端口上通信。
## 设置存款账户
Solana 帐户不需要任何链上的初始化设置;只要有 SOL 余额,它们就自动出现。 您可以使用任何我们的 [钱包工具](../wallet-guide/cli.md) 生成一个 Solana 密钥,来设置一个交易所存款帐户。
我们建议您为每个用户配置一个独特的存款帐户。
Solana 帐户在每个 epoch 都收取一次 [ 租金 ](developing/programming-model/accounts.md#rent),但如果它们的 SOL 价值包括两年,就可以免除租金。 想要找到您存款账户的最低免租余额,请查询[`getMinimumBalanceForRentExemption` 端点](developing/clients/jsonrpc-api.md#getminimumbalanceforrentexemption)
```bash
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getMinimumBalanceForRentExemption","params":[0]}' localhost:8899
{"jsonrpc":"2.0","result":890880,"id":1}
```
### 离线账户
为了提高的安全性,您可能想离线保存一个或多个收藏账户的密钥。 这时候您需要使用我们的 [ 离线方法](../offline-signing.md) 将 SOL 转移到热钱包。
## 正在等待充值
如果某个用户想 SOL 存入您的交易所,请指示他们发送一笔金额到相应的存款地址。
### 区块投票
您可以使用 Solana API 节点的 JSON-RPC 服务来跟踪交易所的所有存款帐户,对每个确认的区块进行调查或检查感兴趣的地址。
- 要确定哪些区块处于可用状态,请发送 [`getConfirmedBlocks` request](developing/clients/jsonrpc-api.md#getconfirmedblocks),通过您已经处理过的最后一个块作为启动槽参数:
```bash
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlocks","params":[5]}' localhost:8899
{"jsonrpc":"2.0","result":[5,6,8,9,11],"id":1}
```
不是每个 Slot 都会出块,所以在整数序列中可能存在缺口。
- 对于每个块,可以通过 [`getConfirmedBlock` request](developing/clients/jsonrpc-api.md#getconfirmedblock) 请求其包含的内容:
```bash
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[5, "json"]}' localhost:8899
{
"jsonrpc": "2.0",
"result": {
"blockhash": "2WcrsKSVANoe6xQHKtCcqNdUpCQPQ3vb6QTgi1dcE2oL",
"parentSlot": 4,
"previousBlockhash": "7ZDoGW83nXgP14vnn9XhGSaGjbuLdLWkQAoUQ7pg6qDZ",
"rewards": [],
"transactions": [
{
"meta": {
"err": null,
"fee": 5000,
"postBalances": [
2033973061360,
218099990000,
42000000003
],
"preBalances": [
2044973066360,
207099990000,
42000000003
],
"status": {
"Ok": null
}
},
"transaction": {
"message": {
"accountKeys": [
"Bbqg1M4YVVfbhEzwA9SpC9FhsaG83YMTYoR4a8oTDLX",
"47Sbuv6jL7CViK9F2NMW51aQGhfdpUu7WNvKyH645Rfi",
"11111111111111111111111111111111"
],
"header": {
"numReadonlySignedAccounts": 0,
"numReadonlyUnsignedAccounts": 1,
"numRequiredSignatures": 1
},
"instructions": [
{
"accounts": [
0,
1
],
"data": "3Bxs3zyH82bhpB8j",
"programIdIndex": 2
}
],
"recentBlockhash": "7GytRgrWXncJWKhzovVoP9kjfLwoiuDb3cWjpXGnmxWh"
},
"signatures": [
"dhjhJp2V2ybQGVfELWM1aZy98guVVsxRCB5KhNiXFjCBMK5KEyzV8smhkVvs3xwkAug31KnpzJpiNPtcD5bG1t6"
]
}
}
]
},
"id": 1
}
```
` 原先余额 `` 交易后余额 ` 字段能让您跟踪余额每个账户中的变动,而无需解析整个交易。 他们将每个账户的最初和交易后余额分别列出在 [ lamports ](../terminology.md#lamport) 中,并索引到 `账户` 列表。 例如,您准备充值的地址是 ` 47Sbuv6jL7CViK9F2NMW51aQGhfdpUu7WNvKyH645Rfi `,它表示一笔 218099990000 - 207099990000 = 11000000000 lamports = 11 SOL 的交易。
如果需要更多关于交易类型或其他细节的信息,您可以用二进制格式从 RPC 请求区块,然后使用 [Rust SDK](https://github.com/solana-labs/solana) 或 [Javascript SDK](https://github.com/solana-labs/solana-web3.js) 进行解析。
### 地址历史
您也可以查询特定地址的交易历史记录。 这通常 *不是* 一种追踪您所有插槽的所有存款地址的可行方法, 但可能检查一段时间内的几个账户非常有用。
- 向 api 节点发送 [`getConfirmedSignaturesFors2`](developing/clients/jsonrpc-api.md#getconfirmedsignaturesforaddress2) 请求:
```bash
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedSignaturesForAddress2","params":["6H94zdiaYfRfPfKjYLjyr2VFBg6JHXygy84r3qhc3NsC", {"limit": 3}]}' localhost:8899
{
"jsonrpc": "2.0",
"result": [
{
"err": null,
"memo": null,
"signature": "35YGay1Lwjwgxe9zaH6APSHbt9gYQUCtBWTNL3aVwVGn9xTFw2fgds7qK5AL29mP63A9j3rh8KpN1TgSR62XCaby",
"slot": 114
},
{
"err": null,
"memo": null,
"signature": "4bJdGN8Tt2kLWZ3Fa1dpwPSEkXWWTSszPSf1rRVsCwNjxbbUdwTeiWtmi8soA26YmwnKD4aAxNp8ci1Gjpdv4gsr",
"slot": 112
},
{
"err": null,
"memo": null,
"signature": "dhjhJp2V2ybQGVfELWM1aZy98guVVsxRCB5KhNiXFjCBMK5KEyzV8smhkVvs3xwkAug31KnpzJpiNPtcD5bG1t6",
"slot": 108
}
],
"id": 1
}
```
- 对于返回的每个签名,发送 [`getConsulmedTransaction`](developing/clients/jsonrpc-api.md#getconfirmedtransaction) 请求来获取交易细节:
```bash
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc": "2.0","id":1,"method":"getConfirmedTransaction","params":["dhjhJp2V2ybQGVfELWM1aZy98guVVsxRCB5KhNiXFjCBMK5KEyzV8smhkVvs3xwkAug31KnpzJpiNPtcD5bG1t6", "json"]}' localhost:8899
// 结果
{
"jsonrpc": "2.0",
"result": {
"slot": 5,
"transaction": {
"message": {
"accountKeys": [
"Bbqg1M4YVVfbhEzwA9SpC9FhsaG83YMTYoR4a8oTDLX",
"47Sbuv6jL7CViK9F2NMW51aQGhfdpUu7WNvKyH645Rfi",
"11111111111111111111111111111111"
],
"header": {
"numReadonlySignedAccounts": 0,
"numReadonlyUnsignedAccounts": 1,
"numRequiredSignatures": 1
},
"instructions": [
{
"accounts": [
0,
1
],
"data": "3Bxs3zyH82bhpB8j",
"programIdIndex": 2
}
],
"recentBlockhash": "7GytRgrWXncJWKhzovVoP9kjfLwoiuDb3cWjpXGnmxWh"
},
"signatures": [
"dhjhJp2V2ybQGVfELWM1aZy98guVVsxRCB5KhNiXFjCBMK5KEyzV8smhkVvs3xwkAug31KnpzJpiNPtcD5bG1t6"
]
},
"meta": {
"err": null,
"fee": 5000,
"postBalances": [
2033973061360,
218099990000,
42000000003
],
"preBalances": [
2044973066360,
207099990000,
42000000003
],
"status": {
"Ok": null
}
}
},
"id": 1
}
```
## 发送提现请求
要满足用户的提款请求, 您必须生成一笔 Solana 转账交易,并将其发送到 API 节点来扩散到集群中。
### 同步
发送同步传输到 Solana 集群可以让您轻松保证转账的成功并由集群确定最终性。
Solana的命令行工具提供了一个用于生成、提交和确认转账交易的简单命令 `solana transfer`。 默认情况下,该方法将等待并跟踪 stderr 的进度,直到集群确认了某笔交易。 如果交易失败,它将报告任何类型的交易错误。
```bash
solana transfer <USER_ADDRESS> <AMOUNT> --keypair <KEYPAIR> --url http://localhost:8899
```
[Solana Javascript SDK](https://github.com/solana-labs/solana-web3.js) 为 JS 生态提供了类似的方法。 使用 `SystemProgram` 创造一笔转账交易,然后使用 `sendAndConfirmTransaction` 方法提交。
### 异步
为了更大的灵活性,您可以异步提交提现转账。 在这些情况下,您有责任验证交易的成功性并由集群最终确认。
** 请注意:** 每笔交易都包含一个 [ 最新区块哈希 ](developing/programming-model/transactions.md#blockhash-format) 表明它在线。 在某笔提现交易没有被集群确认或最终确定的时候,如果要重新提现,等待这个区块哈希过期是非常 **重要** 的。 否则,你将会面临双花的风险。 更多内容请参见下方 [blockhash expiration](#blockhash-expiration)。
首先,使用 [`getFees` 端点](developing/clients/jsonrpc-api.md#getfees) 或 CLI 命令获取最近的区块哈希:
```bash
solana fees --url http://localhost:8899
```
在命令行工具中,通过 `--no-wait` 参数发送异步传输,使用 `--blockhash` 参数包含您最近的区块哈希:
```bash
solana transfer <USER_ADDRESS> <AMOUNT> --no-wait --blockhash <RECENT_BLOCKHASH> --keypair <KEYPAIR> --url http://localhost:8899
```
您也可以手动化生成、签名和序列化一笔交易,然后用 JSON-RPC [`发送交易` 端点](developing/clients/jsonrpc-api.md#sendtransaction) 将它关闭到某个集群。
#### 交易确认 & 最终性
使用 [`getSignatureStatuses` JSON-RPC 端点](developing/clients/jsonrpc-api.md#getsignaturestatuses) 获取一批交易的状态。 `确认` 字段报告了自交易处理后,有多少 [已确认区块](../terminology.md#confirmed-block) 。 如果 `confirmations: null`,那么它就是 [已经确认](../terminology.md#finality)。
```bash
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getSignatureStatuses", "params":[["5VERv8NMvzbJMEkV8xnrLkEaWRtSz9CosKDYjCJjBRnbJLgp8uirBgmQpjKhoR4tjF3ZpRzrFmBV6UjKdiSZkQUW", "5j7s6NiJS3JAkvgkoc18WVAsiSaci2pxB2A6ueCJP4tprA2TFg9wSyTLeYouxPBJEMzJinENTkpA52YStRW5Dia7"]]}' http://localhost:8899
{
"jsonrpc": "2.0",
"result": {
"context": {
"slot": 82
},
"value": [
{
"slot": 72,
"confirmations": 10,
"err": null,
"status": {
"Ok": null
}
},
{
"slot": 48,
"confirmations": null,
"err": null,
"status": {
"Ok": null
}
}
]
},
"id": 1
}
```
#### 区块哈希过期
当您使用 [`getFees` endpoint](developing/clients/jsonrpc-api.md#getfees) 或 `solana fees` 请求您提款交易最近的区块哈希,响应将包括 `lastValidSlot`,有效区块哈希的最后一个插槽。 您可以使用 [`getSlot` query](developing/clients/jsonrpc-api.md#getslot) 检查集群插槽;一旦集群槽大于`lastValidSlot`,那么使用该区块哈希的提现交易永远不会成功。
您也可以通过发送一个以区块哈希作为参数 [`getFeeCalculatorForBlockhash`](developing/clients/jsonrpc-api.md#getfeecalculatorforblockhash) 的请求,来再次确认某个区块哈希是否仍然有效。 如果响应值为空,那么该区块哈希已经过期,提现请求就一定不会成功。
### 验证用户提供的提款账户地址
由于提款是不可逆过程,因此最好在提款确认之前对用户提供的帐户地址进行验证,以防止用户资产意外丢失。
Solana 的普通账户地址是一个 256 位 ed25519 公钥的 Base58 编码字符串。 并非所有位图案都是 ed25519 曲线的有效公共密钥, 这样可以确保用户提供的帐户地址至少是正确的 ed25519 公钥。
#### Java
这是验证用户提供的地址为有效 ed25519 公钥的 Java 示例:
下面的代码例子假设你正在使用 Maven。
`pom.xml`:
```xml
<repositories>
...
<repository>
<id>spring</id>
<url>https://repo.spring.io/libs-release/</url>
</repository>
</repositories>
...
<dependencies>
...
<dependency>
<groupId>io.github.novacrypto</groupId>
<artifactId>Base58</artifactId>
<version>0.1.3</version>
</dependency>
<dependency>
<groupId>cafe.cryptography</groupId>
<artifactId>curve25519-elisabeth</artifactId>
<version>0.1.0</version>
</dependency>
<dependencies>
```
```java
import io.github.novacrypto.base58.Base58;
import cafe.cryptography.curve25519.CompressedEdwardsY;
public class PubkeyValidator
{
public static boolean verifyPubkey(String userProvidedPubkey)
{
try {
return _verifyPubkeyInternal(userProvidedPubkey);
} catch (Exception e) {
return false;
}
}
public static boolean _verifyPubkeyInternal(String maybePubkey) throws Exception
{
byte[] bytes = Base58.base58Decode(maybePubkey);
return !(new CompressedEdwardsY(bytes)).decompress().isSmallOrder();
}
}
```
## 支持 SPL 代币标准
[SPL 代币](https://spl.solana.com/token) 是在 Solana 区块链上创建和交易包装/合成代币的标准。
SPL 代币的工作流程类似于原生 SOL 代币,但本节将讨论它们的几个不同之处。
### 代币铸造
每种 *类型* 的 SPL 代币都是由一个 *铸造* 账号所产生。 该帐户存储了代币功能的元数据,如供应量、小数点数和对铸造的多种权限。 每个 SPL Token 帐户引用与它铸造相关的字段,并且只能与该种类型的 SPL 代币交互。
### 安装 `spl-token` CLI 工具
使用 `spl-token` 命令行功能查询和修改 SPL Token 帐户。 本部分提供的示例取决于能否在本地系统安装。
`spl-token` 从 Rust [crates.io](https://crates.io/crates/spl-token) 中通过 Rust `cargo` 命令行功能衍生出来的。 最新版本的 `cargo` 可以在 [rustuprers](https://rustup.rs),通过方便的工具安装在您的平台。 一旦 `cargo` 安装完毕, `spl-toke` 可以通过以下命令获得:
```
cargo install spl-token-cli
```
然后您可以检查已安装的版本进行验证
```
spl-token --version
```
输出结果应该类似于
```text
spl-token-cli 2.0.1
```
### 创建帐户
SPL 代币账户包含了本地系统程序账户所不具备的额外要求:
1. 在创建 SPL Token 帐户之前,必须先存入一定数量的代币。 代币帐户可以使用 `spl-token create-account` 命令显式创建, 或者 `spl-token transfer --fund-receiving ...` 命令隐式创建。
1. 在生效期间SPL Token 帐户必须保持 [rent-exempt](developing/programming-model/accounts.md#rent-exemption) 状态,因此在创建帐户时需要存入少量的原生 SOL 代币。 对于 SPL Token v2 账户,该数量为 0.00203928 SOL(2 039 280 lamports)。
#### 命令行
创建具有以下属性的 SPL 代币帐户:
1. 关联指定的铸造
1. 由资产账户的密钥所拥有
```
spl-token create-account <TOKEN_MINT_ADDRESS>
```
#### 示例:
```
$ spl-token create-account AkUFCWTXb3w9nY2n6SFJvBV6VwvFUCe4KBMCcgLsa2ir
Creating account 6VzWGL51jLebvnDifvcuEDec17sK6Wupi4gYhm5RzfkV
Signature: 4JsqZEPra2eDTHtHpB4FMWSfk3UgcCVmkKkP7zESZeMrKmFFkDkNd91pKP3vPVVZZPiu5XxyJwS73Vi5WsZL88D7
```
或者创建指定密钥对的 SPL 代币账户:
```
$ solana-keygen new -o token-account.json
$ spl-token create-account AkUFCWTXb3w9nY2n6SFJvBV6VwvFUCe4KBMCcgLsa2ir token-account.json
Creating account 6VzWGL51jLebvnDifvcuEDec17sK6Wupi4gYhm5RzfkV
Signature: 4JsqZEPra2eDTHtHpB4FMWSfk3UgcCVmkKkP7zESZeMrKmFFkDkNd91pKP3vPVVZZPiu5XxyJwS73Vi5WsZL88D7
```
### 检查账户余额
#### 命令行
```
spl-token balance <TOKEN_ACCOUNT_ADDRESS>
```
#### 示例:
```
$ solana balance 6VzWGL51jLebvnDifvcuEDec17sK6Wupi4gYhm5RzfkV
0
```
### 代币转移
发送代币的源账户是包含余额的实际代币账户。
但是收款人地址可以是一个普通的钱包帐户。 如果给定钱包关联的代币帐户不存在,那么将在发送交易的时候创建一个地址,条件是 `--fund-receiver` 所提供的参数。
#### 命令行
```
spl-token transfer <SENDER_ACCOUNT_ADDRESS> <AMOUNT> <RECIPIENT_WALLET_ADDRESS> --fund-recipient
```
#### 示例:
```
$ spl-token transfer 6B199xxzw3PkAm25hGJpjj3Wj3WNYNHzDAnt1tEqg5BN 1 6VzWGL51jLebvnDifvcuEDec17sK6Wupi4gYhm5RzfkV
发送 1 个代币
发送方6B199xxzw3PkAm25hGJpjj3Wj3WNYNHzDAnt1tEqg5BN
接收方6VzWGL51jLebvnDifvcuEDec17sK6Wupi4gYhm5RzfkV
签名3R6tsog17QM8KfzbcbdP4aoMfwgo6hBggJDVy7dZPVmH2xbCWjEj31JKD53NzMrf25ChFjY7Uv2dfCDq4mGFFyAj
```
### 充值
因为每个 `(user, mint)` 对需要在链上有一个单独的帐户,所以建议交易所提前创建批量代币帐户,并分配给各个用户。 这些账户都由交易所账号密钥所拥有。
存款交易的监控应遵循上面描述的 [block polling](#poll-for-blocks) 方法。 每个新区块应该扫描获得铸造 SPL 代币的成功交易 [Transfer](https://github.com/solana-labs/solana-program-library/blob/096d3d4da51a8f63db5160b126ebc56b26346fc8/token/program/src/instruction.rs#L92) 或 [Transfer2](https://github.com/solana-labs/solana-program-library/blob/096d3d4da51a8f63db5160b126ebc56b26346fc8/token/program/src/instruction.rs#L252) 指令来引用用户帐户,然后查询 [代币账户余额](developing/clients/jsonrpc-api.md#gettokenaccountbalance) 更新。
[Considerations](https://github.com/solana-labs/solana/issues/12318) 正在扩展 `preBalance`和`postBalance` 交易状态元数据字段,来把 SPL代币余额转移包括进去。
### 提现
用户提供的提现地址应该是和普通 SOL 提款地址相同。
在执行提款 [transfer](#token-transfers) 之前,交易所应检查地址符合 [上文所述](#validating-user-supplied-account-addresses-for-withdrawals) 的规则。
从提款地址为正确的铸币确定关联的代币帐户,并将转账发送到该帐户。 请注意关联的代币帐户现在还不存在,因此交易所应该代表用户为该账户提供资金。 对于 SPL Token v2 账户,为提款账户提供的资金额为 0.00203928 SOL (2,039 280 lamports)。
用来提现的 `spl-token transfer` 命令模板为:
```
$ spl-token transfer --fund-recipient <exchange token account> <withdrawal amount> <withdrawal address>
```
### 其他考虑因素
#### 冻结权限
出于法规合规性原因SPL 代币发行实体可以为与铸造相关联的所有帐户选择保留“冻结权限”。 这允许他们按照需要将一个给定帐户的资产 [冻结](https://spl.solana.com/token#freezing-accounts),直到解冻以后才能使用。 如果开放该功能,冻结权限的公钥将在 SPL 代币的铸造账户中注册。
## 测试集成
请务必先在 Solana devnet 和 testnet [clusters](../clusters.md) 测试完整的工作流,然后再迁移到 mainnet-beta 上。 Devnet 是最开放和最灵活、最理想的初始开发方式,而 testnet 提供了更现实的集群配置。 Devnet 和 testnet 都有一个水龙头,您可以通过运行 `solana airdrop 10` 获取一些用来开发和测试的 devnet 或 testnet 的 SOL 代币。

View File

@ -1,33 +0,0 @@
---
title: 简介
---
## Solana是什么
Solana是一个开源项目它实现了一个全新的高性能无许可区块链。 Solana基金会位于瑞士日内瓦维护着开源项目。
## 为什么需要Solana
如果平均每个交易不超过176个字节则标准数据库可能每秒可以处理710,000个交易。 中心化数据库还可以使用称为Optimistic Concurrency Control[\[H.T.Kung, J.T.Robinson (1981)\]](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.65.4735)的分布式系统技术来复制自身并保持高可用性,而不会显着损害交易处理速率。 在Solana我们证明了这些相同的理论极限同样适用于对抗网络上的区块链。 关键点在哪里? 当节点无法彼此信任时,我们找到了一种共享时间的方法。 一旦节点可以相互信任40年的分布式系统研究经验就可以应用于区块链了
> 也许我们的方法与基于超时(timeout) 算法之间最显着的区别是,使用超时会产生一种传统的分布式算法,其中进程异步运行,而我们的方法产生一种全局同步的算法,其中每个进程在同一时刻都执行相同的操作。 我们的方法似乎与分布式处理的整个目的相矛盾,后者旨在允许不同的进程独立运行并执行不同的功能。 但是,如果分布式系统实际上是单个系统,则必须以某种方式同步进程。 从概念上讲,同步流程的最简单方法是让所有流程同时执行相同的操作。 因此,我们的方法用于实现和执行必要同步的内核——例如,确保两个不同的进程不会尝试在同一时间修改文件。 进程可能只花费一小部分时间来执行同步内核;在其余时间里,它们可以独立运行——例如访问不同的文件。 即使在不需要容错的情况下,这也是我们提倡的一种方法。 该方法的基本简单性使其更易于理解系统的精确属性,这对于了解系统的容错能力至关重要。 [\[L.Lamport (1984)\]](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.71.1078)
此外,令我们惊讶的是,它可以使用从第一天开始就存在于比特币中的一种机制来实现。 比特币功能称为nLocktime它可用于使用区块高度而不是时间戳记来对交易进行排序。 作为比特币客户端,如果您不信任网络,则可以使用区块高度而不是时间戳。 事实证明,区块高度是密码学界所谓的“可验证延迟功能”的一个实例。 这是一种表示时间已过去的加密安全方法。 在Solana中我们使用更细粒度的可验证延迟函数SHA 256哈希链来检查并协调共识。 有了它我们实现了乐观并发控制现在正朝着每秒710,000个事务的理论极限迈进。
## 文件概述
Solana文档描述了Solana开源项目这是一个从头开始大规模构建的区块链。 它们涵盖了Solana可用的原因使用方法工作原理以及即使Solana倒闭以后很长一段时间内它将继续工作。 Solana架构的目标是证明了存在一套软件算法当结合使用这些算法来实现区块链时它消除了软件的性能瓶颈使交易吞吐量与网络带宽成比例扩展。 该架构继续满足适当区块链的所有三个理想特性:可扩展、安全性和去中心化。
该体系结构描述了标准千兆位网络上每秒71万笔交易 \(tps\) 的理论上限而40GB上每秒2840万tps的交易上限。 此外该体系结构支持安全并发地执行以通用编程语言例如C或Rust编写的程序。
## 什么是 Solana 集群?
群集是一组可以协同工作的计算机,可以从外部将其视为单个系统。 Solana集群是一组相互独立的计算机这些计算机一起工作(有时互相冲突),以验证用户提交的不可信程序输出。 只要用户希望及时保留事件的不变记录或这些事件的程序解释就可以利用Solana集群。 一种用途是跟踪哪些计算机做了有意义的工作来保持群集运行。 另一个用途可能是跟踪对现实世界资产的拥有权。 在每种情况下,集群都会生成一个称为账本的事件记录。 它将在群集的整个生命周期内保留。 只要世界上某个地方的某人维护了的副本,其程序的输出\(可能包含谁拥有什么的记录\) 将永远是可复制的,而与发起它的组织无关。
## 什么是SOL
SOL是Solana原生代币的名称可以将其传递给Solana集群中的节点以换取运行链上程序或验证其输出。 系统可以执行分数SOL的微支付称为_lamports_。 它们的名称是为了纪念Solana的最大技术影响力[Leslie Lamport](https://en.wikipedia.org/wiki/Leslie_Lamport)。 1 Lamport的值为0.000000001 SOL。
## 免责声明
本项目中描述的所有索赔、内容、设计、算法、预估、路线图、规格和性能度量均由作者尽力而为。 读者应检查并验证其准确性和真实性。 此外,该项目中的任何内容都不构成投资的行为。

Some files were not shown because too many files have changed in this diff Show More