前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【刘文彬】区块链 + 大数据:EOS存储

【刘文彬】区块链 + 大数据:EOS存储

作者头像
圆方圆学院
修改于 2019-01-08 13:35:40
修改于 2019-01-08 13:35:40
9440
举报

原文链接:醒者呆的博客园,https://www.cnblogs.com/Evsward/p/storage.html

谈到区块链的存储,我们很容易联想到它的链式存储结构,然而区块链从比特币发展到今日当红的EOS,技术形态已经演化了10年之久。目前的EOS的存储除了确认结构的链式存储以外,在状态存储方面有了很大的进步,尤其是引入了MongoDB plugin以后,可以将功能有限的状态库搭上大数据的班车。本文将全面介绍EOS的存储技术。

EOS 存储,Merkle Tree,mongodb,chainbase,源码学习,context_free_actions

EOS的链式存储结构

EOS的区块数据结构如下:

field

explanation

timestamp

时间戳

producer

生产者

confirmed

生产者确认数

previous

链式结构前一个区块的id

transaction_mroot

交易默克尔树根

action_mroot

动作默克尔树根

schedule_version

生产者版本排序号

new_producers

下一个生产者

header_extensions

区块头扩展字段

producer_signature

区块签名,由生产者签名

transactions

块打包交易内容,是数组结构,可以多个

block_extensions

区块扩展字段

id

当前块id

block_num

当前块高度

ref_block_prefix

引用区块的区块头

Merkle Tree

默克尔树的演化路线是 Hash => Hash Tree => Merkle Tree ,他们都是为解决数据一致性而存在的,具体的含义如下:

  • Hash 是我们都熟知的技术了,它可以为一个文件或其他数据生成一个Hash值,我们在下载一个文件时,通常会在下载页面看到这个文件的Hash值以及该Hash值的算法,下载完毕以后,我们可以在本地对整个文件进行同样的Hash算法得到Hash值,然后与网页上的Hash值进行对比,如果相同,则说明文件完整,是网页上的源文件,如果不匹配则说明文件损坏,被修改或者不完整。
  • 仍旧是文件完整性的校验需求,当这个文件特别大的时候,对这个文件进行Hash算法是能耗巨大的,所以可以将文件切割成很多的小块,每一个小块都有一个Hash,然后将所有小块的Hash值拼在一起再次进行Hash算法得到的就是Root Hash。这样一来,我们在下载大文件的时候,会先下载一个包含Root Hash的Hash list,通过校验Root Hash可以确定Hash list的正确性,确定Hash list正确以后,再逐个下载小块文件并逐一验证Hash,当发现某个小块Hash不匹配的时候,就可以单独重新下载该小块即可,而不必重新下载全部。Hash list的结构实际上是一个Root Hash 为根,小块Hashs为叶子节点的树高为2的Hash Tree。
  • Merkle Tree实际上是对Hash List的优化,它极大的提高了性能。它的结构是一个二叉树(也可以是多叉树,性能优化的关键点是它的高度是大于等于2的),每个节点最多只有两个子节点,只有叶节点是根据小块文件做的Hash,每两个相邻的叶节点的父节点是由这两个Hash做的父Hash,如果叶节点的总数是单数,则会剩余一个,逐级而上,最终会有一个的根节点,这个根节点就是Merkle Root。这样以来,我们在下载大文件的时候,会首先下载一个Merkle Tree,从最左下叶节点进行校验,逐级而上,将整个Merkle Tree校验完毕。这里面不同于上面Hash Tree的是,只要最左下相邻的两个叶节点的Hash值与他们的父节点的Hash通过了匹配,则可以立即开始下载这两个叶节点对应的文件块,并行地,再校验其他叶节点,这就提高了性能,不必校验完整的Merkle Tree之后再下载文件。

Merkle Tree 与 区块链

上面的区块数据结构中包含了两个与Merkle Tree相关的字段:

  • transaction_mroot,一个区块中的transactions字段可以包含多笔交易,区块中的transaction_mroot是所有该区块内打包的交易的Merkle Root,可以用来校验其中的每笔交易的正确性。如果该区块中不包含任何交易,则该字段的值为0000000000000000000000000000000000000000000000000000000000000000。节点同步数据的时候,会先将交易的Merkle Tree下载并通过Merkle Root来校验,而不是将所有的交易主体全部下载下来,这样可以节省轻节点的数据量。
  • action_mroot,创建一个基于所有分发的action的根,在区块内接收交易时进行评估。用在轻客户端的校验,功能同上。
代码语言:txt
AI代码解释
复制
    action_mroot是始终有值的,哪怕transaction_mroot是0,这是因为出块本身也是一个action动作onblock,这个动作调用的是system合约的onblock函数。TODO:源码分析
代码语言:txt
AI代码解释
复制
/**
*  At the start of each block we notify the system contract with a transaction that passes in
*  the block header of the prior block (which is currently our head block)
*/
signed_transaction get_on_block_transaction()
{
  action on_block_act;
  on_block_act.account = config::system_account_name;
  on_block_act.name = N(onblock);
  on_block_act.authorization = vector<permission_level>{{config::system_account_name, config::active_name}};
  on_block_act.data = fc::raw::pack(self.head_block_header());

  signed_transaction trx;
  trx.actions.emplace_back(std::move(on_block_act));
  trx.set_reference_block(self.head_block_id());
  trx.expiration = self.pending_block_time() + fc::microseconds(999'999); // Round up to nearest second to avoid appearing expired
  return trx;
}

context_free_actions

通过对eosio.null账户的nouce动作,可以将无签名的数据打包进入context_free_action字段,结果区块信息如下:

代码语言:txt
AI代码解释
复制
evsward@evsward-TM1701:~/work/src/github.com/eos/tutorials/bios-boot-tutorial$ cleos --wallet-url http://127.0.0.1:6666 --url http://127.0.0.1:8000 get block 440
{
  "timestamp": "2018-08-14T08:47:09.000",
  "producer": "eosio",
  "confirmed": 0,
  "previous": "000001b760e4a6610d122c5aa5d855aa49e29f3052ac3e40b9e1ef78e0f1fd02",
  "transaction_mroot": "32cb43abd7863f162f4d8f3ab9026623ea99d3f8261d2c8b4d8bf920ab97e3d1",
  "action_mroot": "09afeaf40d6988a14e9e92817d2ccf4023b280075c99f13782a6535ccc58cbb0",
  "schedule_version": 0,
  "new_producers": null,
  "header_extensions": [],
  "producer_signature": "SIG_K1_K2eFDzbxCg3hmQzpzPuLYmiesrciPmTHdeNsQDyFgcHUMFeMC3PntXTqiup5VuNmyb7qmH18FBdMuNKsc7jgCm1TSPFbaj",
  "transactions": [{
      "status": "executed",
      "cpu_usage_us": 290,
      "net_usage_words": 16,
      "trx": {
        "id": "d74843749d1e255f13572b7a3b95af9ddd6df23d1d0ad19d88e1496091d4be2b",
        "signatures": [
          "SIG_K1_KVzwg3QRH6ZmempNsvAxpPQa42hF4tDpV5cqwqo7EY4oSU7NMrEFwG7gdSDCnUHHhmH1EwtVAmV1z9bqtTvvQNSXiSgaWG"
        ],
        "compression": "none",
        "packed_context_free_data": "",
        "context_free_data": [],
        "packed_trx": "8497725bb601973ea96f0000000100408c7a02ea3055000000000085269d000706686168616861010082c95865ea3055000000000000806b010082c95865ea305500000000a8ed3232080000000000d08cf200",
        "transaction": {
          "expiration": "2018-08-14T08:49:08",
          "ref_block_num": 438,
          "ref_block_prefix": 1873362583,
          "max_net_usage_words": 0,
          "max_cpu_usage_ms": 0,
          "delay_sec": 0,
          "context_free_actions": [{
              "account": "eosio.null",
              "name": "nonce",
              "authorization": [],
              "data": "06686168616861"
            }
          ],
          "actions": [{
              "account": "eosiotesta1",
              "name": "hi",
              "authorization": [{
                  "actor": "eosiotesta1",
                  "permission": "active"
                }
              ],
              "data": {
                "user": "yeah"
              },
              "hex_data": "0000000000d08cf2"
            }
          ],
          "transaction_extensions": []
        }
      }
    }
  ],
  "block_extensions": [],
  "id": "000001b8d299602b289a9194bd698476c5d39c5ad88235460908e9d43d04edc8",
  "block_num": 440,
  "ref_block_prefix": 2492570152
}

正常的actions的内容是hi智能合约的调用,而context_free_action中包含了无签名的data数据,是已做数字摘要后的形态。源码中的操作:

代码语言:txt
AI代码解释
复制
//lets also push a context free action, the multi chain test will then also include a context free action
("context_free_actions", fc::variants({
    fc::mutable_variant_object()
       ("account", name(config::null_account_name))
       ("name", "nonce")
       ("data", fc::raw::pack(v))
    })
 )

EOS的StateDB

我们来设想一个场景:

代码语言:txt
AI代码解释
复制
A账户转账给B账户100个SYS,如何查看A账户的余额?

对于不知道以上动作何时发生的我们来讲,我们要如何做呢:

  • 首先是从头扫描区块内的交易,交易内的action,直到找到A账户被创建的action所对应的区块号。
  • 从这个区块号开始继续扫描,要将所有A账户的转账,包括收入和支出的所有action记录下来并统计。
  • 算出A的当前余额。

以上步骤很容易出错且繁琐,每一次的余额查询都要重复这些操作实在是毫无意义,因此StateDB就诞生了,这个库顾名思义就是用来存储状态数据的,如果有了StateDB,上面的场景的解决办法就是:

  • 从A账户被第一次收入SYS开始,为A账户在StateDB中建立一个table,存储A账户的余额,每当A账户发生转账的action,都会同步更新StateDB中相关table中A账户的余额的值,当我们需要知道A账户的余额时,我们可以直接查找这个余额state即可。

测试用例

这里为大家提供一个测试方法,也是我的命令history:

代码语言:txt
AI代码解释
复制
cleos create key
cleos wallet import 5JA9oDotJHoKnjUV6NrAMx4g5gWTCVCRLybTnG1XVU3EKGZZeNY
cleos wallet import --private-key 5JA9oDotJHoKnjUV6NrAMx4g5gWTCVCRLybTnG1XVU3EKGZZeNY
cleos wallet keys
cleos wallet import --private-key 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
cleos wallet keys
cleos create account eosio usertesta1 EOS761KfjhYy3FSypZGG5hePrR8K2wzmw75JCDXgypKt2DLXZoZPW
cleos create account eosio eosio.token EOS761KfjhYy3FSypZGG5hePrR8K2wzmw75JCDXgypKt2DLXZoZPW
cleos set contract eosio.token work/src/github.com/eos/build/contracts/eosio.token/
cleos push action eosio.token create '["eosio","100000000.0000 SYS"]'
cleos push action eosio.token issue 
cleos push action eosio.token issue '["usertesta1","10000000.0000 SYS"]' -p eosio.token
cleos push action eosio.token issue '["usertesta1","10000000.0000 SYS"]' -p eosio
cleos get currency balance eosio.token usertesta1
cleos get table eosio.token usertesta1 accounts
cleos get table eosio.token eosio accounts

可以看到当我想获得usertesta1账户的余额时,是通过查询StateDB的table来获取的,而不是最开始的那种扫块的笨方法。

链式存储和StateDB存储的区别

  • 链式存储,存储的是固定结构的数据:Block=> Block Header/ transactions=>actions,一个action的结构例子:
代码语言:txt
AI代码解释
复制
{
    "account": "eosiotesta1",
    "name": "hi",
    "authorization": [{
      "actor": "eosiotesta1",
      "permission": "active"
    }],
    "data": {
    "user": "yeah"
    },
    "hex_data": "0000000000d08cf2"
}

这个例子中,我们调用了hello合约的hi函数,data传入的格式是hi函数中自定义的,所以在链式存储中,留给我们发挥的空间也即在此。

  • StateDB,存储的是一个最终要记录的状态,这个状态数据必须是有意义的,是有人关心的,无关紧要的数据请不要放在StateDB中去,所以StateDB是可以增删改查的,就像一个普通数据库那样,在合约中通过multi_index来操作,具体请参照文章EOS技术研究:合约与数据库交互
代码语言:txt
AI代码解释
复制
    很多人搞不明白为什么区块链不可篡改,却在StateDB中好像可以修改还能删除?

其实不是这样的,链式存储的内容会将所有的动作action全部记录下来,是所有的过程数据,是流水帐,元数据,这些数据一旦上链是不可修改,不可删除的。而StateDB只是为了保存一个状态信息,这个状态信息的修改与删除并不影响区块链的不可篡改的特性。

目前StateDB的主流实现方式是将它放在内存中,而当有些人对StateDB的认识有偏差造成滥用的时候,会引发内存过载,因此一方面我们要非常清楚的理解StateDB的含义,一方面EOSIO帮助我们提供了一个mongodb-plugin插件来同步StateDB数据。

mongodb

安装

  • 下载tgz安装包
  • 解压安装到/usr/local/bin(或者其他某路径)
  • sudo mkdir /data/db

普通模式

  • sudo mongod
  • mongo

服务模式

我们也可以使用ubuntu系统的服务模式。

代码语言:txt
AI代码解释
复制
曾经我们要定义一个系统启动时自启动服务的方式是在/etc/init.d 目录下写一个脚本来执行,现在在ubuntu的服务模式下,我们可以丢弃那种方式,服务模式的命令是service,而现在的ubuntu系统推崇使用的systemctl命令,他俩的使用方法的区别就在于参数的顺序。
  • 定义一个配置文件mongod.conf
  • 定义一个服务文件,放置在/etc/systemd/system/
代码语言:txt
AI代码解释
复制
    sudo vi /etc/systemd/system/mongodb.service
代码语言:txt
AI代码解释
复制
[Unit]
Description=High-performance, schema-free document-oriented database
After=network.target
 
[Service]
User=mongodb
ExecStart=['mongod' command location] --quiet --config /etc/mongod.conf
 
[Install]
WantedBy=multi-user.target
  • 查找服务状态
代码语言:txt
AI代码解释
复制
    systemctl list-unit-files
  • 查询mongodb服务的激活状态
代码语言:txt
AI代码解释
复制
    systemctl is-enabled mongodb
  • 激活系统自启动服务
代码语言:txt
AI代码解释
复制
    sudo systemctl enable mongodb
  • 启动mongodb服务
代码语言:txt
AI代码解释
复制
    sudo systemctl start mongodb
  • 查询mongodb服务状态
代码语言:txt
AI代码解释
复制
    sudo systemctl status mongodb
  • 停止mongodb服务
代码语言:txt
AI代码解释
复制
    sudo systemctl stop mongodb

###调试模式

IDE选择CLion,EOS源码下载最新的,保证本地可以使用脚本编译通过,安装了相关依赖包,然后在CLion中导入EOS,CLion会自动识别CMakeList.txt文件生成makefile文件并make编译执行。编译时可能会遇到错误,一般来讲要么是环境依赖没有配置好,要么就是CMakeList.txt要有修改,例如mongodb-plugin导入时要在总开关配置上开启。

代码语言:txt
AI代码解释
复制
set(BUILD_MONGO_DB_PLUGIN "true")

全部编译成功以后,会自动识别出可以debug的target,与EOS中配备CMakeList.txt的模块一一对应。

安装Mongo Explorer插件

上面我们介绍了MongoDB的安装方法,以及启动nodeos时的配置方法(除了上文提到的总开关,当然要在config.ini文件末尾设置上plugin = eosio::mongo_db_plugin,这部分内容演练多次,这里不再赘述。)链启动开始出块以后,会同步到mongodb中去(注意要预先启动mongod守护进程,可以理解为服务端),通过mongo命令接入可使用mongo命令查询数据,但这样很不方便。可以在CLion中安装mongo-plugin,配置好效果如下:


相关文章和视频推荐

【许晓笛】EOS 数据库与持久化 API —— 实战

圆方圆学院汇集大批区块链名师,打造精品的区块链技术课程。 在各大平台都长期有优质免费公开课,欢迎报名收看。

公开课地址:https://ke.qq.com/course/345101?flowToken=1007371

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Kubernetes中ConfigMap的使用
ConfigMap 是一种 API 对象,用来将非机密性的数据保存到键值对中。使用时, Pods 可以将其用作环境变量、命令行参数或者存储卷中的配置文件。
王先森sec
2023/10/17
3930
腾讯云TKE使用之ConfigMap挂载
ConfigMap(Kubernetes简称cm)作为Kubernetes 提供的一种资源,可以将配置信息,跟业务镜像解耦开来,提高业务镜像的移植性。
白鹏飞
2020/05/17
1.8K0
Kubernetes 漫游:理解 ConfigMap
通过 docker desktop 可以安装适用于单机和开发环境单机版的 K8S,如果 docker desktop 无法启动 Kubernates 通过以下方式解决:
phoenix.xiao
2023/11/16
3080
Kubernetes 漫游:理解 ConfigMap
kubernetes ConfigMap使用详解
在应用部署时,配置文件几乎是绕不开的,通常我们是把配置文件放置在指定目录下,通过配置文件使应用修改的灵活性更高。但是如果我们把应用打包成容器镜像后,容器内的镜像文件就不容易修改了,一般我们会采用以下方式修改容器中的配置文件:
星星在线
2020/11/19
2K0
kubernetes ConfigMap使用详解
Kubernetes中的Configmap和Secret
如果需要向容器传递参数,可以在Yaml文件中通过command和args或者环境变量的方式实现。
大江小浪
2018/12/05
1.9K0
Kubernetes中的Configmap和Secret
【K8S专栏】Kubernetes应用配置管理
不论什么样的应用,基本都有配置文件,在企业中,大部分会用到配置中心,比如apollo、nacos等,也有一些公司直接使用Kubernetes自带的配置管理,主要有:
没有故事的陈师傅
2022/12/06
6110
ConfigMap(二)
一旦创建了 ConfigMap,就可以在 Kubernetes Pod 中使用它。有两种方法可以使用 ConfigMap 中的配置数据:作为环境变量或作为卷。
玖叁叁
2023/05/02
3290
「走进k8s」Kubernetes1.15.1的ConfigMap(26)
PS:ConfigMap是kubernetes的一个核心的概念,跟上次说的service一样,这个在实际的环境中使用很频繁。当ConfigMap以数据卷的形式挂载进Pod的时,这时更新ConfigMap(或删掉重建ConfigMap),Pod内挂载的配置信息会热更新。这时可以增加一些监测配置文件变更的脚本,然后reload对应服务。ConfigMap允许您将配置文件从容器镜像中解耦,从而增强容器应用的可移植性。
IT架构圈
2019/08/29
6890
「走进k8s」Kubernetes1.15.1的ConfigMap(26)
K8S ConfigMap使用
configmap是k8s的一个配置管理组件,可以将配置以key-value的形式传递,通常用来保存不需要加密的配置信息,加密信息则需用到Secret,主要用来应对以下场景:
oYabea
2020/09/07
3.6K0
一文学会 Kubernetes 之 ConfigMap
我们知道,在几乎所有的应用开发中,都会涉及到配置文件的变更,比如说在web的程序中,需要连接数据库,缓存甚至是队列等等。而我们的一个应用程序从写第一行代码开始,要经历开发环境、测试环境、预发布环境只到最终的线上环境。而每一个环境都要定义其独立的各种配置。如果我们不能很好的管理这些配置文件,你的运维工作将顿时变的无比的繁琐。为此业内的一些大公司专门开发了自己的一套配置管理中心,如360的Qcon,百度的disconf等。kubernetes也提供了自己的一套方案,即ConfigMap。kubernetes通过ConfigMap来实现对容器中应用的配置管理。
kubernetes中文社区
2019/06/24
7730
【云原生 | Kubernetes篇】Kubernetes 配置(十五)
要使用 Secret,Pod 需要引用 Secret。 Pod 可以用三种方式之一来使用 Secret:
Lansonli
2022/06/21
6390
【云原生 | Kubernetes篇】Kubernetes 配置(十五)
Pod 配置管理:configMap
ConfigMap 以一个或多个 key:value 的形式保存在 kubernetes 系统中供应用使用,既可以用于表示一个变量的值,也可以用于表示一个完整配置文件的内容。
看、未来
2022/09/27
6420
k8s-kubernetes-configmap存储
ConfigMap功能在Kubernetes1.2版本中引入,许多应用程序会从配置文件、命令行参数或环境变量中读取配置信息。 ConfigMap API给我们提供了向容器中注入配置信息的机制, ConfigMap可以被用来保存单个属性,也可以用来保存整个配置文件或者JSON二进制大对象
eadela
2019/12/11
4880
kubernetes中configmap和secret
Kubernetes是一个容器编排平台,用于管理容器化应用程序的部署、扩展和管理。在Kubernetes中,ConfigMap和Secret是两个核心概念,它们用于存储应用程序的配置和敏感数据,以便应用程序可以使用它们。
堕落飞鸟
2023/04/01
4880
ConfigMap用管理对象的方式管理配置
在今天的文章中我将介绍Kubernetes中的ConfigMap对象。它的主要用途什么,为什么要用ConfigMap以及在Kubernetes里通常是如何使用ConfigMap的管理应用配置的。
KevinYan
2020/11/10
9980
K8S学习笔记之Kubernetes 配置管理 ConfigMap
很多情况下我们为某一应用做好镜像,当我们想修改其中的一些参数的时候,就变得比较麻烦,又要重新制作镜像,我们是不是有一种方式,让镜像根据不同的场景调用我们不同的配置文件呢,那我们就需要用到 k8s 的另外一种资源,那就是 ConfigMap。
Jetpropelledsnake21
2019/07/01
2K0
kubernetes系列之ConfigMap使用方式
如启用一个mysql容器,mysql容器重要的文件有两部分,一部分为存储数据文件,一部分为配置文件my.cnf,存储数据可以用持久存储实现和容器的分离解耦,配置文件也能够实现和容器的分离解耦,也就是说mysql容器能够直接读取并使用预先配置好的配置文件(而不是使用容器中默认自带的配置文件).这就是configMap的功能。
程序员同行者
2019/04/18
3.1K4
Kubernetes-配置字典ConfigMap
很多生产环境中的应用程序配置较为复杂,可能需要多个config文件、命令行参数和环境变量的组合。使用容器部署时,把配置应该从应用程序镜像中解耦出来,以保证镜像的可移植性。尽管Secret允许类似于验证信息和秘钥等信息从应用中解耦出来,但在K8S1.2前并没有为了普通的或者非secret配置而存在的对象。在K8S1.2后引入ConfigMap来处理这种类型的配置数据。
菲宇
2019/06/12
1.5K0
结合例子讲解 Kubernetes 如何实现应用配置管理
本文整理自《CNCF x Alibaba 云原生技术公开课》第 8 讲,点击“阅读原文”直达课程页面
kubernetes中文社区
2020/09/01
7110
结合例子讲解 Kubernetes 如何实现应用配置管理
Kubernetes持久化配置文件
ConfigMap了解 描述信息ConfigMap 功能在 Kubernetes1.2 版本中引入,许多应用程序会从配置文件、命令行参数或环境变量中读取配置信息。ConfigMap API 给我们提供了向容器中注入配置信息的机制,ConfigMap 可以被用来保存单个属性,也可以用来保存整个配置文件或者 JSON 二进制大对象
Yuou
2022/09/26
2800
相关推荐
Kubernetes中ConfigMap的使用
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档