(注:为保证更好的阅读体验,推荐大家可将文章拉到底部点击左下角阅读原文,跳转阅读文章 Notion 版本。)
前言
区块链自诞生以来已经走过了 14 个年头,单片区块链的“不可能三角”问题阻碍了区块链应用的大规模采用。社区在单片链扩容的方向上进行了多次尝试,可结果却不尽如人意,最终模块化区块链的发展方向逐渐迈入正轨。以太坊在 The Merge 之后,将会加速实现 Danksharding 的 DA 层扩容方案。为 Rollup 专设的 Data Blob 数据,也会在固定时间以后被网络删除,如果数据不删除的话,将会导致以太坊的状态爆炸,单个节点要存储的数据将会大大增加,最终影响网络的去中心化,数据不能妥善的存储,则 Data Available 将不能保障,EthStorage 通过构建一个去中心化的存储网络补齐了这个短板。
本篇文章将从去中心化存储的流程开始,由浅入深,为读者逐步分析 EthStorage 的架构并剖析架构之下对于数据 CRUD 操作的细节,帮助理解 EthStorage 的技术潜力,并展望 EthStorage 生态下可能出现的潜在应用。
随着 Web3 的发展,越来越多属于 Web3 的数据被挖掘和产生,而这些数据往往被存储在 web2 的中心化的存储平台上,但是这往往会带来额外的问题,例如中心化服务器的崩溃导致数据丢失,数据审查等问题。
因此去中心化存储也孕育而生,跟中心化存储不同的是,提供数据存储服务的数据节点网络,是通过 p2p 网络进行网络通信和数据同步的,并且任何人都可以无许可 ( permissionless ) 的加入这个网络,成为网络中的一个数据节点 。
从用户端而言,任何用户都可以无许可地存储任何你想存储的数据,只要为数据支付相应的存储费用;最终存储的数据会以多个副本的形式保存在网络内,因此也基本上避免了数据丢失的可能性 ( 容灾性好 )。
需要读取数据的时候,用户的读取请求会由 p2p 网络进行转发,最终存储了该数据的数据节点会响应该读取请求。
EthStorage 是一个去中心化的存储方案,采用 key-value 的存储范式,与处于市场头部的去中心化存储解决项目 Arweave 和 Filecoin 采用的静态文件的存储范式不同。
得益于 key-value 的存储范式,EthStorage 可以支持 CRUD, 即创建新的存储数据,更新存储数据,读取存储数据和删除存储数据。这在中心化存储领域是很容易实现的,但是在去中心化存储领域目前只有 EthStorage 可以支持完整的 CRUD,究其原因,是因为更新的操作跟区块链的不可篡改的特性有冲突;为实现更新数据这一功能,EthStorage 实现了一种充满智慧的存储方案:KV 的存储范式结合定制的存储证明算法 ( EthStorage 的存储证明算法是 Pow 与 Proof-of-Random-Access 的结合体 )。
EthStorage 和其他的去中心化存储方案的对比:
Filecoin 和 Arweave 是当前市场上应用最广泛的去中心化存储解决方案,被许多 Web3 应用所使用。
当我们在以太坊生态里去使用这些去中心化存储方案时,会发现他们和智能合约的互操作性很差。
假设这样一种场景:
用户 A 希望给自己 mint 的 NFT 设置一张图片。
当 A 使用 Arweave 存储图片时:
A 需要先通过 Arweave 的钱包创建并提交一笔存储图片的交易,然后在 Arweave 上等待这笔存储交易成功执行,并获取相应的交易哈希 (ar_image_txhash)。接着 A 回到以太坊上用以太坊的钱包创建一笔 mint NFT 的交易, 当这笔交易成功执行后, 再创建另外一笔 setTokenURI(tokenId , ar_image_txhash) 的交易来将 NFT 的 metadata 指向自己在 Arweave 上存储的图片。
当 A 使用 EthStorage 存储图片时:
A 只需要在 mint 的时候,把自己想设置的图片作为参数传进去,并且在合约实现的时候,确保 mint 的时候其内部会调用 EthStorage Contract 的 put 方法,同时在 setTokenUri( ) 的时候传入调用 put( ) 方法时使用的 key。
这样 A 就只需要执行一笔以太坊交易就完成了给自己 mint 一个带图片的 NFT 的操作;而在使用 Arweave 的时候,A 需要提交 1 笔 Arweave 交易和两笔以太坊交易,并且没办法像 EthStorage 一样做到同步执行,Ethstorage 实现了交易和存储的原子性,成为更加具有竞争优势的存储方案。
兼容 EVM 的 EthStorage 可以给智能合约带来一个近乎完美的互操作性,这在其他的去中心化存储方案中是无法实现的。
EthStorage 实际上采用的是类似 L2 的架构,在以太坊上会部署一个存储合约作为 EthStorage 的数据操作的入口;同时数据节点链下存储数据 (off chain storage data) 的证明也需要通过这个合约验证;证明过程具体见 3.1。
现在的 Rollup 的解决方案是将交易产生的状态树链下存储 (off chain state tree), 将每次状态转换后的状态树根和对应的一批交易 ( 状态转换证明 ) 一 起提交给以太坊上合约的状态转换函数,如果状态转换成功,则会更新合约内存储的状态树根,完成一次状态转换流程。
因此我们可以发现现在 EthStorage 的架构跟 Ethereum L2 的架构是很相似的:
Rollup(L2) 链下存储的是状态树,链上的承诺 (commitment) 是状态树根,同时 Rollup 在接受到新的数据之后还需要在链下执行交易来完成状态转化的过程,建立新的状态树;
EthStorage 链下存储的是数据,链上的承诺 (commitment) 是数据存储的证明,同时 EthStorage 接受到更新存储数据的请求之后,会重新为这些数据生成新的存储证明。
以上我们也可以看出,当前的 optimism rollup 或者 zk-rollup 的扩容的方向是扩容以太坊的计算能力 ( 链下执行出新的状态树 ) , 而 EthStorage Rollup 的扩容方向是扩容以太坊存储数据能力。
EthStorage 的客户端是以太坊客户端 Geth 的超集,这意味着运行 EthStorage 的节点的时候,依然可以正常参与以太坊的任何流程。
从图中可以看到,当启动一个 EthStorage Node 的时候,实际上是运行着一个 Geth 和 Data Provider 的结合体,内部运行的 Geth 可以保证节点正常参与以太坊网络,例如,一个节点可以是以太坊的验证者节点的同时也是 EthStorage 的数据节点。
每个 EthStorage Node 的 Data Provider 模块会跟其他 EthStorage Node 的 Data Provider 发起建立连接请求,当它们互相连接之后,实际上就构成了一个去中心化存储网络。
在下图中,橙色网络代表去中心化的以太坊网络,绿色网络代表由 EthStorage 支持的去中心化存储网络。
首先 EthStorage 会在以太坊主网上部署一个智能合约来支持 CRUD。
智能合约具体如下:
contract DecentralizedKV {
function put(bytes32 key, bytes memory data) public payable {
}
function get(
bytes32 key,
uint256 off,
uint256 len
) public view returns (bytes memory) {
}
function remove(bytes32 key) public {}
function verify(bytes32 key, bytes memory data) public view returns (bool){}
}
合约中每个方法的具体使用场景:
put : 这是一个写数据的方法,调用这个方法会将你给出的数据 (data) 存储在我们对应的数据分片 (shard) 中,并且你可以在读取方法中通过对应的 key 读到它。
get : 这是一个查询方法,调用这个方法可以进行获取要查询的 key 的对应数据。
remove : 这是一个删除数据的方法,调用这个方法可以会删除 key 对应的数据。
verify: 这是一个验证方法,可以检查分片 (shard) 中存储的数据的数据哈希是否是跟合约内的数据哈希匹配,如果匹配证明链下存储的数据是正确的。
应用写数据到 EthStorage 是通过调用合约中的put(bytes32 key, bytes memory data)方法进行的
我们在以太坊上部署一个智能合约 ( 如下图 ),当用户在put(bytes32 key, bytes memory data)方法中填上任意的 key 和 data 之后,发起一笔以太坊交易调用这个方法,在等到交易执行成功时,合约内会记录这次写操作的 key,以及对应数据的数据哈希 (dataHash) 以及一个 EthStorage 分配的 kvIdx; 当 EthStorage 客户端监听到有 put 交易发生时,会根据数据被分配的 kvIdx 将数据存储在数据分片中对应的位置。
在这个调用过程中,用户所需要存储的 data 存储在 EthStorage 的网络中,这是一个以太坊的链下网络,而存储在链上智能合约中的数据仅有 key,datahash,kvIdx 三个值。这和以太坊的 Rollup 较为类似,即交易数据存储在链下的 Rollup 网络里,仅仅将 calldata 和 proof 上传到以太坊主网的合约里面。
更加具体数据存储流程如下图所示:
假设某个用户提交了一笔存储交易(调用 put(key,data)),并且被当前的提案区块所包含,当这个区块中的这笔交易进入虚拟机 (EVM) 中执行并且执行成功之后;以太坊世界状态中会记录下 key 以及要存储数据 和该数据的数据哈希 (dataHash),同时给这个 key 分配一个 kvIdx。
当整个区块完全执行成功的时候,验证者若同时运行验证节点和数据节点,验证节点会根据正常流程更新自己的世界状态 ( 更新世界状态的过程中也在合约账户内更新最新存储的 key,data,dataHash),然后数据节点会将内存中要存储的原数据 (data) 根据分配的 kvIdx 存储到对应数据分片 (shard) 的对应位置上。( 根据 kvIdx 选择 DataShard 请见下文 )
如果是没有运行数据节点的验证者,执行 geth 的正常流程,只进行状态树的正常更新。
当用户需要更新存储在 EthStorage 上的数据的时候,依然是通过发起一笔以太坊调用合约的交易,来执行 put 方法:
调用方法
put( key , new_data )
key: 用户之前存储数据对应的 key
new_data: 用户希望存储的新数据
执行流程
更新数据的执行流程跟存储数据流程基本差不多,具体如下:
下图中标注为红色方格的数据,是 update 过程中被修改的数据。
用户发起一笔调用合约 put 方法的交易: submit Tx{ put( key , new_data )};
这笔交易会将记录在合约中对应的 key 的哈希更新: dataHash = hash(new_data);
EthStorage 客户端 的 ShardManager 监听到有新的 put 交易发生,获取到 put 方法中携带的要更新的数据 new_data;
ShardManager 将要更新的数据进行 mask 之后替换之前存储的数据;store( kvIdx , mask(new_data))
下图中标注为红色方格的数据,是 update 过程中被修改的数据。
3.3 存储布局
EthStorage 对数据的存储采用分片 (shard) 存储,每个矿工可以选择自己感兴趣的分片 (shard) 进行存储。
EthStorage 规定每个分片 (shard) 拥有固定数量的 键值对数量,并且每一个 键对应的数据大小有一个最大限制。
例如一个 400 KB 大小的 Shard , 它的包含的 KV 条目的数量固定为 100 ,那么每个 键值对对应的 数据大小最大不能超过 4KB。
数据分片和每个键能存储的数据大小的关系如下:
ShardSize = MaxKVSize * MaxKvEntries
maxKvSize 代表每一个键对应的数据大小的最大限制
maxKvEntries 代表每个数据分片 可以存储的键值对的最大数量。
每一个键对应的数据可能需要多个 Chunk 来存储:
ChunkNum = MaxKVSize / ChunkSize
ChunkNum 存储每一个键的数据需要的 Chunk 数量
ChunkSize 每个 chunk 能存储的数据大小
分配 kvIdx
当每个用户调用put(key,data)的时候,合约会为每个 Put 操作提供一个 kvIdx , 每发生一次 Put 操作, kvIdx 就会自增一次,因此每个 Put 操作都有自己对应的一个 kvIdx。
data 被存储在哪个分片 (shard) 完全由 kvIdx 决定,其所在的分片 (shard) 的计算公式如下:
ShardId = kvIdx / MaxKvEntries
3.4 验证数据
核心原理是 EthStorage 会在合约内记录 key 所对应数据的哈希,在每次进行随机访问证明 (Proof-of-Random-Access) 的时候会将矿工上传的数据进行哈希,然后与合约内存储的数据数据哈希进行验证,只有合约内存储的数据哈希和上传的数据哈希相等,随机访问证明才会生成一个有效的证明。
3.5 读取数据
可以在对 EthStorage 合约中提供的 get(key) 方法进行本地调用,可以通过 key 找到对应的 kvIdx,然后根据 kvIdx 向数据节点发送读取请求,数据节点判断该 kvIdx 对应的数据是刚好在自己存储的数据分片中的时候,会向用户回复请求的数据;如果矿工没有存储对应数据,则会帮助用户转发读取数据的请求。
3.6 删除数据
假设以下场景:
KeyA 对应的 kvIdx 为 1 ,用户此时要将 KeyA 对应的数据移除;当前最大的 kvIdx 对应的键为 KeyB。
调用方法:
remove(key)
key: 你想要删除数据的索引 key
删除步骤:
发起一笔调用 remove 方法的交易: submit Tx{remove(KeyA)};
ShardManager 将 KeyB 对应的数据读出来之后,复制到 KeyA 的数据存储位置: copy(chunkIdx(KeyA), readMasked(keyB));
ShardManager 将 KeyB 的数据 ( chunk99 ) 删除: delete(chunk99);
合约 将 KeyB 对应的 kvIdx 从 99 改为 1: updateKvIdx(KeyB, 1);
合约 将 lastKVIdx 由 99 改为 98: lastKvIdx =lastKvIdx - 1;
在文章的三章我们基本介绍了 EthStorage 写数据、更新数据、读数据和删除数据的具体实现,接下来我们将会介绍 EthStorage 是怎么通过经济激励的方式来使每个数据分片被足够的数据节点所存储。
去中心化存储和中心化存储最大的区别是存储服务不再是由一个中心化的实体去运营,任何节点都可以无需许可的加入存储网络,选择自己感兴趣的数据提供存储服务。
以太坊这种区块链系统也可以满足去中心化存储的特性,但是存在一个巨大的缺点,存储成本太过昂贵,而造成这一现象的原因是因为所有以太坊的节点都在维护同一份数据,换句话说,每份数据的副本数量太多。
因此后来出现的实现去中心化存储的方案都是希望在保证数据去中心化存储的同时降低存储成本,最简单的解决方案就是每个节点不再需要存储全部的数据,而是可以选择自己感兴趣的数据进行存储,从而通过显著降低每份数据的副本数量,来降低数据存储成本。
恰好你是这个去中心化存储方案的设计者,在这种实现思路下,需要考虑以下几个问题:
一个节点如何高效地证明它完整的存储了一个数据分片的数据?
如何给节点发放存储奖励?
在这个系统下,怎么保证存储的数据不丢失?或者说怎么保证这份数据有足够保证安全的副本数量?
如何让每份数据的副本数量基本保持一致?
我们来考虑如何解决上面提出的问题 (1),很显然节点证明存储了某一份数据最直接的证明方法是直接把原数据展示出来。
这是一个看起来很美好的方案,但是在去中心化存储的应用下,我们往往需要每次证明自己完整存储了一个数据分片的全部数据,而一个数据分片的数据是不会少于 TB 级别的,这意味着我们想在智能合约上做一次证明的成本极其高昂,并且这么大量的数据会对数据节点的带宽和内存提出极大的挑战,因此我们希望找到一种只要极少数据就能证明你存储了整个数据分片的证明方案。
EthStorage 通过其定制的随机访问证明来解决这个问题:
分片 (shard) 的存储格局我们上文已经介绍过了,在写数据的时候,会为每个 key 对应的所有 chunk 构造一个默克尔树,并且会将这棵树的树根存储在合约中。
每次随机访问证明会随机采样分片 (shard) 里的多个 chunk,随机访问位置的取决于矿工的 nonce 和这个数据分片上一次被挖出时的哈希, 如下:Random Access Positions = Random( nonce, miningHash)。
我们假设在当前环境某次证明的随机位置为 (0,20,99), 此时我们需要被验证的数据为下图的黄色块,因为我们仅仅需要提供 chunk0、chunk20、chunk29 对应的数据以及其默克尔树的证明路径。
其中 chunk 的大小为 4KB,默克尔树的中间节点 (node2、node4、node5) 大小为 32 个字节。
这个方案基本上实现了通过极少的数据证明矿工完整存储了某个分片的数据的目标。
这时你可能会有这样的疑惑:随机访问证明是通过随机选择要检查的位置实现的,那么数据节点可能不完整存储一个数据分片的数据也有机会证明成功?
在我们的随机访问证明的实现中,要求所有随机到的位置的数据都要能验证通过才算一个成功的证明;因此假如一个矿工仅仅存储一个数据分片中 80% 的数据,进行 3 次抽样的成功率为 0.512;进行 6 次抽样的结果为 0.262。
在 EthStorage 的实现中,我们会对每个分片进行 16 次 ( 计划 ) 抽样,因此作为一个理性的矿工,一定会选择存储整个分片的数据。
EthStorage 的奖励发放策略和 Pow 类似,都是第一个提交符合当前难度值的随机访问证明的节点才会得到奖励。
数据节点通过挖矿赢得存储奖励的步骤:
从 4.1 节中我们已经知道随机访问证明的访问位置如下: random_access_positions = Random( nonce , miningHash )
根据当前的 Random Access Positions 生成 Proof of Random Access: PoRA_data = generatePoRA(random_access_positions)
对当前生成的 PoRA 数据做混合 (MIX) 操作,比如 XOR : mix_data = Mix(PoRA_data)
将混合后的数据进行哈希运算,得出一个最终的哈希值: final_hash = Hash(mix_data)
检查 final_hash 是否可以满足当前数据分片的难度要求:
如果算出来的哈希值满足难度要求,接着执行步骤 (6)。
如果 final_hash 不满足难度值要求,数据节点需要将 nonce++,重新执行步骤 (1)。
数据节点会通过合约提交自己挖出来的有效随机访问证明以及对应的 nonce: submit Tx{mine( PoRA_data, nonce)}
合约中会进行以下验证:
验证 PoRA 的有效性。
对 PoRA 重新执行步骤 (3) (4) , 在链上算出 final_hash ,然后检查能否满足合约内记录的当前难度值的要求 ( 这个有效性检查和步骤 (5) 的检查一样,区别在于一个在链上一个在链下 )。
满足难度值要求继续步骤 (8)。
不满足难度值要求,这笔智能合约调用的交易会失败,数据节点损失交易手续费,程序退出。
当数据节点提交一个成功的随机访问证明之后,合约为矿工发放存储奖励。
矿工每次提交一个满足难度值的随机访问证明可以获得的存储奖励公式如下:
d 代表每秒的贴现率 ,c 则是一个常量,用来控制基础的存储费用。
简单来说矿工可以收取从上一次满足难度值的随机访问证明到当前时间该数据分片所有键值对的存储费用。
根据与上一次数据节点挖出有效的时间间隔,来动态调整难度值: diff = adjust(mine_time , previous_mine_time)
合约内会计算会计算此次挖出有效的随机访问证明与上一次的间隔时间: internal = mine_time - previous_mine_time
将实际间隔时间和期望的间隔时间 ( 目前的系统配置的期望时间是 1 小时 ) 做比较:
如果实际的时间间隔小于期望的时间间隔,调高难度值。
如果实际的时间间隔大于期望的时间间隔,调低难度值。
EthStorage 开创了 Pow + Proof-of-Random-Access 来解决去中心化存储中需要提供时空证明的问题;其中 Pow 负责证明数据节点在某段时间存储该数据,PoRA 证明你确实在你的硬盘中存储了该数据分配的所有数据。
通过这种特殊且具有开创性的方式 EthStorage 才有能力将数据存储的时空证明和链共识解藕,将每笔数据 CRUD 的操作和数据节点的证明和奖励等机制都可以通过发起一笔以太坊交易来实现,从而使 EthStorage 可以完美的扩容 Ethereum 的存储能力,并且成为一个完美的数据存储 L2 ;这也是 EthStorage 与 Arweave 和 Filecoin 最大的不同。
在这里我们将论述 EthStorage 如何解决问题 (3) 和问题 (4) 。
问题 (3) 和 (4) 实际上都是通过难度调整算法来解决的,当一个数据分片太多节点挖的时候,这个数据分片生成的随机访问证明需要满足的难度值会上升,于是一部分节点会离开这个分片,重新选择一个难度值降低的分片来赚取存储奖励,于是我们基本上可以保证维持每个数据分片的节点数量基本一致。
控制数据的副本数量,或者换句话说,控制存储相应数据分片的节点数量,则主要通过经济模型来解决。EthStorage 可以通过调整存储奖励跟计算和存储成本之间关系,来控制支持数据分片的节点数量,因为每个节点都是因为有利可图才会提供存储服务的。
首先我们规定在去中心化存储经常提到的 seal 或 mask 统一翻译为密封数据,这一过程实际上是对数据取掩码的操作。
在去中心化存储解决方案里,密封数据是每个方案的必备操作。
因为如果每个数据节点仅仅存储原数据,意味着多个矿工可以共享一份存储数据,这违背了我们希望每个数据节点都提供一份数据的设计目的。
因此我们需要结合矿工的公钥或地址对数据进行密封,保证每个矿工都需要自己存储一份数据,达到维持每份数据都有足够的副本数量。
密封算法如下:
注意:由于数据节点存储的是密封过的数据,当我们找数据节点读取数据时,也需要执行解开密封数据的操作;因此我们希望密封算法在解封时候尽可能简单高效。
密封算法的设计原则
密封算法的设计原则是让存储成本低于计算成本与带宽成本之和。
因为 mask 算法是为了避免多个节点存储同一份数据这种情况的发生,而在这种情况下,这些节点计算一个合法随机访问证明的步骤如下:
从专用存储节点那里传输一份数据。
将收到的数据进行密封。
生成随机访问证明和最终哈希 H。
判断 H 是否符合当前难度值,如果不符合重新执行步骤 (1)。
因此只要设计一个算法,保证 1 和 2 的成本高于直接在将 mask 后的数据在本地持久化存储的成本,那么矿工就没有动力做这种事情,从而达到让每个数据节点独立保存一份数据副本的设计目标。
解决数据没有填充满一个数据分片的问题
在去中心化存储系统中,随着系统中存储的数据不断增加,会不断开辟新的数据分片,但是当一个数据分片刚被开辟的时候其上的数据是很少的,比如现在这个数据分片就存储一个 KV 的数据,那么在这种情况下矿工可能会偏好这种新的数据分片。
但是由于我们在矿工的真实存储中,存的都是密封过的数据,即便你的整个数据分片都是空的,经过密封后,数据分片的真实大小和存满数据的数据分片的大小是一样的,因此矿工就会失去故意存储新数据分片的动力,从而让新数据分片和老数据分片的数据节点数量都趋于平稳。
在去中心化的存储系统中绕不开一个问题是怎么设计一个合理的存储费用的收费模型,保证数据尽可能有足够安全数量的数据副本的同时用户的存储成本不要太昂贵。
EthStorge 的数据存储费用主要分为两部分:
以太坊交易的手续费
调用合约 put(bytes32 key, bytes memory data) 方法:
在以太坊交易中目前 calldata 的每一个字节需要花费 3 gas; calldata_cost = len(data) * 3
需要通过合约调用一次 put 操作并且完成其中的逻辑 ; contract_call_cost
在合约中使用一个新的 slot 进行存储 数据; slot_cost = 21000
数据存储费用
假如用户希望支付从当前到未来时间 Tp的存储费用,那么他需要支付的费用满足公式:
d 代表每秒的贴现率 ,c 则是一个常量,用来控制基础的存储费用。
第一次存储数据的总成本如下:
put_cost = calldata_cost + contract_call_cost + slot_cost + p
EthStorge 的数据更新费用主要为两部分:
以太坊交易的手续费
调用合约 put(bytes32 key, bytes memory data) 方法:
在以太坊交易中目前 calldata 的每一个字节需要花费 3gas; calldata_cost = len(data) * 3 gas
需要通过合约调用一次 put 操作并且完成其中的逻辑 ; contract_call_cost
在合约中更新合约内某一个 slot 里存储的数据;slot_cost = 5000 gas
数据存储费用
更新数据不需要在支付数据存储费用。
更新数据的总成本如下:
update_cost = calldata_cost + contract_call_cost + slot_cost
EthStorge 的数据更新费用主要为两部分:
以太坊交易的手续费
调用合约 remove(bytes32 key) 方法:
由于 remove 的参数仅仅只是一个 32 位字节的 key,所以 calldata 的成本基本可以忽略不计
需要通过合约调用一次 remove 操作并且完成其中的逻辑 ; contract_call_cost
数据存储费用
假设用户希望在时间 Tr 移除数据,那么它可以收到退款:
删除数据的总成本如下:
remove_cost = contract_call_cost - r
我们假设当前 EthStorage 网络一共有三个数据分片, shard0 , shard1,shard2, 同时每个数据分片 (shard) 被多个数据节点存储。
Store_Shard0_Nodes ={Node0, Node1,Node2,Node5} ;
Store_Shard1_Nodes ={Node2, Node3,Node4,Node6};
Store_Shard2_Nodes ={Node2, Node5,Node6};
此时 EthStorage 的网络状态如图:
网络中拥有相同数据分片的节点会相互建立 Tcp 连接用来同步数据。
每个数据节点可以根据自己的喜好存储相应的数据分片。
当数据节点完整存储一个数据分片的时候,它就可以参与到通过 PoRA 挖矿赚取存储奖励的过程中。
当一个数据分片被越多数据节点存储的时候,它的挖矿难度就会越高;相反,当一个数据分片被越少数据节点存储的时候,它的挖矿难度就越低。
我们知道网页或互联网服务的数据是托管在中心化的服务器之中的,当我们停止为服务器续费,应用所使用的云服务会停止,应用的数据将会被中心化的服务方删除。用户通过 URL 来访问网络中的资源,其中:
Scheme:代表协议,不止有 https,还有 ipfs,ftp,ssh 等。不同协议代表不同类型的应用在提供资源。
Host:(subdomain,domain,top level domain)代表主机,对应的 IP 地址由 DNS 提供服务,也就是通过将域名和互联网中服务器资源的 IP 地址映射来获取服务器中的数据。
Port:端口,指提供服务的应用。
Path:是路径,代表资源在服务器中的路径。
Query:(Query string/parameter,query string separator)是查询条件,代表资源中的某个部分)
Fragment:代表二级查询条件,用于前端展示定位内容
与 web2 通过访问服务器资源的方式不同,web3:// Access protocol 通过 Web3 URL 直接去渲染在以太坊智能合约上面托管的资源,包括 HTML、CSS、PDF 等这样的文件。
以上是 Web3URL 图示,介绍如下:
Scheme:使用的是 Web3 Access Protocol,具体可以参考EIP-4804
userinfo:交易调用者的地址
contractName:可以是合约地址或者反向解析合约地址以后的域名
ChainID:指的是对应的区块链的链 ID
methodId:指合约中对应的方法
arg0:可以是钱包地址或整数,对应该合约中某地址的情况或者合约中某个序号下的数据情况
query:希望返回的数据格式
目前 web3:// Access Protocol 并不能被常规的浏览器所直接解析,所以需要使用 Gateway 来对 web3:// Access Protocol 进行解析,以适配浏览器,未来会通过以太坊超轻节点来实现和浏览器的集成。
w3link.io/w3eth.io 是现在使用的网关,其中 w3link.io 适用于多链,而 w3eth.io 更偏向于转译 Ethereum。
Format:
https://[name].[NS_suffix].[chain_id or short name].w3link.io/
example:
web3://w3url.eth/ => https://w3url.eth.eth.w3link.io/
集成 web3:// Access Protocol 的浏览器可以使用“web3://w3url.eth/”直接访问链上智能合约所托管的资源,但是并未集成的浏览器需要使用 w3link.io 的 gateway 转译为 https://w3url.eth.eth.w3link.io/,其中 w3url 代表了合约名字;第一个.eth 代表“Ethereum Name Service;第二个.eth 代表了 Ethereum 链,如测试网之类的用 ChainID;w3link.io 是 gateway。
如果想在 FireFox 浏览器中直接使用 web3:// Access Protocol,不进行转译的话,可以下载转译的扩展插件https://addons.mozilla.org/en-US/firefox/addon/web3q/
解析过程
以上图为例介绍解析的过程:
合约 0xaabbccddee…拥有者通过 ENS 注册一个 app-uniswap-org.eth 的域名,并将该域名反向解析到 0xaabbccddee…合约地址。
通过集成了 web3:// Access Protocol 的浏览器,输入 web3URL :“web://app-uniswap-org.eth”。
浏览器解析出 app-uniswap-org.eth 对应的合约地址 0xaabbccddee…,并调用 call 方法请求该合约的 calldata 数据。
ENS 文本记录中 web3 条目所对应的 EVM 链响应请求并返回 0xaabbccddee…合约所托管的数据给浏览器进行渲染。
目前的 NFT 都是将 Metadata 存放在 Arweave 和 IPFS 里面为主,仅仅将返回的内容 Hash 提交上链,这是因为将 NFT 元数据上链的话会特别贵,因为数据会占据较大的区块空间,所以之前的 NFT,大多将元数据放在链下。但是使用 Ethstorage 的方案,用户可以将 NFT 的数据直接放在链上,通过智能合约来托管数据,并通过 Web3 Access Protocol 访问数据在前端渲染出来。相较之下,我们最常使用的 NFT 交易市场 Opensea,它的前端所展示出来的 NFT 也并不是直接获取的元数据,而是将对应 NFT 的元数据存储在 Opensea 中心化的服务器里面,以此获得更高的访问体验。使用 Ethstorage 的方案,用户不仅能将 NFT 数据直接存在链上,而且可以通过前端直接获取到链上的 NFT 元数据,极大的改善了用户体验。
除了可以实现 On-chain NFT 之外,通过利用智能合约的可编程性实现一些动态 NFT,三维 NFT,以及 NFT 的组合,可编程性意味着有无限的可能,对于 On-chain Game,链上 NFT 的可组合和可编程可以激发游戏的创造性,诞生更多的玩法。
个人网站的数据通过智能合约把本地目录的所有数据上传,最好通过 Web3 Access Protocol 把本地目录映射成为智能合约上面托管的文件系统,当用户想要访问相对路径和绝对路径的资源的时候,会通过 calldata 去访问到需要的数据内容。
除此之外,也可以使用 ERC-5018(Filesystem-Like Interface)的协议去访问数据,ERC-5018是一个基于智能合约的协议,使得智能合约可以提供一个像文件系统一样的接口去供用户访问数据。
此外,对于数据的上传,可以使用w3q-deployer to synchronize folder/files批量的将本地的文件上传到区块链上面。
我们知道以太坊是一个去中心化的网络,在以太坊上面诞生了很多去中心化的 dapp,可这些 dapp 并不是完全去中心化的,很多应用的前端依然是通过中心化的云服务在托管,像 Uniswap 的前端网页宕机,删除交易对以及 Tornado.Cash 因为涉嫌洗钱被监管而导致前端服务停用等都是因为其前端是托管在中心化的服务器上面,无法有效抗审查。但是使用 EthStorage 的方案,网页文件和数据被托管在智能合约中,由去中心化的网络共同运行和维护,使得抗审查性大大提高。通过智能合约的可编程性实现 DeWeb,可以实现很多有意思的应用,比如 De-github,De-blog,以及各种 dapp 的前端。
我们知道 Mirror 使用的是 Arweave 来进行数据的存储,而 Arweave 是一个数据永存的存储协议,用户通过 Mirror 创作的文章最终是存储在 Arweave 上面的,因为是永久存储和不可篡改的特性,意味着用户通过 Mirror 发布的文章是没办法在原来的基础上修改的,如果要修改需要将修改后的文章重新发起交易再存储到 Arweave 上面,这就造成了网络上数据的重复存储,且需要承担双倍的存储成本。但是选择使用 EthStorage 来构建类似 Mirror 的应用,用户可以通过 Update 的操作直接更新存储的数据而不用覆盖之前存储的数据。
EthStorage 是一个扩容以太坊存储能力的 Rollup , 通过 EthStorage ,可以实现使以太坊直接拥有低成本存储 PB/TB 级别的数据存储能力,并且对 EthStorage 的数据操作都可以直接调用在以太坊上存储合约的方法来实现,因此 EthStorage 是一种兼容 EVM 的去中心化存储方案。
EthStorage 是一种底层采用 KV 的存储方案,可以支持 CRUD。
EthStorage 通过随机访问证明结合挖矿算,可以实现证明数据在某段时间被数据节点完整的存储的同时动态控制每个数据分片的数据节点数量,保持整个存储网络的稳定。
EthStorage 的数据节点是以太坊客户端 Geth 的超集,因此运行 EthStorage 的节点也可以很轻松的接入以太坊网络。
【1】https://ethresear.ch/t/ethstorage-scaling-ethereum-storage-via-l2-and-da/14223
【2】https://ethstorage.io/
【3】https://perfect-amphibian-929.notion.site/Proof-of-Storage-on-Large-Dynamic-Datasets-Part-1-2-0f8328c85270479988d8fd964548b8bb
【4】https://file.w3q.w3q-g.w3link.io/0x67d0481cc9c2e9dad2987e58a365aae977dcb8da/dynamic_data_sharding_0_1_6.pdf
关于 EthStorage
EthStorage 是基于以太坊数据可用(Data Availability)的基础上提供可编程的动态存储的二层解决方案(L2)。EthStorage 将会大大降低以太坊上大量数据的存储开销,节省 100 到 1000 倍成本,更好的支持未来完全去中心化的网络。EthStorage 与 EVM 高度整合,并完全兼容 Solidity、Remix、Hardhat 和 MetaMask 等以太坊工具。团队获得了以太坊基金会官方对于数据可用(Data Availability)研究的资助。
官网:https://ethstorage.io/
Twitter:https://twitter.com/home
关注我们:
【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。