Blockchain smart contract based on Fisco-Bcos-simple case practice

1. Introduction to smart contracts

Smart contracts refer to electronically placing contract/agreement terms on the blockchain network in the form of code. The FISCO BCOS platform supports two types of smart contracts: Solidity smart contracts and pre-compiled smart contracts

Solidity is similar to Java. After the code is written, it needs to be converted into binary by a compiler. In Java, the compiler is Javac, and for Solidity, it is solc. The generated binary code will be executed in the virtual machine. Java code is executed in the Java Virtual Machine (JVM), which in Solidity is a virtual machine EVM on the blockchain. The purpose is to provide a unified set of logic for the blockchain, so that the same code can run on each node of the blockchain, and with the help of the consensus algorithm, the data of the blockchain can be changed in a unified way to achieve global consistency. result

 

aim of design:

Provide a unified set of logic for the blockchain, allowing the same code to run on each node of the blockchain. With the help of the consensus algorithm, the data of the blockchain can be changed in a unified way to achieve globally consistent results.

Solidity limitations and improvements

- Solidity is not flexible enough and is limited by its own stack depth. The total number of function parameters and local parameters cannot exceed 16. Solidity is a strongly typed language, but its type conversion is more troublesome - Poor performance

The underlying storage unit is 32 bytes (256 bits), which requires high hard disk read and write requirements and wastes a lot of storage resources.

In response to the above two points, FISCO BCOS provides a way to write contracts in C++: precompiled contracts. Developers can write smart contract logic in C++ and build it into the node,

The precompiled contract breaks through the limitations of the Solidity language. With the help of the powerful C++ language, various logics can be flexibly implemented, and the flexibility is greatly improved. At the same time, the performance advantages of C++ have also been well utilized. The performance of logic written through precompiled contracts has been improved compared to Solidity language.

Contract writing

Development tools: use of remix-ide, choose online remix for the development and compilation process

Remix is ​​a powerful open source tool that helps you write Solidity contracts directly from the browser. Remix is ​​written in JavaScript and supports both browser and local use.

Remix also supports smart contract testing, debugging, deployment, and more.

advantage:

1. Dynamic compilation, controllable compiled version 2. Instant error reminder 3. Automatic code completion 4. Code problem reminder during release stage 5. Simple call to design method

 

 

 

 

Understand the contract

example:

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;  
   }
}

2. Case contract design

The logic is as follows:

definition:

- Define the event method AddEqu(string equnum, string data) - Create the t_equipment table in the constructor - Query method: select(string equnum), query the device registration information according to the device number, or use records. (returns 0 if successful, returns -1 if the device does not exist) - addEqu(string equnum, string data), verify the uniqueness of the data before adding it. If it already exists, it will not be inserted.

Eqump contract class diagram

Contract: Java entity contract type abstraction for interacting with smart contracts

ManagedTransaction: transaction management

Eqump contract core code

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);
}

Compile and release

Introduction to WeBASE:

WeBASE (WeBank Blockchain Application Software Extension) is a set of common components built between blockchain applications and FISCO-BCOS nodes. Each module is designed around transactions, contracts, key management, data, and visual management. Developers can select subsystems for deployment based on business needs. WeBASE shields the complexity of the underlying blockchain, lowers the threshold for developers, and greatly improves the development efficiency of blockchain applications. It includes subsystems such as node front-end, node management, transaction links, data export, and Web management platforms.

process

  • - Compile and release

  • Test and verify Send transaction-->addEqu
{
  equnum : y1
  data:y1
}

 Send transaction-->select

{
 equnum:y1
}

Debugging Eqump based on web3sdk

1. Write smart contracts in the IDE. 2. After the contract is written, get the fisco ckient command line tool to compile and generate the java SDK. Execute a command in the /home/FISCO-BCOS/generator/console directory to parse the contract into a java SDK file.

sh sol2java.sh com.wg.service

 

Features

  • - Lightweight configuration to connect to blockchain nodes
  • - Generate .abi and .bin files with one click based on the .sol contract file
  • - 一键生成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","预约审核人":"李波 ","操作人员":"李波 ","仪器工作状态":"正常 ","预约形式":"必须预约 ","预约类型":"项目预约","仪器大类":"室内分析测试设备","仪器中类":"波谱仪器 ","仪器小类":"核磁共振波谱仪器"}

资料及参考

spring-boot-starter

Solidity官方文档

FISCO BCOS学习资料索引

在线remix

remix-ide的使用

Guess you like

Origin blog.csdn.net/u014535922/article/details/129374317