Solidity合约中Merkle Root验证的一点实践

2023-11-17

背景

在上一篇文章"Solidity合约中签名验证的一点实践"中提到过,白名单机制一般有两种,除了签名验证的方式外,就是本文讲述的Merkle Root验证的方式。
主要做法是在服务端对白名单地址列表整体构建Merkle树,计算出树的root hash,合约只需存储这个Merkle的根哈希值就可以了。由于Merkle tree的构建,不需要任何私钥,所以安全性有很大提升,目前大多数新项目都会采用这个方法。

整体交互流程和签名验证比较相似,大致为:

  1. 后端预先收集所有的白名单地址,构建出完整的Merkle树
  2. 用户在前端网页操作发起pre mint时,弹出信息提示用户对该请求进行签名
    请求发到后端,后端校验签名后,查询地址是否在白名单列表中。
  3. 如果确实存在白名单中,则从Merkle树查询该地址对应的Merkle proof列表,并返回给前端
  4. 前端调用钱包,把后端返回的proof列表作为参数传给合约pre mint方法
  5. 合约通过该地址和proof列表,计算出root hash,与合约中保存的root hash做比较,相同则通过校验,并且保存该地址到合约中,避免用户重复发起。

Merkle Tree

在这里插入图片描述
Merkle树在区块链中应用非常广泛,比如在比特币中就是用交易作为了叶子节点的数据节点,使得可以快速验证某一笔交易是否在区块中是否存在。概念参考: Merkle Tree与区块链
而在白名单场景中,叶子节点的数据节点就是一个一个的地址。

合约

同样,在第三方库OpenZeppelin中,已经实现(或者说定义)了根哈希验证的方法,用户的自定义合约里只有引入MerkleProof这个library即可。验证的源代码如下:

function verify(
    bytes32[] memory proof,
    bytes32 root,
    bytes32 leaf
) internal pure returns (bool) {
    bytes32 computedHash = leaf;

    for (uint256 i = 0; i < proof.length; i++) {
        bytes32 proofElement = proof[i];

        if (computedHash <= proofElement) {
            // Hash(current computed hash + current element of the proof)
            computedHash = keccak256(abi.encodePacked(computedHash, proofElement));
        } else {
            // Hash(current element of the proof + current computed hash)
            computedHash = keccak256(abi.encodePacked(proofElement, computedHash));
        }
    }

    // Check if the computed hash (root) is equal to the provided root
    return computedHash == root;
}

在这里我们可以看到传参分别是proof的byte32数组,root的hash值,以及leaf(实际就是用户地址)。这个方法中,通过leaf和对应Merkle节点的hash值,循环计算出根root哈希值,如果与传参root相同则验证通过。
其中需要注意的是

  1. 每次计算下一个hash时,都需要先比较computedHash与proofElement的大小,较小的在拼接时放在前面。由于两者都是bytes32的数据类型,而solidity中byte是无符号的,所以在服务端务必需要用相同规则生成这个merkle树,这样才能满足合约的校验方法。
  2. 如果节点总数为奇数,那么最后一个节点,需要和自身拼接后进行hash

使用该library的合约示例代码如下,rootHash则为预先保存在合约里的根哈希值:

using MerkleProof for bytes32[];
bytes32 public rootHash;

function merkleVerify(bytes32[] memory proof) public view returns(bool){
    return proof.verify(rootHash,bytes32(uint256(uint160(msg.sender))));
} 

后端

根据语言的不同,后端工作量差别较大。
如果是nodejs,则有OpenZeppelin推荐的merkletreejs库,直接使用即可,操作十分简单。可参考OpenZeppelin官方测试用例

如果是其他语言,则需要寻找是否有现成的第三方库,目前构建Merkle树的库很多,但是加入了节点间排序的比较少,而且构造的proof列表往往不符合solidity验证的需求。因此本文模仿merkletreejs的核心逻辑,结合web3j库,用java实现了初始化构建、生成proof列表,验证proof 这三个功能:

import org.web3j.crypto.Hash;
import org.web3j.utils.Numeric;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class MerkleTree {
   
    private final List<byte[]> leaves;
    private final List<List<byte[]>> layers;

    public MerkleTree(List<String> leafList) {
   

        this.leaves = leafList.stream().map(key -> Hash.sha3(key.getBytes())).collect(Collectors.toList());
        this.layers = new ArrayList<>();
        processLeaves(this.leaves);
    }

    private byte[] getRoot() {
   
        if(this.layers.size() == 0){
   
            return new byte[]{
   };
        }
        return this.layers.get(this.layers.size()-1).get(0);
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Solidity合约中Merkle Root验证的一点实践 的相关文章

  • 微信小程序官方组件展示之画布canvas源码

    以下将展示微信小程序之画布canvas源码官方组件能力 组件样式仅供参考 开发者可根据自身需求定义组件样式 具体属性参数详见小程序开发文档 功能描述 画布 2 9 0 起支持一套新 Canvas 2D 接口 需指定 type 属性 同时支持
  • 数字IC设计——跨时钟域篇3(单比特处理)

    数字IC设计 跨时钟域篇3 单比特处理 下面介绍常见的单比特跨时钟域的处理方法 一 慢时钟域信号同步到快时钟域的处理方法 两级寄存器同步 慢时钟信号进入到更快的时钟域时 频率相差2倍以上 此时不用考虑快时钟域信号采样丢失问题 可以考虑使用两

随机推荐

  • java 线性表---------双向链表(源代码)

    1 public class DuLinkList
  • 【python数据挖掘课程】十二.Pandas、Matplotlib结合SQL语句对比图分析

    这篇文章主要讲述Python常用数据分析包Numpy Pandas Matplotlib结合MySQL分析数据 前一篇文章 python数据挖掘课程 十一 Pandas Matplotlib结合SQL语句可视化分析 讲述了MySQL绘图分析
  • msys2 修改国内源加速pacman

    1 msys2 修改国内源加速pacman 清华大学 etc pacman d mirrorlist mingw32 Server https mirrors tuna tsinghua edu cn msys2 mingw i686 1
  • netty源码分析之LengthFieldBasedFrameDecoder

    http www jianshu com p a0a51fd79f62 hmsr toutiao io utm medium toutiao io utm source toutiao io 拆包的原理 关于拆包原理的上一篇博文 netty
  • 五一节假期结束给团队开会,快速进入工作状态

    大家好 五一的假期大家过的都还开心吧 五一长假已经结束了 更开心的事情马上又要来了 再坚持10天 又要发工资了 再坚持上3天班又可以缓冲一下休息一天了 打工人 上班快乐 伴随着这些开心在这里我请大家尽快从自由松散的假期状态中走出来 重整旗鼓
  • Mac VS Code 如何去除右边的预览功能

    取消选中Minimap即可
  • Scala中的高阶函数

    1 定义 当一个函数 func1 中的 参数列表 传入的是函数 那么函数func1 就是高阶函数 上边几个函数可以 提炼出为一个高阶函数 因为 他们只是 黄线标志的部分不同 我们可以 定义一个 函数作为作为参数传递进去 来提取为 一般规律
  • opencv+nvcodec实现视频硬解码

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 系统配置 前言 一 NVCODEC是什么 二 OpenCV编译 1 安装Driver CUDA 2 编译OpenCV 总结 系统配置 操作系统 Ubuntu18 0
  • SpringMVC的拦截器

    SpringMVC的拦截器 SpringMVC的拦截器 SpringMVC的拦截器 01 SpringMVC拦截器 拦截器的作用 理解 02 SpringMVC拦截器 interceptor和filter区别 理解 记忆 03 Spring
  • style-components的熟练运用

    安装 首先下一个包 npm install save styled components 使用 创建组价以及根据属性加样式 import React Component from react import styled from style
  • driver.get_screenshot_as_file()没有保存图片的原因

    部分代码如下 cur time time strftime Y m d H M S filename os path dirname os path abspath screenshot cur time png driver get sc
  • JavaScript:实现简易计算功能

    JavaScript 实现简易计算功能 body部分
  • 数据结构:手撕图解单链表---phead的多种传参方式对比和辅助理解

    文章目录 为什么要引入链表 单链表 单链表的定义和原理 单链表的头插 对于指针的深层次理解 链表的尾插 封装malloc函数 尾删 头删 查找 链表中元素的插入 在某节点前插入 在某节点后插入 链表中元素的删除 删除pos位置的值 删除po
  • umi学习总结

    文章目录 umi介绍 umi是什么 umi的特性 开发环境 Node js 依赖管理工具 目录结构 路由 配置路由 页面跳转 Link组件 路由组件参数 路由动态参数 query信息 样式 使用css样式 dva 为什么需要状态管理 umi
  • Qt弹出窗口

    Qt弹出Widget窗口置顶 1 需求 Widget每次都弹出且为非模态窗口 2 老版代码 if widget NULL widget new QWidget widget gt show 想象 弹出窗口后 如果发生窗口切换 再次点击时 弹
  • Go语言常用的标准库

    文章目录 打印日志 系统调用命令 json的序列化和反序列化 base64 压缩和解压 标准输入 文件操作 目录操作 init函数 包的可见性 数学库 生成随机数 时间函数 打印日志 package main import log os f
  • Java内存回收机制

    C C 等语言中 内存的分配和释放由程序代码来完成 容易出现由于程序员漏写内存释放代码引起的内存泄露 最终导致系统内存耗尽 Java代码运行在JVM中 由JVM来管理 堆Heap 内存的分配和回收 Garbage Collection 把程
  • 接口如何处理重复请求?

    本文主要来源于 处理重复请求的三种方式 服务端如何高效的处理重复请求 对其整理和总结 用于学习记录 重复请求常用的处理方式就是幂等性处理 幂等性可以理解为 无论执行了多少次重复请求 数据只会处理一次 在数据库里也只会有一条数据 和数据库的唯
  • 以太坊智能合约各方法对应的签名编码

    erc20智能合约常见方法对应的签名编码 常见例如交易 transfer address uint256 编码为 web3 sha3 transfer address uint256 substring 0 10 gt 0xa9059cbb
  • Solidity合约中Merkle Root验证的一点实践

    背景 在上一篇文章 Solidity合约中签名验证的一点实践 中提到过 白名单机制一般有两种 除了签名验证的方式外 就是本文讲述的Merkle Root验证的方式 主要做法是在服务端对白名单地址列表整体构建Merkle树 计算出树的root