FISCO BCOS实战教程

FISCO BCOS实战教程

官方教程:
https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/browser/browser.html
场景:
对应场景的区块链设计:

一、底层

区块链分为公有链、私有链和联盟链,Fisco bcos是一个开源的区块链底层平台(联盟链)。
1.节点划分:节点有业务节点和记账节点,本次考虑的是记账节点.
2.群组的生成:按照业务我们搭建了一个单群组
3.搭建控制台

二、中间层

1.SDK的选择:(软件开发工具包)java,python,we3sdk等等
2.区块链浏览器
我们搭建好了区块链的整个底层之后,区块链底层的应用更偏向于开发者人员模式,但针对普通人员,他只需要看到目前整个区块链网络是一个怎样的状态,以及目前的交易是怎样的,或者是想要查询指定一个交易具体的一个情况等等,它只需要这些东西,那么区块链浏览器就诞生了,整体来说,它是一种针对底层这种看不见摸不着的一种补充,对大家关注的比较重要的东西采用可视化的形式呈现出来。
我们搭建的区块链浏览器,可以做到以下几个功能:
1.简单了解区块链网络机构和节点组成
2.可以浏览并查询区块链交易数据
3.交易解析(合约上传编译后,解析合约相关交易的方法名和参数)
4.事件解析(合约上传编译后,解析合约相关交易回执中的事件方法名和参数)
。。。。
区块链搭建完成之后是一个这种样子的:
FISCO BCOS实战教程_第1张图片
主要功能模块有:群组切换模块,配置模块(包括群组配置,合约配置,节点配置),区块链信息展示模块(区块链浏览器主要展示了链上群组的具体信息,这些信息包括:概览信息,区块信息,交易信息)。

3.webase中间分布式平台:
什么是webase:
官方解释:webase是在区块链应用和FISCO-BCOS节点之间搭建的一套通用组件。围绕交易、合约、密钥管理、数据、可视化管理来设计各个模块,开发者可以根据业务需求,选择子系统进行部署。
简单来说,他是一个承上启下的一个组件,它的下是对应的底层,上对应的是区块链应用层,区块链底层是一个类似于dos界面,终端操作需要敲指令,虽然我们已有一套可视化界面就是区块链浏览器,但是总体而言区块链浏览器只是一个单一化的简单的系统呈现,就只是一些交易、节点呈现,但是我们想实现底层的很多的功能,比如说节点的管理,密钥的管理,其它相关的各个环节,希望采用一个简单的方式,那么随着区块链的发展,webase的出现就成了必然。
FISCO BCOS实战教程_第2张图片
对于开发者而言,webase可以简化开发工作,很多功能都可以在webase上完成,就不需要在底层终端进行操作。
webase的部署方式有两种,这里为了体验效果采用一键部署的方式,另外,如果为了针对性的研究各个子系统之间的模块关系,采用手动部署是比较合理的,因为原理性的东西只有比较深入的了解之后才方便进行系统化的研究。
一键部署会搭建:节点(FISCO-BCOS 2.0+)、管理平台(WeBASE-Web)、节点管理子系统(WeBASE-Node-Manager)、节点前置子系统(WeBASE-Front)、签名服务(WeBASE-Sign)。其中,节点的搭建是可选的,可以通过配置来选择使用已有链或者搭建新链。一键部署架构如下:
FISCO BCOS实战教程_第3张图片

参考连接:
https://blog.csdn.net/xxiangyusb/article/details/103026496
3.1节点前置服务Webase-front搭建
3.2 webase-deploy一键部署
修改 common.properties 配置文件,如下:

# WeBASE子系统的最新版本(v1.1.0或以上版本)
webase.web.version=v1.4.1
webase.mgr.version=v1.4.1
webase.sign.version=v1.4.1
webase.front.version=v1.4.1

# 节点管理子系统mysql数据库配置
mysql.ip=127.0.0.1
mysql.port=3306
mysql.user=root
mysql.password=123456
mysql.database=webasenodemanager

# 签名服务子系统mysql数据库配置
sign.mysql.ip=localhost
sign.mysql.port=3306
sign.mysql.user=root
sign.mysql.password=123456
sign.mysql.database=webasesign

# 节点前置子系统h2数据库名和所属机构
front.h2.name=webasefront
front.org=fisco

# WeBASE管理平台服务端口
web.port=5000
# 节点管理子系统服务端口
mgr.port=5001
# 节点前置子系统端口
front.port=5002
# 签名服务子系统端口
sign.port=5004


# 节点监听Ip
node.listenIp=127.0.0.1
# 节点p2p端口
node.p2pPort=30300
# 节点链上链下端口
node.channelPort=20200
# 节点rpc端口
node.rpcPort=8545

# 是否使用国密(0: standard, 1: guomi)
encrypt.type=0

# 是否使用已有的链(yes/no)
if.exist.fisco=yes

# 使用已有链时需配置
# 已有链的路径,start_all.sh脚本所在路径
# 路径下要存在sdk目录,sdk里存放sdk证书(ca.crt、node.crt和node.key)
fisco.dir=/root/fisco/nodes/127.0.0.1
# 前置所连接节点的绝对路径
# 路径下要存在conf文件夹,conf里存放节点证书(ca.crt、node.crt和node.key)
node.dir=/root/fisco/nodes/127.0.0.1/node0

# 搭建新链时需配置
# FISCO-BCOS版本
fisco.version=2.6.0
# 搭建节点个数(默认两个)
node.counts=4

三、业务层

1.区块链底层链管理员、系统管理员、机构节点用户、常规用户等设定,联盟委员会
2.业务设计:哪些数据上链,形式:智能合约
3.表规划,权限设计
4.sdk使用,以及微服务设计
5.配套安全审计
6.落盘加密方案
FISCO-BCOS中表的概念:
1.公有链上链时候就直接打包区块,因为它大部分只有资产这一需求
2.联盟链:更多是偏向业务层,除了打包区块数据不可篡改,还有其他业务数据有上链需求。但这些数据可以被修改,修改过程完全记录到区块链上。
智能合约:
在webase编写部署,在sdk中转换为soltojava的代码进行调用。
简单介绍下智能合约:
首先说一下图灵完备,简单来说,一切可计算的问题都能计算这样的虚拟机或者编程语言就叫做图灵完备的。图灵完备是在接触到区块链这个概念之后才被炒起来,早期图灵完备可能被部分人知道,但是被认知的广度没那么大。比特币比特网络的实现是图灵不完备的,图灵不完备就是不允许或者是限制循环的,可以保证,每段程序都不会陷入死循环,导致崩溃,都有运行完的时候。
那么智能合约是做什么的,我们执行一些智能化操作,比如说交易、记账等等,把数据打包到区块链,需要一些简化的程序来执行,然后记录上链,类似于一个黑盒操作,这个黑盒就是智能合约搭载EVM虚拟机运行的。
智能合约的本质是解决一些简单的操作,太复杂的逻辑不便于在区块链上使用,因为公链上的资源是有限的,全世界公链上的节点去共识记账,如果太复杂会造成高强度的计算造成资源消耗,导致区块链网络拥堵等问题。
官方提供的一个Table.sol,它可以理解为我们常规应用开发使用的仓储层,实现了常规的增删改查条件、基础数据转换等功能。

//假设是这个代码,不建议使用
pragma solidity ^0.4.25;

import "./Table.sol";

contract Donation1 {
     
    // event
    event RegisterEvent(int256 ret, string donationId, string donationName, uint256 donation_value, string serviceType, string whereValue, uint256 donation_counts);
    event TransferEvent(int256 ret, string from_donationId, string to_donationId, uint256 amount);

    constructor() public {
     
        // 构造函数中创建t_donation1表
        createTable();
    }

    function createTable() private {
     
        TableFactory tf = TableFactory(0x1001);
        // 捐款表, key : donorId
        // |    Id(主键) |   名称       |    物资      |   业务类型   | 捐款地/捐款人 | 成功收款/捐款次数      |
        // |-------------|-------------|--------------|-------------|--------------|-----------------------|
        // | donationId  | donationName|donation_value| serviceType | whereValue   |  donation_counts     |
        // |-------------|-------------|--------------|-----------  |--------------|-----------------------|
        // 创建表
        //7 arguements given but expects 3
        //tf.createTable("t_donation1","donationId", "donationName", "donation_value", "serviceType", "whereValue", "donation_counts");
        tf.createTable("t_donation1","donationId", "donationName, donation_value, serviceType, whereValue, donation_counts");
    }

    function openTable() private returns(Table) {
     
        TableFactory tf = TableFactory(0x1001);
        Table table = tf.openTable("t_donation1");
        return table;
    }

    /*
    描述 : 查询
    查询信息:
        捐款状态
    返回值:
            参数一: 成功返回0, 账户不存在返回-1
            参数二: 第一个参数为0时有效
    */
    function select(string donationId) public constant returns(int256, string, string, uint256, string, string, uint256) {
     
        // 打开表
        Table table = openTable();
        // 查询
        Entries entries = table.select(donationId, table.newCondition());
        uint256 donation_value = 0;
        uint256 donation_counts= 0;
        if (0 == uint256(entries.size())) {
     
            return (-1, "", "", donation_value,"", "" ,donation_counts);
        } else {
     
            Entry entry = entries.get(0);
            return (0, entry.getString("donationId"),entry.getString("donationName"),uint256(entry.getInt("donation_value")),entry.getString("serviceType"), entry.getString("whereValue"), uint256(entry.getInt("donation_counts")));
        }
    }


    
    //合约内调用的简易版查询函数
    function _select(string donationId) public constant returns(int256,  uint256, uint256) {
     
        // 打开表
        Table table = openTable();
        // 查询
        Entries entries = table.select(donationId, table.newCondition());
        uint256 donation_value = 0;
        uint256 donation_counts= 0;
        if (0 == uint256(entries.size())) {
     
            return (-1, donation_value, donation_counts);
        } else {
     
            Entry entry = entries.get(0);
            return (0, uint256(entry.getInt("donation_value")), uint256(entry.getInt("donation_counts")));
        }
    }
    

    /* 
    描述 : 数据上链
    参数 :
        
    返回值:
        0 数据上链成功
        -1 数据ID已存在
        -2 其他错误
    */
    function register(string donationId, string donationName, uint256 donation_value, string serviceType, string whereValue, uint256 donation_counts) public returns(int256){
     
        int256 ret_code = 0;
        int256 ret= 0;
        uint256 temp_donation_value = 0;
        uint256 temp_donation_counts=0;
        // 查询账户是否存在
        (ret, temp_donation_value, temp_donation_counts) = _select(donationId);
        if(ret != 0) {
     
            Table table = openTable();

            Entry entry = table.newEntry();
            entry.set("donationId", donationId);
            entry.set("donationName", donationName);
            entry.set("donation_value", int256(donation_value));
            entry.set("serviceType", serviceType);
            entry.set("whereValue", whereValue);
            entry.set("donation_counts", int256(donation_counts));
            // 插入
            int count = table.insert(donationId, entry);
            if (count == 1) {
     
                // 成功
                ret_code = 0;
            } else {
     
                // 失败? 无权限或者其他错误
                ret_code = -2;
            }
        } else {
     
            // 账户已存在
            ret_code = -1;
        }

        emit RegisterEvent(ret_code, donationId, donationName, donation_value, serviceType, whereValue, donation_counts);
        return ret_code;
    }

    /*
    描述 : 捐款受理
    参数 :
            from_donationId : 捐款人账户
            to_donationId : 受助地账户
            amount : 款项金额
            account: 业务办理次数
    返回值:
            0  捐款成功
            -1 捐款账户不存在
            -2 受助地账户不存在
            -3 实际捐款金额与受理捐款金额不一致
            -4 金额溢出
            -5 其他错误
    */
    function transfer(string from_donationId, string to_donationId, uint256 amount) public returns(int256) {
     
        // 查询账户信息
        int ret_code = 0;
        int256 ret = 0;
        uint256 from_donation_value = 0;
        uint256 from_donation_counts=0;
        uint256 to_donation_value = 0;
        uint256 to_donation_counts=0;

        // 捐款账户是否存在?
        (ret, from_donation_value, from_donation_counts) = _select(from_donationId);
        if(ret != 0) {
     
            ret_code = -1;
            // 捐款账户不存在
            emit TransferEvent(ret_code, from_donationId, to_donationId, amount);
            return ret_code;

        }

        // 受助地账户是否存在?
        (ret, to_donation_value, to_donation_counts) = _select(to_donationId);
        if(ret != 0) {
     
            ret_code = -2;
            // 受助地账户不存在
            emit TransferEvent(ret_code, from_donationId, to_donationId, amount);
            return ret_code;
        }

        if(from_donation_value < amount) {
     
            ret_code = -3;
            // 实际捐款金额与受理捐款金额不一致
            emit TransferEvent(ret_code, from_donationId, to_donationId, amount);
            return ret_code;
        }

        if (to_donation_value + amount < to_donation_value) {
     
            ret_code = -4;
            // 接收账户金额溢出
            emit TransferEvent(ret_code, from_donationId, to_donationId, amount);
            return ret_code;
        }

        Table table = openTable();

        Entry entry0 = table.newEntry();
        entry0.set("donationId", from_donationId);
        entry0.set("donation_value", int256(from_donation_value - amount));
        entry0.set("donation_counts", int256(from_donation_counts + 1));
        entry0.set("whereValue", to_donationId);
        // 更新捐款账户
        int count = table.update(from_donationId, entry0, table.newCondition());
        if(count != 1) {
     
            ret_code = -5;
            // 失败? 无权限或者其他错误?
            emit TransferEvent(ret_code, from_donationId, to_donationId, amount);
            return ret_code;
        }

        Entry entry1 = table.newEntry();
        entry1.set("donationId", to_donationId);
        entry1.set("donation_value", int256(to_donation_value + amount));
        entry1.set("donation_counts", int256(to_donation_counts +1));
        entry1.set("whereValue", from_donationId);
        // 更新接收账户
        table.update(to_donationId, entry1, table.newCondition());

        emit TransferEvent(ret_code, from_donationId, to_donationId, amount);

        return ret_code;
    }
}

四、演示

访问WeBASE管理平台 :在浏览器输入地址http://localhost:5000
1. 登录webase,默认的官方账号密码admin,Abcd1234
FISCO BCOS实战教程_第4张图片
FISCO BCOS实战教程_第5张图片
2. 添加私钥管理
FISCO BCOS实战教程_第6张图片
3. 建立合约,编译合约,部署合约
FISCO BCOS实战教程_第7张图片
4.发送交易
FISCO BCOS实战教程_第8张图片
FISCO BCOS实战教程_第9张图片
blockNumber:块高14
FISCO BCOS实战教程_第10张图片

你可能感兴趣的:(FISCO-BCOS,区块链)