基于Fisco-Bcos的区块链智能合约-简单案例实践
一、智能合约介绍
智能合约是指把合同/协议条款以代码的形式电子化地放到区块链网络上。FISCO BCOS平台支持两种智能合约类型:Solidity智能合约与预编译智能合约
Solidity与Java类似。代码写好后,都需要通过编译器将代码转换成二进制,在Java中,编译器是Javac,而对于Solidity,是solc。生成后的二进制代码,会放到虚拟机里执行。Java代码在Java虚拟机(JVM)中执行,在Solidity中,是一个区块链上的虚拟机EVM。目的,是给区块链提供一套统一的逻辑,让相同的代码跑在区块链的每个节点上,借助共识算法,让区块链的数据以统一的方式进行改变,达到全局一致的结果
设计目的:
为区块链提供一套统一的逻辑,让相同的代码跑在区块链的每个节点上,借助共识算法,让区块链的数据以统一的方式进行改变,达到全局一致的结果
Solidity 局限与改进
- Solidity不够灵活
受自身堆栈深度的限制,函数传参和局部参数的个数总和不能超过16个,Solidity是一种强类型的语言,但其类型转换较为麻烦
- 性能差
底层存储单位是32字节(256 bits),对硬盘的读写要求较高,浪费了大量的存储资源
针对上述两点,FISCO BCOS提供了一种用C++写合约方式:预编译合约。开发者可以用C++编写智能合约逻辑,并将其内置在节点中,
预编译合约突破了Solidity语言的限制,借助强大的C++语言,可以灵活的实现各种逻辑,灵活性大大提高。同时,C++的性能优势也得到了很好的利用,通过预编译合约编写的逻辑,相比于Solidity语言来说,性能得到提升
合约编写
开发工具:remix-ide的使用,开发编译过程选择在线remix
Remix是功能强大的开源工具,可帮助您直接从浏览器编写Solidity合同。Remix用JavaScript编写,支持在浏览器和本地使用。
Remix还支持智能合约的测试,调试和部署等等。
优点:
1. 动态编译、可调控编译版本
2. 即时错误提醒
3. 代码自动补全
4. 发布阶段,代码问题提醒
5. 对设计方法的简单调用
认识合约
例:
pragma solidity ^ 0.4.26; constant Sample{
//变量 address表示账户地址
address private _admin;
uint private _state; //修饰符 ,为函数提供一些额外的功能,例如检查、清理等工作
// 检测函数的调用者是否为函数部署时设定的那个管理员(即合约的部署人)
modifier onlyAdmin(){
require(msg.sender==_admin,"You are not admin");
_; }
//事件
// 记录事件定义的参数,存储到区块链交易的日志中,提供廉价的存储。
// 提供一种回调机制,在事件执行成功后,由节点向注册监听的SDK发送回调通知,触发回调函数被执行。
// 提供一个过滤器,支持参数的检索和过滤。
event SetState(unit valule); //构造方法 构造函数用于初始化合约
constructor() public {
_admin=msg.sender;
} //函数 方法
function setSate(unit value) public onlyAdmin(){
_state=value;
emit SetState(value);
}
function getValue() public view return (uint){
return _state;
}
}
二、案列合约设计
逻辑如下:
定义:
- 定义事件方法AddEqu(string equnum, string data)
- 构造函数中创建t_equipment表
- 查询方法:select(string equnum),根据设备编号查询设备备案信息,或使用记录。( 成功返回0, 设备不存在返回-1)
- addEqu(string equnum, string data),添加数据前校验数据唯一性,已存在不在插入
Eqump合约类图
Contract:Java与智能合约进行交互的实体合约类型抽象
ManagedTransaction: 交易管理
Eqump合约核心代码
Eqump.sol
pragma solidity ^ 0.4.25; import "./Table.sol";
contract Eqump{
// event
event AddEqu(string equnum, string data);
//
constructor() public {
// 构造函数中创建t_equipment表
createTable();
} function createTable() private {
TableFactory tf = TableFactory(0x1001); // 创建表
tf.createTable("t_equipment", "equnum", "data");
} function openTable() private view returns(Table) {
TableFactory tf = TableFactory(0x1001);
Table table = tf.openTable("t_equipment");
return table;
} /*
描述 : 根据设备管理信息查询设备信息
参数 :
equ_num : 设备编号
返回值:
参数一: 成功返回0, 设备不存在返回-1
*/
function select(string equnum) public view returns(int256, string) {
// 打开表
Table table = openTable();
// 查询
Entries entries = table.select(equnum, table.newCondition());
if (0 == uint256(entries.size())) {
return (-1, "");
} else {
Entry entry = entries.get(0);
return (0, entry.getString("data"));
}
}
/*
描述 : 添加信息
参数 :
equnum : 案信息主键
data : 信息
返回值:
0 备案成功
-1 备案信息已存在
-2 其他错误
*/
function addEqu(string equnum, string data) public returns(int256){
int256 ret_code = 0;
Table table = openTable();
Entries entries = table.select(equnum, table.newCondition());
if(0 == uint256(entries.size())) {
Entry entry = table.newEntry();
entry.set("equnum", equnum);
entry.set("data", data);
// 插入
int count = table.insert(equnum, entry);
if (count == 1) {
// 成功
ret_code = 0;
} else {
// 失败? 无权限或者其他错误
ret_code = -2;
}
} else {
// 备案信息
ret_code = -1;
} emit AddEqu(equnum, data);
return ret_code;
} }
pragma solidity ^0.4.24; contract TableFactory {
function openTable(string) public constant returns (Table); // 打开表
function createTable(string,string,string) public returns(int); // 创建表
} // 查询条件
contract Condition {
//等于
function EQ(string, int) public;
function EQ(string, string) public; //不等于
function NE(string, int) public;
function NE(string, string) public; //大于
function GT(string, int) public;
//大于或等于
function GE(string, int) public; //小于
function LT(string, int) public;
//小于或等于
function LE(string, int) public; //限制返回记录条数
function limit(int) public;
function limit(int, int) public;
} // 单条数据记录
contract Entry {
function getInt(string) public constant returns(int);
function getAddress(string) public constant returns(address);
function getBytes64(string) public constant returns(byte[64]);
function getBytes32(string) public constant returns(bytes32);
function getString(string) public constant returns(string); function set(string, int) public;
function set(string, string) public;
function set(string, address) public;
} // 数据记录集
contract Entries {
function get(int) public constant returns(Entry);
function size() public constant returns(int);
} // Table主类
contract Table {
// 查询接口
function select(string, Condition) public constant returns(Entries);
// 插入接口
function insert(string, Entry) public returns(int);
// 更新接口
function update(string, Entry, Condition) public returns(int);
// 删除接口
function remove(string, Condition) public returns(int); function newEntry() public constant returns(Entry);
function newCondition() public constant returns(Condition);
}
编译发布
WeBASE简介:
WeBASE(WeBank Blockchain Application Software Extension) 是在区块链应用和FISCO-BCOS节点之间搭建的一套通用组件。围绕交易、合约、密钥管理,数据,可视化管理来设计各个模块,开发者可以根据业务所需,选择子系统进行部署。WeBASE屏蔽了区块链底层的复杂度,降低开发者的门槛,大幅提高区块链应用的开发效率,包含节点前置、节点管理、交易链路,数据导出,Web管理平台等子系统。
过程
- 编译发布
- 测试验证 发交易-->addEqu
{
equnum : y1
data:y1
}
发交易-->select
{
equnum:y1
}
基于web3sdk 调试Eqump
1. 在IDE⾥编写智能合约。
2. 合约编写完成后,拿到fisco ckient 命令⾏⼯具内进⾏编译和⽣成java SDK的操作。 在/home/FISCO-BCOS/generator/console⽬录下执⾏⼀下命令,将合约解析成java SDK⽂件。
sh sol2java.sh com.wg.service
特点
- - 轻量化配置,即可连接区块链节点
- - 根据.sol 合约文件,一键生成.abi 和 .bin文件
- - 一键生成java 合约文件
基于springboot-demo项目
applycation.yml
encrypt-type: # 0:普通, 1:国密
encrypt-type: 0 group-channel-connections-config:
caCert: ca.crt
sslCert: sdk.crt
sslKey: sdk.key
all-channel-connections:
- group-id: 1 #group ID
connections-str:
# 若节点小于v2.3.0版本,查看配置项listen_ip:channel_listen_port
- 127.0.0.1:20200 # node channel_listen_ip:channel_listen_port
- 127.0.0.1:20201
- group-id: 2
connections-str:
# 若节点小于v2.3.0版本,查看配置项listen_ip:channel_listen_port
- 127.0.0.1:20202 # node channel_listen_ip:channel_listen_port
- 127.0.0.1:20203
channel-service:
group-id: 1 # sdk实际连接的群组
agency-name: fisco # 机构名称
SSL连接配置
国密区块链和非国密区块链环境下,节点与SDK之间均可以建立SSL的连接,将节点所在目录nodes/${ip}/sdk/目录下的ca.crt、sdk.crt和sdk.key文件拷贝到项目的资源目录。(低于2.1版本的FISCO BCOS节点目录下只有node.crt和node.key,需将其重命名为sdk.crt和sdk.key以兼容最新的SDK)
启动
无异常,看到区块链的版本、java环境地址、端口为正常启动。
2020-07-17 09:13:21,417 [nioEventLoopGroup-2-1] INFO [org.fisco.bcos.channel.handler.ConnectionCallback] ConnectionCallback.java:267 - support channel handshake node: Version [buildTime=20200602 03:35:56, buildType=Linux/clang/Release, chainID=1, version=2.4.1, gitBranch=HEAD, gitCommit=f6f2b4f12d5441e24c81a7c862691636c9cb3263, supportedVersion=2.4.1], content: {"id":0,"jsonrpc":"2.0","result":{"Build Time":"20200602 03:35:56","Build Type":"Linux/clang/Release","Chain Id":"1","FISCO-BCOS Version":"2.4.1","Git Branch":"HEAD","Git Commit Hash":"f6f2b4f12d5441e24c81a7c862691636c9cb3263","Supported Version":"2.4.1"}} 2020-07-17 09:13:21,422 [nioEventLoopGroup-2-1] INFO [org.fisco.bcos.channel.handler.ConnectionCallback] ConnectionCallback.java:167 - channel protocol handshake success, set socket channel protocol, host: 10.2.23.16:20200, channel protocol: ChannelProtocol [protocol=3, nodeVersion=2.4.1, EnumProtocol=VERSION_3]
2020-07-17 09:13:21,424 [restartedMain] INFO [org.fisco.bcos.channel.client.Service] Service.java:373 - Connect to nodes: [10.2.23.16:20200] ,groupId: 1 ,caCert: class path resource [ca.crt] ,sslKey: class path resource [sdk.key] ,sslCert: class path resource [sdk.crt] ,java version: 1.8.0_151 ,java vendor: Oracle Corporation
2020-07-17 09:13:21,432 [nioEventLoopGroup-2-1] INFO [org.fisco.bcos.channel.handler.ConnectionCallback] ConnectionCallback.java:338 - send update topic message request, seq: 89300763da2a4279bcb49b4b8187e477, content: ["_block_notify_1"]
2020-07-17 09:13:21,434 [nioEventLoopGroup-2-1] INFO [org.fisco.bcos.channel.handler.ConnectionCallback] ConnectionCallback.java:370 - query block number host: 10.2.23.16:20200, seq: 0db7f13819ec425c8d9494cb68cd98cd, content: {"jsonrpc":"2.0","method":"getBlockNumber","params":[1],"id":1}
2020-07-17 09:13:21,440 [nioEventLoopGroup-2-1] INFO [org.fisco.bcos.channel.handler.ConnectionCallback] ConnectionCallback.java:395 - query blocknumer, host:10.2.23.16:20200, blockNumber: 336
验证
编写单元测试
核心代码
package org.fisco.bcos; import org.fisco.bcos.constants.GasConstants;
import org.fisco.bcos.solidity.Eqump;
import org.fisco.bcos.web3j.crypto.Credentials;
import org.fisco.bcos.web3j.protocol.Web3j;
import org.fisco.bcos.web3j.tuples.generated.Tuple2;
import org.fisco.bcos.web3j.tx.gas.StaticGasProvider;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import java.math.BigInteger; import static org.junit.Assert.assertTrue; /**
* 财政部大型设备合约单元测试
*/
public class EqumpTest extends BaseTest { @Autowired
private Web3j web3j;
@Autowired
private Credentials credentials; /**
* 部署调用合约
* @throws Exception
*/
@Test
public void deployAndCall() throws Exception {
// deploy contract
Eqump eqump =
Eqump.deploy(web3j, credentials, new StaticGasProvider(GasConstants.GAS_PRICE, GasConstants.GAS_LIMIT)).send();
if (eqump != null) {
System.out.println("Eqump address is: " + eqump.getContractAddress());
// call set function
eqump.addEqu("1A2B","12312").send();
// call get function
Tuple2<BigInteger, String> send = eqump.select("1A2B").send();
System.out.println(send.getValue1());
System.out.println(send.getValue2());
assertTrue("Eqump!".equals(send));
}
} /**
* 查询
* @throws Exception
*/
@Test
public void queryAndCall() throws Exception {
// deploy contract
Eqump eqump =
Eqump.deploy(web3j, credentials, new StaticGasProvider(GasConstants.GAS_PRICE, GasConstants.GAS_LIMIT)).send();
if (eqump != null) {
System.out.println("Eqump address is: " + eqump.getContractAddress());
// call set function
// call get function
Tuple2<BigInteger, String> send = eqump.select("y6").send();
System.out.println(send.getValue1());
System.out.println(send.getValue2());
}
}
}
核心业务代码
/**
* 添加设备信息
*
* @param dataArray
* @throws InterruptedException
*/
private void addIpassItem(JSONArray dataArray) {
System.out.println("===========================addIpassItem 添加设备信息业务开始================================");
try {
Eqump eqump =
Eqump.deploy(web3j, credentials, new StaticGasProvider(GasConstants.GAS_PRICE, GasConstants.GAS_LIMIT)).send();
for (int i = 0; i < dataArray.size(); i++) {
List list = (List) dataArray.getJSONObject(i).get("equipmentInfor");
long startime = System.currentTimeMillis();
for (int j = 0; j < list.size(); j++) {
JSONObject jobj = (JSONObject) list.get(j);
String sbbh = StringUtil.validator(jobj.get("设备编号"));
String jsonStr = StringUtil.validator(jobj);
if (eqump != null) {
System.out.println("Eqump address is: " + eqump.getContractAddress());
eqump.addEqu(sbbh,jsonStr).send();
}
}
System.out.println("耗时:" + (System.currentTimeMillis() - startime) + " 毫秒");
}
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("===========================addIpassItem 添加设备信息业务结束================================");
} /**
* 添加设备使用信息
*
* @param dataArray
* @throws InterruptedException
*/
private void addIpassUse(JSONArray dataArray) {
System.out.println("===========================addIpassUse 添加设备信息业务开始================================");
try {
for (int i = 0; i < dataArray.size(); i++) {
List list = (List) dataArray.getJSONObject(i).get("equipmentUsageRec");
long startime = System.currentTimeMillis();
for (int j = 0; j < list.size(); j++) {
JSONObject jobj = (JSONObject) list.get(j);
String sbbh = StringUtil.validator(jobj.get("设备编号"));
String jsonStr = StringUtil.validator(jobj);
Eqump eqump =
Eqump.deploy(web3j, credentials, new StaticGasProvider(GasConstants.GAS_PRICE, GasConstants.GAS_LIMIT)).send();
if (eqump != null) {
System.out.println("Eqump address is: " + eqump.getContractAddress());
eqump.addEqu(sbbh,jsonStr).send();
}
}
System.out.println("耗时:" + (System.currentTimeMillis() - startime) + " 毫秒");
}
} catch (NumberFormatException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("===========================addIpassUse 添加设备信息业务结束================================");
}
日志信息
===========================addIpassItem 添加设备信息业务开始================================
2020-07-23 17:20:57,261 [http-nio-8080-exec-1] INFO [org.fisco.bcos.web3j.utils.Async] Async.java:19 - default set setExeutor , pool size is 50
2020-07-23 17:20:57,262 [http-nio-8080-exec-1] INFO [org.fisco.bcos.web3j.utils.Async] Async.java:81 - set setExeutor because executor null, executor is java.util.concurrent.ThreadPoolExecutor@3ac27c97[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
2020-07-23 17:20:57,458 [nioEventLoopGroup-2-1] INFO [org.fisco.bcos.channel.client.Service] Service.java:1388 - Receive block notify: {"blockNumber":353,"groupID":1} Eqump address is: 0x0066699656ac8bc09ec364858680f2357f899ae0
2020-07-23 17:21:01,012 [nioEventLoopGroup-2-1] INFO [org.fisco.bcos.channel.client.Service] Service.java:1388 - Receive block notify: {"blockNumber":354,"groupID":1} Eqump address is: 0x0066699656ac8bc09ec364858680f2357f899ae0
2020-07-23 17:21:02,807 [nioEventLoopGroup-2-1] INFO [org.fisco.bcos.channel.client.Service] Service.java:1388 - Receive block notify: {"blockNumber":355,"groupID":1} Eqump address is: 0x0066699656ac8bc09ec364858680f2357f899ae0
2020-07-23 17:21:04,341 [nioEventLoopGroup-2-1] INFO [org.fisco.bcos.channel.client.Service] Service.java:1388 - Receive block notify: {"blockNumber":356,"groupID":1} 耗时:6593 毫秒
===========================addIpassItem 添加设备信息业务结束================================
webase验证
{"序号":"3","仪器名称":"600MHz超导核磁共振仪","仪器型号":"Avance III 600","设备编号":"Avance III 600","所属单位":"昆明植物研究所 ","所属区域中心":"昆明生物多样性大型仪器区域中心","制造商名称":"瑞士布鲁克公司 ","国别":"瑞士","购置时间":"20100109","放置地点":"分析测试中心101","预约审核人":"李波 ","操作人员":"李波 ","仪器工作状态":"正常 ","预约形式":"必须预约 ","预约类型":"项目预约","仪器大类":"室内分析测试设备","仪器中类":"波谱仪器 ","仪器小类":"核磁共振波谱仪器"}
资料及参考
基于Fisco-Bcos的区块链智能合约-简单案例实践的更多相关文章
- Spring Boot 整合 Fisco Bcos(区块链)
简介 FISCO BCOS是由国内企业主导研发.对外开源.安全可控的企业级金融联盟链底层平台,由金链盟开源工作组协作打造,并于2017年正式对外开源. 目前,成熟的区块链的平台不少,之所以选择FISC ...
- 用Java为Hyperledger Fabric(超级账本)编写区块链智能合约链代码
编写第一个 Java 链代码程序 在上一节中,您已经熟悉了如何构建.运行.部署和调用链代码,但尚未编写任何 Java 代码. 在本节中,将会使用 Eclipse IDE.一个用于 Eclipse 的 ...
- FISCO BCOS WorkShop | 区块链开发特训营,开课啦!
FISCO BCOS是完全开源的联盟区块链底层技术平台,由金融区块链合作联盟(深圳)(简称金链盟)成立开源工作组通力打造.开源工作组成员包括博彦科技.华为.深证通.神州数码.四方精创.腾讯.微众银行. ...
- 用Java为Hyperledger Fabric(超级账本)开发区块链智能合约链代码之部署与运行示例代码
部署并运行 Java 链代码示例 您已经定义并启动了本地区块链网络,而且已构建 Java shim 客户端 JAR 并安装到本地 Maven 存储库中,现在已准备好在之前下载的 Hyperledger ...
- 基于Fisco-Bcos的区块链智能合约-业务数据上链SDK实现
合约的编写 基于springboot : https://github.com/FISCO-BCOS/spring-boot-starter pragma solidity ^0.4.24; cont ...
- 人民网基于FISCO BCOS区块链技术推出“人民版权”平台
FISCO BCOS是完全开源的联盟区块链底层技术平台,由金融区块链合作联盟(深圳)(简称金链盟)成立开源工作组通力打造.开源工作组成员包括博彦科技.华为.深证通.神州数码.四方精创.腾讯.微众银行. ...
- 基于Tendermint的区块链漂流瓶简单实现
本文主要借demo介绍基于Tendermint的区块链应用开发,这个demo很简单,主要包含以下功能: 扔漂流瓶 捞漂流瓶 之后投放者和打捞者可以相互传递[加密]信息 代码已上传至github. Te ...
- 区块链之智能合约 solidity踩坑 --上篇
概述 最近在写合约时遇到一些坑,做一下总结: 介绍主要分一下三个方面: 对区块链的简单描述 结合业务场景,编写简单智能合约,时遇到的坑(上篇) assembly 的使用说明(下篇) 正文 进入正题之前 ...
- EOS测试链智能合约部署调用
ETH与EOS两者智能合约进行简单的对比. 1.编译智能合约(合约编译成.wasm与.abi格式后即可部署到区块链) [root@C03-12U-26 testcontract]# cat testc ...
随机推荐
- 面试题:了解MySQL的Flush-List吗?顺便说一下脏页的落盘机制!(文末送书)
Hi,大家好!我是白日梦! 今天我要跟你分享的MySQL话题是:"了解Flush-List吗?顺便说一下脏页的落盘机制!(文末送书)" 本文是MySQL专题的第 8 篇,共110篇 ...
- Linux操作系统选择
主流的操作系统 ubuntu centos debian oracle linux 主要使用的操作系统就是上面几个,主要是ubuntu和centos,debian是基于ubuntu改的,oracle ...
- oracle的三种连接方式
1.通过sid jdbc:oracle:thin:@host:port:SID Example: jdbc:oracle:thin:@localhost:1521:sid_test 2.通过servi ...
- matlab 第五章单元数组、字符串作业
1.创建 2×2 单元数组,第 1.2 个元素为字符串,第三个元素为整型变量,第四个元素为双精度(double)类型,并将其用图形表示. A=cell(2,2); A(1,1)={'mat'}; A( ...
- [LeetCode题解]143. 重排链表 | 快慢指针 + 反转
解题思路 找到右边链表,再反转右边链表,然后按左.右逐一合并 代码 /** * Definition for singly-linked list. * public class ListNode { ...
- Windows常用的网络命令
命令 说明 ipconfig 查看网络配置的信息 ping 测试网络是否连通 tracert 网络诊断工具,可以列出分组经过的路由节点 net 有添加用户.开启服务等功能 netstat 显示协议统计 ...
- java开发两年,这些线程知识你都不知道,你怎么涨薪?
前言 什么是线程:程序中负责执行的哪个东东就叫做线程(执行路线,进程内部的执行序列),或者说是进程的子任务. Java中实现多线程有几种方法 继承Thread类: 实现Runnable接口: 实现Ca ...
- 使用celery异步发送短信
目录 1.使用celery异步发送短信 1.1在celery_task/mian.py中添加发送短信函数 1.2在verifcations/views.py中添加celery发送短信视图函数 1.3 ...
- 【模版】【P3806】点分治
(7.17)早就想学点分治了--今天状态不太在线,眯一会写篇笔记来理理思路. ------------------------------------------------------------- ...
- vue前端静态页面Github Pages线上预览实现
一.前期准备之项目编译 此处记录如何解决vue2.0 打包之后,打开index.html出现空白页的问题,附上@参考地址 打包之前修改三个文件 第一步,找到build文件,在webpack.prod. ...