七、区块链如何运用merkle tree验证交易真实性

转载自:https://www.tangshuang.net/4117.html

本文假设你已经知道区块链中merkle tree的原理,现在搞明白具体怎么来实现交易真实性验证。

Merkle Tree

这个小节简述一下merkle的原理。简单说,merkle tree就是一个hash二叉树,父节点是两个子节点的double sha256的结果,叶子节点就是交易的content的double sha256结果。

上图中最下面那一层就是交易数据,每一个交易都可以计算出一个hash,从而层层向上,得到merkle root。但是由于blockchain里面都merkle运算要求叶子节点是偶数,所以,当一个区块内包含当交易数量为奇数时,把最后一个交易复制一份,凑成偶数。

最后就是把merkle root保存在区块头中,交易数据被保存在区块体中,其实中间当那些hash并没有被保存,它们只是运算过程数据。

SPV

为什么要搞这么复杂?直接把所有交易数据保存起来了,要验证交易是否存在还不简单吗?之所以要这么干,是因为比特币发明之初,中本聪想到有一种轻钱包的设计,这就是SPV(简化支付验证,Simplified Payment Verification)。

轻钱包并不保存完整的区块链,而是只保存每一个区块的区块头。区块体保存了完整的交易信息,而交易信息需要的存储量大部分都是交易头的千倍以上。所以,如果只保存交易头,就可以极大的减少本地客户端存储的区块链信息。

但是,不能因此让区块链无法工作啊。如果这个时候轻钱包要对某一个交易进行验证,而本地又没有这个交易的信息,那怎么验证呢?这时,区块头里面的merkle root就要起作用了。

验证路径

在讲述轻钱包的验证过程之前,我们需要知道如何在merkle tree里面做验证。我们已知merkle tree里面父节点和子节点的运算关系,因此,当我们要证明一个叶子节点存在于这棵树时,只需要得到从该叶子节点到根的运算过程里面需要的那些hash即可,并不需要所有叶子节点参与计算。

你可能觉得有点奇怪,为什么不直接把所有的叶子节点告诉它就行了,你用所有叶子节点能算出root hash就验证通过了。但事实就是这样,因为每一个父节点hash一定是由两个子节点hash运算得到,所以,我们只需要挑选出所有参与运算的节点,就可以证明这个叶子节点存在于树中。这样可以减少hash运算的次数。而这些被挑选出来的节点,以及它们之间的层级关系,就是验证路径,即上图中merkle root那个盒子下面的所有盒子。

如何证明交易的真实性?

比特币网络中的交易,只有已经被记录到区块链,并且已经得到6个确认的,才被认为是真实的,只有基于这些真实交易发起的新交易(输入与输出的概念),才是合法的。

我们询问一个交易是否真实,往往基于以下前提:

  • 我们在问一个交易是否已被记录到区块链中
  • 而且这个交易所在的区块链是最长的哪一条,没有在分叉链上
  • 当每个节点接收到一条交易广播时,我们要查询作为一笔新交易的输入的真实性
  • 矿工对交易进行打包之前,对所有的输入进行真实性验证(在矿工接收到交易信息时就已经验证过了,打包的时候验证2000条交易信息不可能)

那么对于SPV轻钱包而言,怎么知道一个交易是否真实的呢?SPV拿到一个交易信息之后(比如接收到一笔钱),并不能确认这个交易是否合法,因此要对这个交易的输入进行验证。但是它只拿到了单个交易的信息,而没有本地的完整区块链数据,因此,SPV要拿着这个交易的信息向网络发起查询请求,这个请求被称为merkle block message。当其他有完整区块链数据的客户端收到这个请求之后,利用传过来的交易信息在自己的区块链数据库中进行查询,并把验证路径返回给请求源,SPV拿到验证路径之后,再做一次merkle校验,确认无误之后,就认为这个交易是可信的。

现在的问题是:

  • 怎么从区块链里面查一个交易?
  • 怎么获取merkle验证路径?
  • 怎么确保网络上这个返回的验证路径不是伪造的?

从区块链查交易

区块链的数据结构是离散的,比特币里面一个区块被保存在一个文件里面,要得到一个交易的验证路径,必须得到这个交易所在的区块链。这是一个复制的查询过程,可能需要把所有的区块都遍历一遍才能找到。因此,blockchain.info这样的网站孕育而生,帮助你通过一个信息查这个信息在区块链上的所有相关记录。但是对于客户端而言,可没那么容易,它不能信任blockchain.info这个网站,只能信任自己本地存储的区块链。所以,只能用比较合理的算法,去优化交易查询。

一种设计是,把每一个区块的数据结构修改为关系型数据库,通过关系型数据库,可以用sql语句快速查询。但是,要遍历查询所有区块链,是比较浪费的。还有一种想法是,利用交易的时间戳来快速定位区块位置,在临近的几个区块中快速找到它。

如何获取merkle验证路径?

实际上,merkle的验证路径生成的前提是已经存在一棵完整的merkle树。市面上有很多merkle树的实现包,有的包直接给出来getProof的方法来获取某个叶子节点的验证路径。

在客户端收到merkle block message之后,要执行下面的步骤:

  1. 通过上述方法找到包含该交易的区块
  2. 检查该区块是否是整个网络中最长链条里面的
  3. 取出所有交易生成merkle tree,利用getProof方法得到该交易的验证路径
  4. 将该验证路径发送回请求源

SPV得到响应之后,要做如下验证:

  1. 同步区块链,确保是整个网络中最长的一条
  2. 先拿到merkle root去区块链中查找,确保该merkle root hash是在链条中
  3. 利用拿到的验证路径,再进行一次merkle校验,确保验证路径全部合法

为什么SPV还要再做一次merkle校验呢?主要是为了确保响应方发送的验证路径的有效性。

确保验证路径的真实性

上面提到了SPV还要做一次merkle校验,这也是“不信任”的表现之一。我们并不确保响应我们的节点不会作弊或欺诈,因此,我们要自己进行校验。但是,有没有可能虽然校验过程顺利,但是实际上校验路径是伪造的呢?

我们来做一个假设:1)merkle root为真;2)交易为假;3)路径中的hash可真可假。这个假设是否成立?

我们知道,不同字符串碰撞到同一个sha256的概率极小,那么double sha256的概率就是它的平方,而merkle root是经过一层一层计算上来的,如果一个区块只有一个(或2个)交易,那么就是double^(2+1) sha256,而如果是4个交易,就有double^(4 + 2 + 1) sha256,更何况一个区块有那么多交易,要经过merkle运算得到一个相同的hash,几乎是不可能的,因此,在merkle验证中用一个伪造的交易hash来得到一个已知来merkle root是不可能的。

如果还想更进一步校验,可以在区块头中存储区块打包的交易的数量,这样就可以知道从交易hash到merkle root需要经过几层的运算。这也是一个检验点。

小结

merkle tree被广泛运用于区块链中,但并不是只有区块链使用它来进行校验。比如一些p2p下载,如迅雷,就需要把文件分割为小块文件,每块都有一个hash,每块从不同的网络节点下载,最后组成一个完整的文件,但是也需要进行hash验证,它也可以使用merkle来进行验证。merkle tree也不一定是二叉树,可以是任意树结构。而在以太坊中,merkle验证还不够用,增加了Patricia Tree验证,合起来称为“Merkle Patricia Tree”。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499841.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

idea端口占用解决记录

idea端口占用解决 每次占用,每次都要百度,路子很清晰了,记录下 报错:The port may already be in use or the connector may be misconfigured. 解决: 1.cmd 2.netstat -ano|findstr 你的端口号 举例:…

西安软件行业闲谈

尘埃落定,最终选择了西安 讲真,今年年初,就萌生了回西安的想法,毕竟北漂几年,该回了 当初还想着去上海两年,再去深圳两年,好好体验下各个一线城市的不同魅力 性格就是这样,不甘平凡…

消息队列(1):一个消息队列应该有的特点

消息队列常见场景 异步解耦 削峰填谷 日志收集分析 代替事务,最终一致 消息队列特点 消息队列之所以能异步,就是因为消息并不是实时处理的,那肯定会有一个存储消息,处理消息的地方,才能达到异步效果。 所以最简单的…

消息队列(2):常见的消息队列协议

协议很常见,只要是通信,就会用到协议,就像我们说话的语言一样,不同的语言连通着不同的人群。 所以说,消息队列也是一样,想要互相通信,就要使用同一种协议。 每个协议下的消息队列,都…

spingboot下shiro自定义过滤器roles

shiro默认的roles过滤,是与的关系,就是你的用户得有roles对应的所有角色,才算有权限。 但是实际开发过程中,更多的是或的需求,只要用户满足roles中的一个角色,就算有权限。 所以就涉及到重写过滤器roles&am…

shiro的集群动态权限更新

shiro的动态权限,一般都是将权限放在数据库中,项目启动后将权限加载给shiro 当数据库的权限数据发生改变时,重新加载权限给shiro。 单机这样做没问题,但是当项目服务是集群的时候,比如有tomcat1,tomcat2&am…

消息队列(3):RabbitMQ

RabbitMQ,基于AMQP协议实现。 特点: 消息可靠:持久化消息,传输确认,发布确认。 路由灵活:使用AMQP的Exchange来路由消息,包括RabbitMQ内置的Exchange,还有复杂的路由也可以使用多个…

java基础 --- Arrays.asList():返回指定数组支持的固定大小列表

Arrays.asList():返回指定数组支持的固定大小列表 首先看下这个方法的源码注释,注意第一句,Returns a fixed-size list backed by the specified array., 意思就是:返回指定数组支持的固定大小列表 所以:…

[设计模式] ------ 单例模式

单例模式,是众多设计模式中,写法最多,套路最多,坑最多,面试被问最多的设计模式。 基本上,单例模式在面试中已经被问烂了,依稀记得我的上家公司面试我的时候,说到设计模式&#xff0c…

分布式锁闲谈

分布式锁闲谈 前言 服务器单机情况下,要锁住某个资源,我们一般用到的是synchronized,lock等锁,这是java提供的,也确实能很有效的锁住资源。 但是在服务器集群的情况下,上面这些单机锁就不起作用了&#…

java 后端与前端Date类型与String类型互相转换(使用注解)

一:java 后端格式化输出Date类型的属性值到前端 后端返回的类型中,直接定义Date类型,加上此注解,直接将Date类型转成自定义的格式给前端 class TestDateOutput{JsonFormat(pattern "yyyy-MM-dd")private Date create…

spring boot配置对上传文件的大小限制

spring boot配置对上传文件的大小限制 spring-boot 2.0之前 Spring Boot1.4版本后配置更改为(默认单个文件最大1Mb,单次请求文件总数大小最大10Mb): spring.http.multipart.maxFileSize 20MB spring.http.multipart.maxRequestSize20MB spring-boot …

[设计模式] ------ 适配器模式

适配器把一个类的接口,强制变成另外一个类的接口实现。 个人建议,万不得已不要用适配器。但是,你一定要知道有这么个东西。 如果你的项目刚刚起步,就有人用了适配器,那基本上就是滥用。 适配器一般是项目庞大&#xff…

Notepad++中的UTF-8无BOM格式编码

Notepad中,关于utf-8的编码格式,有两种:以UTF-8无BOM格式编码和以UTF-8格式编码。 很容易给人一种错觉,第一反应会选择以UTF-8格式编码,感觉这种就是平时所说的UTF-8,然而这种编码是默认带BOM的&#xff0…

java 上传文件注意事项

java 上传文件注意事项 1、文件名有特殊字符的情况,所以最好是文件名前台url编码,后台再url解码,这点在下载的时候也一样 2、文件大小一定要设置,spring boot 有默认。 3、文件名校验: 3.1、文件后缀校验 3.2、conten…

[设计模式] ------ 装饰模式

首先,假设项目有个核心功能。 随着项目迭代,又产生功能1和功能2 于是有如下需求: 某个场景需要核心功能和功能1组合, 某个场景需要核心功能和功能2组合, 某个场景需要核心功能和功能1和功能2组合, 甚至以后…

hash和一致性hash

hash;简单的hash取余 优点: 计算简单,快速定位 缺点: 容错和扩展差,任何的增加机器或减少机器,都会伴随着重新set值 比如原来有五台机器做缓存,现在加一台,那么余5就变成余6&#x…

[记录]---mysql数据库,decimal类型设置默认值0,不要是null

mysql数据库 类型为decimal的字段,最好把默认值设置为0,不要设置为null。 因为null值在代码中及其容易造成空指针。

Java 线程状态---WAITING(部分转载)

看到一篇关于写线程waiting状态的文章,感觉很生动有趣,转过来保存下。 总结: waiting这个状态,就是等待,明确了等待,就不会抢资源了。 一个线程A在拿到锁但不满足执行条件的时候,需要另一个线…

[记录] --- linux上项目

1.安装jdk8 yum -y list java* 查看可安装java版本 yum install -y java-1.8.0-openjdk-devel.x86_64 java -version jdk在/usr/lib/jvm目录 2.配置环境变量 打开/etc/profile 在profile文件末尾加入: export JAVA_HOME/usr/lib/jvm/java-1.8.0 export PATH$JAVA_HOME/bin:…