Skip to content

Commit

Permalink
review-state
Browse files Browse the repository at this point in the history
  • Loading branch information
hattiepwvi committed May 26, 2024
1 parent ed8c80f commit 3dda151
Showing 1 changed file with 57 additions and 57 deletions.
114 changes: 57 additions & 57 deletions docs/SolanaDocumention/state-compression.md
Original file line number Diff line number Diff line change
@@ -1,148 +1,148 @@
# 状态压缩

Solana 上,状态压缩是创建链外数据“指纹”(或哈希)并将该指纹存储在链上以进行安全验证的方法。有效利用 Solana 账本的安全性来安全地验证链下数据,验证其未被篡改
Solana 状态压缩是一种在链上存储链下数据指纹(哈希)并用于安全验证的方法。有效利用 Solana 账本的安全性来验证链下数据的完整性,确保其未被篡改

这种“压缩”方法允许 Solana 程序和 dApp 使用廉价的区块链账本空间(而不是更昂贵的账户空间)来安全地存储数据
通过状态压缩, Solana 程序和 dApp 能够以较低的成本使用链上存储空间来安全存储数据,而不是昂贵的账户空间

这是通过使用一种特殊的二叉树结构(称为并发默克尔树)来创建每条数据(称为叶子)的哈希值,将它们哈希在一起,然后仅将最终哈希值存储在链上来实现的
这种压缩方法采用了一种特殊的二叉树结构(称为并发默克尔树)来创建每条数据(称为叶子)的哈希值,并将这些哈希值相互连接,最终只将根哈希值存储在链上

## 什么是状态压缩?

简而言之,状态压缩使用“树”结构以确定性方式将链下数据加密地散列在一起,以计算存储在链上的单个最终散列
状态压缩使用树形结构以确定性方式将链下数据加密地散列在一起,以计算最终的链上哈希值

这些树是在这个“确定性”过程中通过以下方式创建的
这些树是通过以下过程创建的

- 获取任何数据
- 创建此数据的哈希值
- 将此哈希值存储为树底部的叶子
- 然后将每个叶子对散列在一起,创建一个分支
- 然后将每个分支散列在一起
- 不断地爬树并将相邻的分支散列在一起
- 一旦到达树的顶部,就会产生最终的根哈希
- 将此哈希值存储为树底部的叶子 `leaf`
- 然后将每个叶子对散列在一起,创建分支 `branch`节点
- 然后将每个分支节点哈希在一起
- 并将相邻的分支哈希在一起
- 不断重复此过程,直到到达树的顶部,得到最终的根哈希 `root hash`

然后将该根哈希存储在链上,作为每个叶子中所有数据的可验证证明。允许任何人以加密方式验证树中的所有链外数据,同时实际上仅在链上存储最少量的数据。因此,由于这种“状态压缩”,显着降低了存储/证明大量数据的成本
然后,将该根哈希存储在链上,作为链下数据完整性的验证证明。这使得任何人都能以加密方式验证所有链下数据的完整性,同时实际上只在链上存储了最少量的数据。因此,通过状态压缩,大大降低了存储和验证大量数据的成本

## 默克尔树和并发默克尔树

Solana 的状态压缩使用了一种特殊类型的默克尔树,它允许对任何给定的树进行多次更改,同时仍然保持树的完整性和有效性。
Solana 的状态压缩使用了一种特殊类型的默克尔树,它允许对树进行多次更改,同时仍然保持树的完整性和有效性。

这种特殊的树,被称为“并发默克尔树”,有效地在链上保留了树的“变更日志”。在证明失效之前,允许对同一棵树进行多次快速更改(即所有更改都在同一块中)。
这种特殊的树,被称为“并发默克尔树”,有效地在链上保留了树的“变更日志”。允许对同一棵树进行多次快速更改(即所有更改都在同一块中), 直到证明失效

### 什么是默克尔树?
### 什么是默克尔树?

Merkle 树,有时称为“哈希树”,是一种基于哈希的二叉树结构,其中每个叶节点都表示为其内部数据的加密哈希。每个不是叶子的节点(称为分支)都表示为其子叶子哈希的哈希。
Merkle 树,有时称为“哈希树”,是一种基于哈希的二叉树结构,其中每个叶节点都表示为其内部数据的加密哈希。每个非叶子节点(称为分支)都表示为其子叶子哈希的哈希。

然后,每个分支也被散列在一起,爬上树,直到最终只剩下一个散列。这个最终的哈希值称为根哈希值或“根”,然后可以与“证明路径”结合使用来验证叶节点中存储的任何数据。
然后,每个分支也被散列在一起,一直到树的根节点,最终只剩下一个散列。这个最终的哈希值称为根哈希值或“根”,然后可以与“证明路径”结合使用来验证叶节点中存储的任何数据。

一旦计算出最终的根哈希,就可以通过重新哈希特定叶子的数据和攀爬树的每个相邻分支的哈希标签(称为证明或“证明路径”)来验证叶节点中存储的任何数据。将此“重新散列”与根散列进行比较是对底层叶数据的验证。如果它们匹配,则数据被验证准确。如果它们不匹配,则叶数据已更改。
通过重新哈希特定叶子的数据并沿着树的每个相邻分支散列(称为证明路径)来验证叶节点中存储的任何数据。将此“重新散列”与根散列进行比较是对底层叶数据的验证。如果它们匹配,则数据被验证为准确。如果不匹配,则叶数据已更改。

无论何时需要,都可以通过简单地散列新叶数据并以与原始根相同的方式重新计算根散列来改变原始叶数据。然后使用这个新的根哈希来验证任何数据,并有效地使先前的根哈希和先前的证明无效。因此,对这些传统merkle树的每次改变都需要串行执行
无论何时需要,都可以通过简单地散列新叶数据并以与原始根相同的方式重新计算根散列来改变原始叶数据。然后使用这个新的根哈希来验证任何数据,并有效地使先前的根哈希和先前的证明无效。因此,对这些传统 merkle 树的每次改变都需要串行执行

> 小贴士
>
> 使用 Merkle 树时,更改叶数据和计算新的根哈希的过程可能是非常常见的事情!虽然它是树的设计点之一,但它可能导致最显着的缺点之一:快速变化。
> 使用 Merkle 树时,更改叶数据和计算新的根哈希的过程可能非常常见!虽然这是树的设计点之一,但它可能导致最明显的缺点之一:快速变化。
### 什么是并发默克尔树? #
### 什么是并发默克尔树?

运行时内,验证器可以相对快速地连续接收更改链上传统 Merkle 树的请求(例如在同一槽内)。每个叶子数据更改仍需要串行执行。由于根哈希和证明被槽中的先前更改请求无效,导致每个后续更改请求失败
在 Solana 上,验证者可以在同一槽内接收到多个对传统 Merkle 树的更改请求。然而,每个叶数据更改仍然需要串行执行。这样的串行执行会导致在相同槽内对同一棵树进行多次更改的请求失败,因为前一个更改会使根哈希和证明无效

输入并发默克尔树。

并发默克尔树存储最近更改的安全更改日志、根哈希以及派生它的证明。此变更日志“缓冲区”存储在链上特定于每棵树的帐户中,具有最大数量的变更日志“记录”(也称为 maxBufferSize
并发默克尔树解决了这个问题,它存储了最近的更改日志、根哈希以及生成该根哈希的证明。这个变更日志缓冲区存储在链上特定于每棵树的账户中,其大小由最大数量的变更日志记录(也称为 `maxBufferSize`)确定

当验证器在同一槽中接收到多个叶数据更改请求时,链上并发默克尔树可以使用此“更改日志缓冲区”作为更可接受的证明的事实来源。有效地允许在同一槽中对同一棵树进行最多 maxBufferSize 的更改。显着提高吞吐量
当验证者在同一槽内接收到多个叶数据更改请求时,链上的并发默克尔树可以使用这个“变更日志缓冲区”作为更可接受的证明来源。这有效地允许在同一槽内对同一棵树进行最多 `maxBufferSize` 次的更改,从而显著提高了吞吐量

### 调整并发默克尔树的大小

创建这些链上树之一时,有 3 个值将决定树的大小、创建树的成本以及树的并发更改数量:
在创建并发默克尔树时,有 3 个值将决定树的大小、创建树的成本以及树的并发更改数量:

1. max depth
2. max buffer size
3. canopy depth
1. `max depth` 最大深度
2. `max buffer size` 最大缓冲区大小
3. `canopy depth` 冠层深度

### 最大深度

树的“最大深度”是从任何数据叶到树根的最大跳数。

由于默克尔树是二叉树,因此每个叶子仅与另一个叶子相连;作为一对叶存在。

因此,树的 maxDepth 用于通过简单的计算来确定要在树中存储的节点(也称为数据块或叶子)的最大数量:
因此,树的 `maxDepth` 用于通过简单的计算来确定要在树中存储的节点(也称为数据块或叶子)的最大数量:

``nodes_count = 2 ^ maxDepth``
`nodes_count = 2 ^ maxDepth`

由于必须在创建树时设置树深度,因此您必须决定希望树存储多少数据。然后使用上面的简单计算,您可以确定存储数据的最低 maxDepth。
由于必须在创建树时设置树深度,因此必须决定希望树存储多少数据。然后使用上面的简单计算,确定存储数据的最低 `maxDepth`

#### 示例 1:铸造 100 nfts

如果你想创建一棵树来存储 100 个压缩的 nft,我们至少需要“100 个叶子”或“100 个节点”。
如果想创建一棵树来存储 100 个压缩的 nft,至少需要“100 个叶子”或“100 个节点”。

```js
// maxDepth=6 -> 64 nodes2^6 = 64
// maxDepth=6 -> 64 nodes2^6 = 64
// maxDepth=7 -> 128 nodes2^7 = 128
```

我们必须使用 maxDepth 7 以确保我们可以存储所有数据。
必须使用 maxDepth 7 以确保我们可以存储所有数据。

#### 示例 2:铸造 15000 nfts

如果你想创建一棵树来存储 15000 个压缩的 nft,我们至少需要“15000 个叶子”或“15000 个节点”。
如果想创建一棵树来存储 15000 个压缩的 nft,至少需要“15000 个叶子”或“15000 个节点”。

```js
// maxDepth=13 -> 8192 nodes2^13 = 8192
// maxDepth=13 -> 8192 nodes2^13 = 8192
// maxDepth=14 -> 16384 nodes2^14 = 16384
```

我们必须使用 maxDepth 14 以确保我们可以存储所有数据
必须使用 maxDepth 14 以确保可以存储所有数据

#### 最大深度越高,成本越高

值将是创建树时成本的主要驱动因素之一,因为您将在创建树时预先支付此成本。最大树深度越高,可以存储的数据指纹(也称为哈希)越多,成本就越高
最大深度是创建并发默克尔树时的一个关键因素,因为它直接影响到树的存储能力和成本。较高的最大深度意味着树可以存储更多的数据指纹(或哈希),但也会增加创建树的成本

### 最大缓冲区大小

“最大缓冲区大小”实际上是在根哈希仍然有效的情况下树上可以发生的最大更改数量
最大缓冲区大小确定了树上可以发生的最大更改数量,而不会使根哈希无效

由于根哈希实际上是所有叶子数据的单个哈希,因此更改任何单个叶子都会使更改常规树的任何叶子的所有后续尝试所需的证明无效。

但对于并发树,实际上存在这些证明的更新的变更日志。该变更日志缓冲区的大小是在树创建时通过 maxBufferSize 值进行调整和设置的。
但对于并发树,实际上存在这些证明的更新的变更日志。该变更日志缓冲区的大小是在树创建时通过 `maxBufferSize` 值进行调整和设置的。

### 冠层深度

“树冠深度”,有时称为树冠大小,是任何给定证明路径在链上缓存/存储的证明节点的数量
冠层深度(或树冠大小)是指在链上缓存或存储的证明节点的数量

当对叶子执行更新操作时,例如转让所有权(例如出售压缩的 NFT),必须使用完整的证明路径来验证叶子的原始所有权,从而允许更新操作。此验证是使用完整的证明路径来正确计算当前根哈希(或通过链上“并发缓冲区”的任何缓存根哈希)来执行的。

树的最大深度越大,执行此验证所需的证明节点就越多。例如,如果您的最大深度为 14,则总共需要使用 14 个证明节点来进行验证。随着树变得越来越大,完整的证明路径也变得越来越大。
树的最大深度越大,执行此验证所需的证明节点就越多。例如,如果最大深度为 14,则总共需要使用 14 个证明节点来进行验证。随着树变得越来越大,完整的证明路径也变得越来越大。

通常,每个证明节点都需要包含在每个树更新交易中。由于每个证明节点值在事务中占用 32 个字节(类似于提供公钥),因此较大的树将很快超过最大事务大小限制。

进入canopy。 Canopy 能够在链上存储一定数量的证明节点(对于任何给定的证明路径)。允许在每个更新交易中包含较少的证明节点,从而使总体交易大小保持在限制以下。
进入 canopy。 Canopy 能够在链上存储一定数量的证明节点(对于任何给定的证明路径)。允许在每个更新交易中包含较少的证明节点,从而使总体交易大小保持在限制以下。

例如,最大深度为 14 的树总共需要 14 个证明节点。如果 canopy 为 10,则每个更新交易只需要提交 4 个证明节点。

#### 冠层深度值越大,成本越高

canopyDepth 值也是创建树时的主要成本因素,因为您将在创建树时预先支付此成本。 Canopy 深度越高,链上存储的数据证明节点越多,成本越高。
`canopyDepth` 值也是创建树时的主要成本因素,因为将在创建树时预先支付此成本。 Canopy 深度越高,链上存储的数据证明节点越多,成本越高。

#### 较小的冠层限制了可组合性

虽然树冠越高,树的创建成本就越高,但树冠深度越低,则需要在每个更新事务中包含更多的证明节点。需要提交的节点越多,交易规模就越大,因此越容易超出交易规模限制
尽管树的冠层越高,创建成本就越高,但较小的冠层深度意味着每个更新事务需要包含更多的证明节点。随着需要提交的节点数量增加,交易规模也会增大,因此更容易超出交易规模限制

对于任何其他尝试与您的树/叶子交互的 Solana 程序或 dApp 来说也是如此。如果您的树需要太多证明节点(由于树冠深度较低),则另一个链上程序可以提供的任何其他附加操作将受到其特定指令大小加上您的证明节点列表大小的限制。限制可组合性以及特定树的潜在附加实用性
这不仅对树和叶子交互的 Solana 程序或 dApp 产生影响,对于任何试图与树/叶子进行交互的其他 Solana 程序或 dApp 也是如此。如果树需要太多的证明节点(由于较小的冠层深度),那么其他链上程序可以提供的任何额外操作都将受到指令大小以及证明节点列表大小的限制。这限制了可组合性,并可能限制了特定树的附加实用性

例如,如果您的树用于压缩 NFT 并且树冠深度非常低,则 NFT 市场可能只能支持简单的 NFT 传输。并且无法支持链上竞价系统
例如,如果树用于压缩 NFT 并且树冠深度非常低,则 NFT 市场可能只能支持简单的 NFT 传输。并且无法支持链上竞价系统或其他复杂功能

## 创建一棵树的成本

创建并发默克尔树的成本基于树的大小参数:maxDepthmaxBufferSize 和 canopyDepth。这些值都用于计算树存在于链上所需的链上存储(以字节为单位)。
创建并发默克尔树的成本基于树的大小参数:`maxDepth``maxBufferSize``canopyDepth`。这些值都用于计算树存在于链上所需的链上存储(以字节为单位)。

一旦计算出所需的空间(以字节为单位),并使用 getMinimumBalanceForRentExemption RPC 方法,请求在链上分配此字节数的成本(以 lamports 为单位)。
一旦计算出所需的空间(以字节为单位),并使用 `getMinimumBalanceForRentExemption` RPC 方法,请求在链上分配此字节数的成本(以 lamports 为单位)。

### 在 JavaScript 中计算树成本

@solana/spl-account-compression 包中,开发人员可以使用 getConcurrentMerkleTreeAccountSize 函数来计算给定树大小参数所需的空间。
`@solana/spl-account-compression` 包中,开发人员可以使用 `getConcurrentMerkleTreeAccountSize` 函数来计算给定树大小参数所需的空间。

然后使用 getMinimumBalanceForRentExemption 函数获取为链上树分配所需空间的最终成本(以 lamports 为单位)。
然后使用 `getMinimumBalanceForRentExemption` 函数获取为链上树分配所需空间的最终成本(以 lamports 为单位)。

然后确定 lamports 的成本,以使此规模的帐户免租金,类似于任何其他帐户创建。

Expand All @@ -151,12 +151,13 @@ canopyDepth 值也是创建树时的主要成本因素,因为您将在创建
const requiredSpace = getConcurrentMerkleTreeAccountSize(
maxDepth,
maxBufferSize,
canopyDepth,
canopyDepth
);

// 获取链上存储花费
const storageCost =
await connection.getMinimumBalanceForRentExemption(requiredSpace);
const storageCost = await connection.getMinimumBalanceForRentExemption(
requiredSpace
);
```

### 成本示例
Expand Down Expand Up @@ -187,9 +188,8 @@ const storageCost =
- 最大叶节点数:1,048,576
- 树冠深度 15 的创建成本约为 15.814 SOL

## 压缩NFT
## 压缩 NFT

压缩 NFT 是 Solana 上状态压缩最流行的用例之一。通过压缩,一百万个 NFT 集合的铸造成本约为 50 SOL,而未压缩的等效集合则约为 12,000 SOL。

如果您有兴趣自己创建压缩 NFT,请阅读我们有关 [铸造和传输压缩 NFT ](https://solana.com/zh/developers/guides/javascript/compressed-nfts)的开发人员指南。

如果有兴趣自己创建压缩 NFT,请阅读我们有关 [铸造和传输压缩 NFT ](https://solana.com/zh/developers/guides/javascript/compressed-nfts)的开发人员指南。

0 comments on commit 3dda151

Please sign in to comment.