fisco bcosV3 Table智能合约开发

news2025/1/15 10:07:47

环境 : fisco bcos 3.11.0
webase-front : 3.1.1
console 3.8.0
table合约【3.2.0版本后的】

前言

最近在做毕设,数据的存储方式考虑使用fisco-bcos的table表存储,经过这几天的研究,发现对于fisco2fisco3版本的table表合约功能差异还是比较大的,比较起来V3的table合约功能性更丰富,更加的方便开发。

读者们要是没用过v3的链子,可以在fisco3 这里简单启动一条链子,Air版本的搭建和fisco2搭建的链子命令无多大差别【文章后面也有对应的控制台搭建命令,注意控制台2和3版本的链子不互通
然后就是webase-front要使用3.0以上的版本,链接在这里https://webasedoc.readthedocs.io/zh-cn/lab/docs/WeBASE-Install/developer.html

关于fisco3 的Table合约

不知道是不是webase-front 版本的问题,我并未在其代码仓库里找到Table.sol合约的文件,只有KVTable.sol合约。然后我去github的fisco仓库找到了fisco3的版本合约文件,还附带两个合约,都需要import进去才行
[更新一下,这些合约也可以在控制台3.8.0上的contracts/solidity文件夹上找到,注意:v3版本有两个Table合约,一个是3.2.0版本以上,一个是3.2.0以前的版本,我所有介绍的是3.2.0以上的,官方文档给的是3.2.0以前的例子]

Table.sol

// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.6.10 <0.8.20;
pragma experimental ABIEncoderV2;
import "./EntryWrapper.sol";

// KeyOrder指定Key的排序规则,字典序和数字序,如果指定为数字序,key只能为数字
enum KeyOrder {Lexicographic, Numerical}
struct TableInfo {
    KeyOrder keyOrder;
    string keyColumn;
    string[] valueColumns;
}

// 更新字段,用于update
struct UpdateField {
    string columnName;
    // 考虑工具类
    string value;
}

// 筛选条件,大于、大于等于、小于、小于等于
enum ConditionOP {GT, GE, LT, LE, EQ, NE, STARTS_WITH, ENDS_WITH, CONTAINS}
struct Condition {
    ConditionOP op;
    string field;
    string value;
}

// 数量限制
struct Limit {
    uint32 offset;
    // count limit max is 500
    uint32 count;
}

// 表管理合约,是静态Precompiled,有固定的合约地址
abstract contract TableManager {
    // 创建表,传入TableInfo
    function createTable(string memory path, TableInfo memory tableInfo) public virtual returns (int32);

    // 创建KV表,传入key和value字段名
    function createKVTable(string memory tableName, string memory keyField, string memory valueField) public virtual returns (int32);

    // 只提供给Solidity合约调用时使用
    function openTable(string memory path) public view virtual returns (address);

    // 变更表字段
    // 只能新增字段,不能删除字段,新增的字段默认值为空,不能与原有字段重复
    function appendColumns(string memory path, string[] memory newColumns) public virtual returns (int32);

    // 获取表信息
    function descWithKeyOrder(string memory tableName) public view virtual returns (TableInfo memory);
}

// 表合约,是动态Precompiled,TableManager创建时指定地址
abstract contract Table {
    // 按key查询entry
    function select(string memory key) public virtual view returns (Entry memory);

    // 按条件批量查询entry,condition为空则查询所有记录
    function select(Condition[] memory conditions, Limit memory limit) public virtual view returns (Entry[] memory);

    // 按照条件查询count数据
    function count(Condition[] memory conditions) public virtual view returns (uint32);

    // 插入数据
    function insert(Entry memory entry) public virtual returns (int32);

    // 按key更新entry
    function update(string memory key, UpdateField[] memory updateFields) public virtual returns (int32);

    // 按条件批量更新entry,condition为空则更新所有记录
    function update(Condition[] memory conditions, Limit memory limit, UpdateField[] memory updateFields) public virtual returns (int32);

    // 按key删除entry
    function remove(string memory key) public virtual returns (int32);
    // 按条件批量删除entry,condition为空则删除所有记录
    function remove(Condition[] memory conditions, Limit memory limit) public virtual returns (int32);
}

abstract contract KVTable {
    function get(string memory key) public view virtual returns (bool, string memory);

    function set(string memory key, string memory value) public virtual returns (int32);
}

EntryWrapper.sol

这个类似于mybatisplus里面的wrapper条件构造器,用来进行附加查询条件使用,还有Entry用来做返回数据的数据结构

// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.6.10 <0.8.20;
pragma experimental ABIEncoderV2;
import "./Cast.sol";

// 记录,用于select和insert
struct Entry {
    string key;
    string[] fields; // 考虑2.0的Entry接口,临时Precompiled的问题,考虑加工具类接口
}

contract EntryWrapper {   
    Cast constant cast =  Cast(address(0x100f));  
    Entry entry;
    constructor(Entry memory _entry) public {
        entry = _entry;
    }
    function setEntry(Entry memory _entry) public {
        entry = _entry;
    }
    function getEntry() public view returns(Entry memory) {
        return entry;
    }
    function fieldSize() public view returns (uint256) {
        return entry.fields.length;
    }
    function getInt(uint256 idx) public view returns (int256) {
        require(idx >= 0 && idx < fieldSize(), "Index out of range!");
        return cast.stringToS256(entry.fields[idx]);
    }
    function getUInt(uint256 idx) public view returns (uint256) {
        require(idx >= 0 && idx < fieldSize(), "Index out of range!");
        return cast.stringToU256(entry.fields[idx]);
    }
    function getAddress(uint256 idx) public view returns (address) {
        require(idx >= 0 && idx < fieldSize(), "Index out of range!");
        return cast.stringToAddr(entry.fields[idx]);
    }
    function getBytes64(uint256 idx) public view returns (bytes1[64] memory) {
        require(idx >= 0 && idx < fieldSize(), "Index out of range!");
        return bytesToBytes64(bytes(entry.fields[idx]));
    }
    function getBytes32(uint256 idx) public view returns (bytes32) {
        require(idx >= 0 && idx < fieldSize(), "Index out of range!");
        return cast.stringToBytes32(entry.fields[idx]);
    }
    function getString(uint256 idx) public view returns (string memory) {
        require(idx >= 0 && idx < fieldSize(), "Index out of range!");
        return entry.fields[idx];
    }
    function set(uint256 idx, int256 value) public returns(int32) {
        require(idx >= 0 && idx < fieldSize(), "Index out of range!");
        entry.fields[idx] = cast.s256ToString(value);
        return 0;
    }
    function set(uint256 idx, uint256 value) public returns(int32) {
        require(idx >= 0 && idx < fieldSize(), "Index out of range!");
        entry.fields[idx] = cast.u256ToString(value);
        return 0;
    }
    function set(uint256 idx, string memory value) public returns(int32) {
        require(idx >= 0 && idx < fieldSize(), "Index out of range!");
        entry.fields[idx] = value;
        return 0;
    }
    function set(uint256 idx, address value) public returns(int32) {
        require(idx >= 0 && idx < fieldSize(), "Index out of range!");
        entry.fields[idx] = cast.addrToString(value);
        return 0;
    }
    function set(uint256 idx, bytes32 value) public returns(int32) {
        require(idx >= 0 && idx < fieldSize(), "Index out of range!");
        entry.fields[idx] = cast.bytes32ToString(value);
        return 0;
    }
    function set(uint256 idx, bytes1[64] memory value) public returns(int32) {
        require(idx >= 0 && idx < fieldSize(), "Index out of range!");
        entry.fields[idx] = string(bytes64ToBytes(value));
        return 0;
    }
    function setKey(string memory value) public {
        entry.key = value;
    }
    function getKey() public view returns (string memory) {
        return entry.key;
    }
    function bytes64ToBytes(bytes1[64] memory src) private pure returns(bytes memory) {
        bytes memory dst = new bytes(64);
        for(uint32 i = 0; i < 64; i++) {
            dst[i] = src[i][0];
        }
        return dst;
    }
    function bytesToBytes64(bytes memory src) private pure returns(bytes1[64] memory) {
        bytes1[64] memory dst;
        for(uint32 i = 0; i < 64; i++) {
            dst[i] = src[i];
        }
        return dst;
    }
}

Cast.sol

// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.6.10 <0.8.20;
pragma experimental ABIEncoderV2;

abstract contract Cast {
    function stringToS256(string memory) public virtual view returns (int256);
    function stringToS64(string memory) public virtual view returns (int64);
    function stringToU256(string memory) public virtual view returns (uint256);
    function stringToAddr(string memory) public virtual view returns (address);
    function stringToBytes32(string memory) public virtual view returns (bytes32);
    
    function s256ToString(int256) public virtual view returns (string memory);
    function s64ToString(int64) public virtual view returns (string memory);
    function u256ToString(uint256) public virtual view returns (string memory);
    function addrToString(address) public virtual view returns (string memory);
    function bytes32ToString(bytes32) public virtual view returns (string memory);
}

编写TableTest

这里我直接用官网的例子代码进行测试,不过官网例子与实际的table合约有出入,需要进行修改

https://fisco-bcos-doc.readthedocs.io/zh-cn/latest/docs/contract_develop/c++_contract/use_crud_precompiled.html

// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.6.10 <0.8.20;
pragma experimental ABIEncoderV2;
import "./Table.sol";


contract TestTable{

// 创建TableManager对象,其在区块链上的固定地址是0x1002
TableManager constant tm =  TableManager(address(0x1002));
Table table;
string constant TABLE_NAME = "t_test";
constructor () public{
    // 创建t_test表,表的主键名为id,其他字段名为name和age
     string[] memory columnNames = new string[](2);
     columnNames[0] = "name";
     columnNames[1] = "age";
      KeyOrder keyOrder;
      
     TableInfo memory tf = TableInfo(KeyOrder.Numerical,"id", columnNames);
    tm.createTable(TABLE_NAME, tf);
    
    // 获取真实的地址,存在合约中
    address t_address = tm.openTable(TABLE_NAME);
    require(t_address!=address(0x0),"");
    table = Table(t_address);
} 

function insert(string memory id,string memory name,string memory age) public returns (int32){
    string[] memory columns = new string[](2);
    columns[0] = name;
    columns[1] = age;
    Entry memory entry = Entry(id, columns);
    int32 result = table.insert(entry);
    // emit InsertResult(result);
    return result;
}

function update(string memory id, string memory name, string memory age) public returns (int32){
    UpdateField[] memory updateFields = new UpdateField[](2);
    updateFields[0] = UpdateField("name", name);
    updateFields[1] = UpdateField("age", age);

    int32 result = table.update(id, updateFields);

    return result;
}

function remove(string memory id) public returns(int32){
    int32 result = table.remove(id);
   
    return result;
}


function select(string memory id) public view returns (string memory,string memory)
{
    Entry memory entry = table.select(id);

    string memory name;
    string memory age;
    if(entry.fields.length==2){
        name = entry.fields[0];
        age = entry.fields[1];
    }
    return (name,age);
}


// enum ConditionOP {GT, GE, LT, LE, EQ, NE, STARTS_WITH, ENDS_WITH, CONTAINS}
// struct Condition {
//     ConditionOP op;
//     string field;
//     string value;
// }
function selectMore(string memory age)
    public
    view
    returns (Entry[] memory entries)
{
    Condition[] memory conds = new Condition[](1);
    Condition memory eq= Condition({op: ConditionOP.EQ, field: "age",value: age});
    conds[0] = eq;
    Limit memory limit = Limit({offset: 0, count: 100});
    entries = table.select(conds, limit);
    return entries;
}



}


TableInfo的问题

实际的TableInfo 结构如下

// KeyOrder指定Key的排序规则,字典序和数字序,如果指定为数字序,key只能为数字
enum KeyOrder {Lexicographic, Numerical}
struct TableInfo {
    KeyOrder keyOrder;
    string keyColumn;
    string[] valueColumns;
}

是需要三个参数的,而官网的例子只用两个参数,缺少的第一个参数是枚举类,意思是你的主键是string类型还是数字类型,需要标明。

Condition的问题

实际的Condition结构如下

// 筛选条件,大于、大于等于、小于、小于等于
enum ConditionOP {GT, GE, LT, LE, EQ, NE, STARTS_WITH, ENDS_WITH, CONTAINS}
struct Condition {
    ConditionOP op;
    string field;
    string value;
}

官网的只有两个参数,缺少的那个参数是field,也就是此条件是要用在表的哪个字段时安个。

关于主键的问题

在fisco2的table表合约开发时候,因其的设计,导致主键是可以重复的。
但在测试fisco3的table表合约开发的时候,我用重复的主键添加多个数据,发现它遵守了主键唯一的规则,有兴趣的读者可以去测试一下,特别是用TestTable的selectMore,把field改成主键字段,可以测试到返回不了多个数据

结语

这段时间会继续开发v3的table合约,在开发的时候遇到的坑和新发现会持续更新到博客上

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

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

相关文章

快速、可靠且高性价比的定制IP模式提升芯片设计公司竞争力

作者&#xff1a;Karthik Gopal&#xff0c;SmartDV Technologies亚洲区总经理 智权半导体科技&#xff08;厦门&#xff09;有限公司总经理 无论是在出货量巨大的消费电子市场&#xff0c;还是针对特定应用的细分芯片市场&#xff0c;差异化芯片设计带来的定制化需求也在芯片…

基础入门-抓包技术HTTPS协议APP小程序PC应用Web证书信任转发联动

知识点&#xff1a; 1、抓包技术-Web应用-http/s-Burp&Yakit 2、抓包技术-APP应用-http/s-Burp&Yakit 3、抓包技术-PC端应用-http/s-Burp&Yakit 4、抓包技术-WX小程序-http/s-Burp&Yakit 5、抓包技术-软件联动-http/s-Proxifier 6、抓包技术-通用方案-http/s-R…

贪心算法详细讲解(沉淀中)

文章目录 1. 什么是贪心算法&#xff1f;&#xff08;贪婪鼠目寸光&#xff09;经典例题1.1.1 找零问题1.1.2最小路径和1.1.3 背包问题 2.贪心算法的特点2.1 证明例1 3.学习贪心的方向心得体会 1. 什么是贪心算法&#xff1f;&#xff08;贪婪鼠目寸光&#xff09; 贪心策略&a…

SQL面试题2:留存率问题

引言 场景介绍&#xff1a; 在互联网产品运营中&#xff0c;用户注册量和留存率是衡量产品吸引力和用户粘性的关键指标&#xff0c;直接影响产品的可持续发展和商业价值。通过分析这些数据&#xff0c;企业可以了解用户行为&#xff0c;优化产品策略&#xff0c;提升用户体验…

学会使用开源软件jclasslib 字节码文件的组成 详解

应用场景 1 应用场景 2 学习路线 以正确的姿势打开文件 字节码文件的组成 玩转字节码常用工具 以正确的姿势打开文件 开源软件 jclasslib github 地址 https://github.com/ingokegel/jclasslib 工具使用 字节码文件的组成 基本信息 常量池 字段 方法 属性 详解 魔数 主副版…

primitive 的 Appearance编写着色器材质

import { nextTick, onMounted, ref } from vue import * as Cesium from cesium import gsap from gsaponMounted(() > { ... })// 1、创建矩形几何体&#xff0c;Cesium.RectangleGeometry&#xff1a;几何体&#xff0c;Rectangle&#xff1a;矩形 let rectGeometry new…

详情页 路由传值

路由传值获取参数 渲染数据 主页面 <template><div class"km"><div v-for"item in items" :key"item.id"><div class"title-km" ><img :src"item.imageUrl" alt"Image" class"…

OpenCV基础:矩阵的创建、检索与赋值

本文主要是介绍如何使用numpy进行矩阵的创建&#xff0c;以及从矩阵中读取数据&#xff0c;修改矩阵数据。 创建矩阵 import numpy as npa np.array([1,2,3]) b np.array([[1,2,3],[4,5,6]]) #print(a) #print(b)# 创建全0数组 eros矩阵 c np.zeros((8,8), np.uint8) #prin…

解锁未来情感科技:AI 机器人 Ropet 搭载的前沿智能黑科技

2025年的国际消费电子产品展览会&#xff08;CES&#xff09;上&#xff0c;一只可爱的“毛绒玩具”成了全场焦点。 当然&#xff0c;这并不是一个单纯的玩偶&#xff0c;而是和《超能陆战队》的大白一样温暖的陪伴机器人。 相信有很多人和小编一样&#xff0c;当年看完《超能…

软件测试 —— Selenium常用函数

软件测试 —— Selenium常用函数 操作测试对象点击/提交对象 click()模拟按键输入 send_keys("")清除文本内容 clear() 模拟用户键盘行为 Keys包示例用法 获取文本信息 textget_attribute("属性名称") 获取当前页面标题 title获取当前页面的 url current_u…

【WEB】网络传输中的信息安全 - 加密、签名、数字证书与HTTPS

文章目录 1. 概述2. 网络传输安全2.1.什么是中间人攻击2.2. 加密和签名2.2.1.加密算法2.2.2.摘要2.2.3.签名 2.3.数字证书2.3.1.证书的使用2.3.2.根证书2.3.3.证书链 2.4.HTTPS 1. 概述 本篇主要是讲解讲一些安全相关的基本知识&#xff08;如加密、签名、证书等&#xff09;&…

服务器数据恢复—EMC存储POOL中数据卷被删除的数据恢复案例

服务器数据恢复环境&故障&#xff1a; EMC Unity 400存储连接了2台硬盘柜。2台硬盘柜上一共有21块硬盘&#xff08;520字节&#xff09;。21块盘组建了2组RAID6&#xff1a;一组有11块硬盘&#xff0c;一组有10块硬盘。 在存储运行过程中&#xff0c;管理员误操作删除了 2组…

python 轮廓 获取环形区域

目录 效果图&#xff1a; 代码&#xff1a; 效果图&#xff1a; 代码&#xff1a; import cv2 import numpy as np# 读取图像 image cv2.imread(rE:\project\jijia\tools_jijia\img_tools\ground_mask.jpg, cv2.IMREAD_GRAYSCALE) # 二值化图像 # 二值化图像 _, binary cv…

使用 WPF 和 C# 将纹理应用于三角形

此示例展示了如何将纹理应用于三角形,以使场景比覆盖纯色的场景更逼真。以下是为三角形添加纹理的基本步骤。 创建一个MeshGeometry3D对象。像往常一样定义三角形的点和法线。通过向网格的TextureCoordinates集合添加值来设置三角形的纹理坐标。创建一个使用想要显示的纹理的 …

算法妙妙屋-------2..回溯的奇妙律动

回溯算法是一种用于系统性地搜索和解决问题的算法&#xff0c;它以深度优先搜索&#xff08;DFS&#xff09;为基础&#xff0c;用来探索所有可能的解决方案。通过递归地尝试候选解并在必要时回退&#xff08;即“回溯”&#xff09;&#xff0c;它能够高效地解决许多涉及组合、…

如何在Jupyter中快速切换Anaconda里不同的虚拟环境

目录 介绍 操作步骤 1. 选择环境&#xff0c;安装内核 2. 注册内核 3. 完工。 视频教程 介绍 很多网友在使用Jupyter的时候会遇到各种各样的问题&#xff0c;其中一个比较麻烦的问题就是我在Anaconda有多个Python的环境里面&#xff0c;如何让jupyter快速切换不同的Pyt…

《自动驾驶与机器人中的SLAM技术》ch9:自动驾驶车辆的离线地图构建

目录 1 点云建图的流程 2 前端实现 2.1 前端流程 2.2 前端结果 3 后端位姿图优化与异常值剔除 3.1 两阶段优化流程 3.2 优化结果 ① 第一阶段优化结果 ② 第二阶段优化结果 4 回环检测 4.1 回环检测流程 ① 遍历第一阶段优化轨迹中的关键帧。 ② 并发计算候选回环对…

2025/1/12 复习JS

我乞求你别再虚度光阴 ▶ 空心 --------------------------------------------------------------------------------------------------------------------------------- 摘自哔哩哔哩听课笔记。 01 上篇&#xff1a;核心语法 1.基于页面效果的操作 <!DOCTYPE html>…

初学stm32 --- II2C_AT24C02,向EEPROM中读写数据

目录 IIC总线协议介绍 IIC总线结构图 IIC协议时序 1. ACK&#xff08;Acknowledge&#xff09; 2. NACK&#xff08;Not Acknowledge&#xff09; IO口模拟II2C协议 发送起始信号&#xff1a; 发送停止信号&#xff1a; 检测应答信号&#xff1a; 发送应答信号&#x…

Angular-生命周期及钩子函数

什么是生命周期 Angular 创建和渲染组件及其子组件&#xff0c;当它们绑定的属性发生变化时检查它们&#xff0c;并在从 DOM 中移除它之前销毁它们。生命周期函数通俗的讲就是组件创建、组件更新、组件销毁的时候会触发的一系列的方法。当 Angular 使用构造函数新建一个组件或…