Fisco BCOS 区块链初探及数据上链

一、整体说明

BCOS的安装很简单,按照官网教程 https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/installation.html#fisco-bcos 一步步来就行。

完成后直接使用控制台就可以对链进行操作,部署合约等功能。

虚机重启后,可以进入到fisco 底链安装目录,使用 bash start_all.sh 进行启动

[root@localhost ~]# cd /root/fisco/nodes/127.0.0.1/
[root@localhost 127.0.0.1]# bash start_all.sh 
try to start node0
try to start node1
try to start node2
try to start node3
 node0 start successfully
 node1 start successfully
 node2 start successfully
 node3 start successfully

底链启动,之后部署可视化管理控制台WeBase 管理平台 https://webasedoc.readthedocs.io/zh_CN/latest/docs/WeBASE/install.html

使用一键部署可以很快速的部署起来。其所需要的 mysql 可以使用docker进行安装 。 java和python 及各种依赖需要在虚机主机安装。

根据官网教程安装成功后,浏览器访问 虚机ip:5000 即可访问管理平台地址。

因为是用docker安装,所以虚机重启后,可以直接启动docker 容器,来启动控制台。不许启动其他程序。(第四行是启动docker中的所有容器)

[root@localhost 127.0.0.1]# docker ps -a
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
[root@localhost 127.0.0.1]# systemctl start docker
[root@localhost 127.0.0.1]# docker start $(docker ps -a | awk '{ print $1}' | tail -n +2)
71780e025868
6577ffbcb5aa
b590340f7f9a
63d9b01b00af
09205cc428fd
[root@localhost 127.0.0.1]# docker ps -a
CONTAINER ID   IMAGE                              COMMAND                   CREATED      STATUS          PORTS                                         NAMES
71780e025868   webasepro/webase-web:v1.5.4        "/wait-for-it.sh 127…"   6 days ago   Up 21 seconds                                                 webase-web-5000
6577ffbcb5aa   webasepro/webase-node-mgr:v1.5.4   "/wait-for-it.sh 127…"   6 days ago   Up 21 seconds                                                 webase-node-mgr-5001
b590340f7f9a   webasepro/webase-front:v1.5.4      "/wait-for-it.sh 127…"   6 days ago   Up 20 seconds                                                 webase-front-5002
63d9b01b00af   webasepro/webase-sign:v1.5.3       "/wait-for-it.sh 127…"   6 days ago   Up 20 seconds                                                 webase-sign-5004
09205cc428fd   mysql:5.6                          "docker-entrypoint.s…"   6 days ago   Up 19 seconds   0.0.0.0:23306->3306/tcp, :::23306->3306/tcp   mysql-webase-23306

 启动后直接访问控制台即可。

这里贴几个控制台页面,见文章最下方,用于参考。

 

二、sdk开发

BCOS支持使用java进行合约交互。这里是我使用的java代码。

需要注意,java 代码其中的一部分需要使用他说的  sol2java.sh  进行转换。

我的合约源码是这样:

[root@localhost solidity]# cat HelloWorld.sol 
pragma solidity>=0.4.24 <0.6.11;

contract HelloWorld {
    string name;

    constructor() public {
        name = "Hello, World!";
    }

    function get() public view returns (string memory) {
        return name;
    }

    function set(string memory n) public {
        name = n;
    }
}

 

然后使用 sol2java.sh 进行转换成java代码。

 转换后的代码如下:

package org.furao.fisco.contract;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.fisco.bcos.sdk.abi.FunctionReturnDecoder;
import org.fisco.bcos.sdk.abi.TypeReference;
import org.fisco.bcos.sdk.abi.datatypes.Function;
import org.fisco.bcos.sdk.abi.datatypes.Type;
import org.fisco.bcos.sdk.abi.datatypes.Utf8String;
import org.fisco.bcos.sdk.abi.datatypes.generated.tuples.generated.Tuple1;
import org.fisco.bcos.sdk.client.Client;
import org.fisco.bcos.sdk.contract.Contract;
import org.fisco.bcos.sdk.crypto.CryptoSuite;
import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair;
import org.fisco.bcos.sdk.model.CryptoType;
import org.fisco.bcos.sdk.model.TransactionReceipt;
import org.fisco.bcos.sdk.model.callback.TransactionCallback;
import org.fisco.bcos.sdk.transaction.model.exception.ContractException;

@SuppressWarnings("unchecked")
public class HelloWorld extends Contract {
    public static final String[] BINARY_ARRAY = {"608060405234801561001057600080fd5b506040805190810160405280600d81526020017f48656c6c6f2c20576f726c6421000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50610107565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b61010491905b808211156101005760008160009055506001016100e8565b5090565b90565b6102d7806101166000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680634ed3885e146100515780636d4ce63c146100ba575b600080fd5b34801561005d57600080fd5b506100b8600480360381019080803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061014a565b005b3480156100c657600080fd5b506100cf610164565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561010f5780820151818401526020810190506100f4565b50505050905090810190601f16801561013c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b8060009080519060200190610160929190610206565b5050565b606060008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101fc5780601f106101d1576101008083540402835291602001916101fc565b820191906000526020600020905b8154815290600101906020018083116101df57829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061024757805160ff1916838001178555610275565b82800160010185558215610275579182015b82811115610274578251825591602001919060010190610259565b5b5090506102829190610286565b5090565b6102a891905b808211156102a457600081600090555060010161028c565b5090565b905600a165627a7a72305820aa8d37bec7b8a85e32740629893aa0bd0894e6eadefe527bc854f28f9493d1fd0029"};

    public static final String BINARY = org.fisco.bcos.sdk.utils.StringUtils.joinAll("", BINARY_ARRAY);

    public static final String[] SM_BINARY_ARRAY = {"608060405234801561001057600080fd5b506040805190810160405280600d81526020017f48656c6c6f2c20576f726c6421000000000000000000000000000000000000008152506000908051906020019061005c929190610062565b50610107565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100a357805160ff19168380011785556100d1565b828001600101855582156100d1579182015b828111156100d05782518255916020019190600101906100b5565b5b5090506100de91906100e2565b5090565b61010491905b808211156101005760008160009055506001016100e8565b5090565b90565b6102d7806101166000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063299f7f9d146100515780633590b49f146100e1575b600080fd5b34801561005d57600080fd5b5061006661014a565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100a657808201518184015260208101905061008b565b50505050905090810190601f1680156100d35780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b3480156100ed57600080fd5b50610148600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506101ec565b005b606060008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156101e25780601f106101b7576101008083540402835291602001916101e2565b820191906000526020600020905b8154815290600101906020018083116101c557829003601f168201915b5050505050905090565b8060009080519060200190610202929190610206565b5050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061024757805160ff1916838001178555610275565b82800160010185558215610275579182015b82811115610274578251825591602001919060010190610259565b5b5090506102829190610286565b5090565b6102a891905b808211156102a457600081600090555060010161028c565b5090565b905600a165627a7a723058200213820e973bd6ced2ea2b697ad80e27402d270ed95e854e77a8d247f96537c50029"};

    public static final String SM_BINARY = org.fisco.bcos.sdk.utils.StringUtils.joinAll("", SM_BINARY_ARRAY);

    public static final String[] ABI_ARRAY = {"[{\"constant\":false,\"inputs\":[{\"name\":\"n\",\"type\":\"string\"}],\"name\":\"set\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"}]"};

    public static final String ABI = org.fisco.bcos.sdk.utils.StringUtils.joinAll("", ABI_ARRAY);

    public static final String FUNC_SET = "set";

    public static final String FUNC_GET = "get";

    protected HelloWorld(String contractAddress, Client client, CryptoKeyPair credential) {
        super(getBinary(client.getCryptoSuite()), contractAddress, client, credential);
    }

    public static String getBinary(CryptoSuite cryptoSuite) {
        return (cryptoSuite.getCryptoTypeConfig() == CryptoType.ECDSA_TYPE ? BINARY : SM_BINARY);
    }

    public TransactionReceipt set(String n) {
        final Function function = new Function(
                FUNC_SET,
                Arrays.<Type>asList(new org.fisco.bcos.sdk.abi.datatypes.Utf8String(n)),
                Collections.<TypeReference<?>>emptyList());
        return executeTransaction(function);
    }

    public byte[] set(String n, TransactionCallback callback) {
        final Function function = new Function(
                FUNC_SET,
                Arrays.<Type>asList(new org.fisco.bcos.sdk.abi.datatypes.Utf8String(n)),
                Collections.<TypeReference<?>>emptyList());
        return asyncExecuteTransaction(function, callback);
    }

    public String getSignedTransactionForSet(String n) {
        final Function function = new Function(
                FUNC_SET,
                Arrays.<Type>asList(new org.fisco.bcos.sdk.abi.datatypes.Utf8String(n)),
                Collections.<TypeReference<?>>emptyList());
        return createSignedTransaction(function);
    }

    public Tuple1<String> getSetInput(TransactionReceipt transactionReceipt) {
        String data = transactionReceipt.getInput().substring(10);
        final Function function = new Function(FUNC_SET,
                Arrays.<Type>asList(),
                Arrays.<TypeReference<?>>asList(new TypeReference<Utf8String>() {
                }));
        List<Type> results = FunctionReturnDecoder.decode(data, function.getOutputParameters());
        return new Tuple1<String>(

                (String) results.get(0).getValue()
        );
    }

    public String get() throws ContractException {
        final Function function = new Function(FUNC_GET,
                Arrays.<Type>asList(),
                Arrays.<TypeReference<?>>asList(new TypeReference<Utf8String>() {
                }));
        return executeCallWithSingleValueReturn(function, String.class);
    }

    public static HelloWorld load(String contractAddress, Client client, CryptoKeyPair credential) {
        return new HelloWorld(contractAddress, client, credential);
    }

    public static HelloWorld deploy(Client client, CryptoKeyPair credential) throws ContractException {
        return deploy(HelloWorld.class, client, credential, getBinary(client.getCryptoSuite()), "");
    }
}

本地搭建普通maven项目,pom文件如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>fiscoTest</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.fisco-bcos.java-sdk</groupId>
            <artifactId>fisco-bcos-java-sdk</artifactId>
            <version>2.9.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>4.3.27.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>4.3.27.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.27.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>4.3.27.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.14</version>
        </dependency>
    </dependencies>


</project>

把刚才生成的代码贴过来,还有注意conf 文件和证书。这个官网中提到了。之后整体的结构如下,因为我使用了国密加密,所以conf文件夹里有gm文件夹:

以及applicationContext.xml, 注意其中需要指定部署链的虚拟机ip和端口

<?xml version="1.0" encoding="UTF-8" ?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
    <bean id="defaultConfigProperty" class="org.fisco.bcos.sdk.config.model.ConfigProperty">
        <property name="cryptoMaterial">
            <map>
                <entry key="certPath" value="conf" />
            </map>
        </property>
        <property name="network">
            <map>
                <entry key="peers">
                    <list>
                        <value>192.168.204.130:20200</value>
                        <value>192.168.204.130:20201</value>
                    </list>
                </entry>
            </map>
        </property>
        <property name="account">
            <map>
                <entry key="keyStoreDir" value="account" />
                <entry key="accountAddress" value="" />
                <entry key="accountFileFormat" value="pem" />
                <entry key="password" value="" />
                <entry key="accountFilePath" value="" />
            </map>
        </property>
        <property name="threadPool">
            <map>
                <entry key="channelProcessorThreadSize" value="16" />
                <entry key="receiptProcessorThreadSize" value="16" />
                <entry key="maxBlockingQueueSize" value="102400" />
            </map>
        </property>
    </bean>

    <bean id="defaultConfigOption" class="org.fisco.bcos.sdk.config.ConfigOption">
        <constructor-arg name="configProperty">
            <ref bean="defaultConfigProperty"/>
        </constructor-arg>
    </bean>

    <bean id="bcosSDK" class="org.fisco.bcos.sdk.BcosSDK">
        <constructor-arg name="configOption">
            <ref bean="defaultConfigOption"/>
        </constructor-arg>
    </bean>
</beans>

最后需要编写客户端,也就是调用合约的代码。如下:

package org.furao.fisco.client;


import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.List;
import java.util.Properties;
import org.fisco.bcos.sdk.BcosSDK;
import org.fisco.bcos.sdk.abi.datatypes.generated.tuples.generated.Tuple2;
import org.fisco.bcos.sdk.client.Client;
import org.fisco.bcos.sdk.crypto.keypair.CryptoKeyPair;
import org.fisco.bcos.sdk.model.TransactionReceipt;
import org.furao.fisco.contract.HelloWorld;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

public class HelloClient {

    static Logger logger = LoggerFactory.getLogger(HelloClient.class);

    private BcosSDK bcosSDK;
    private Client client;
    private CryptoKeyPair cryptoKeyPair;

    String addr = "0xb41e33298f9156cb52f4c4b0160adedd6ad3cb54";


    public void initialize() throws Exception {
        // 函数initialize中进行初始化
        // 初始化BcosSDK
        @SuppressWarnings("resource")
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
        bcosSDK = context.getBean(BcosSDK.class);
        // 初始化可向群组1发交易的Client
        client = bcosSDK.getClient(1);
        // 随机生成发送交易的公私钥对
        cryptoKeyPair = client.getCryptoSuite().createKeyPair();
        client.getCryptoSuite().setCryptoKeyPair(cryptoKeyPair);
        logger.debug("create client for group1, account address is " + cryptoKeyPair.getAddress());

    }


    /**
     * 部署合约
     */
    public void deploy(){
        try {
            HelloWorld hello = HelloWorld.deploy(client, cryptoKeyPair);
            System.out.println(
                    " deploy Asset success, contract address is " + hello.getContractAddress());

            recordAddr(hello.getContractAddress());
        } catch (Exception e) {
            // TODO Auto-generated catch block
            // e.printStackTrace();
            System.out.println(" deploy Asset contract failed, error message is  " + e.getMessage());
        }
    }

    /**
     * 存储合约地址
     * @param contractAddress
     * @throws FileNotFoundException
     * @throws IOException
     */
    private void recordAddr(String contractAddress)  throws FileNotFoundException, IOException {
//        Properties prop = new Properties();
//        prop.setProperty("address", contractAddress);
//        final Resource contractResource = new ClassPathResource("contract.properties");
//        FileOutputStream fileOutputStream = new FileOutputStream(contractResource.getFile());
//        prop.store(fileOutputStream, "contract address");
        addr = contractAddress;
        
    }

    /**
     * 读取合约地址
     * @return
     * @throws Exception
     */
    public String loadAddr() throws Exception {
//        // load Asset contact address from contract.properties
//        Properties prop = new Properties();
//        final Resource contractResource = new ClassPathResource("contract.properties");
//        prop.load(contractResource.getInputStream());
//
//        String contractAddress = prop.getProperty("address");
//        if (contractAddress == null || contractAddress.trim().equals("")) {
//            throw new Exception(" load Asset contract address failed, please deploy it first. ");
//        }
//        logger.info(" load Asset address from contract.properties, address is {}", contractAddress);
        return addr;
    }


    /**
     * 从合约中获取数据
     */
    public void getAmount() {
        try {
            String contractAddress = loadAddr();
            HelloWorld hello = HelloWorld.load(contractAddress, client, cryptoKeyPair);
            String s = hello.get();
            System.out.println(" value is: "+ s);

        } catch (Exception e) {
            // TODO Auto-generated catch block
            // e.printStackTrace();
            logger.error(" queryAssetAmount exception, error message is {}", e.getMessage());

            System.out.printf(" query asset account failed, error message is %s\n", e.getMessage());
        }
    }

    /**
     * 持久化合约中的属性
     * @param amount
     */
    public void setAccount(String amount) {
        try {
            String contractAddress = loadAddr();

            HelloWorld hello = HelloWorld.load(contractAddress, client, cryptoKeyPair);
            TransactionReceipt receipt = hello.set(amount);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            // e.printStackTrace();

            logger.error(" registerAssetAccount exception, error message is {}", e.getMessage());
            System.out.printf(" register asset account failed, error message is %s\n", e.getMessage());
        }
    }


    public static void main(String[] args) throws Exception {
        HelloClient client = new HelloClient();
        client.initialize();
        client.getAmount();;
//        client.setAccount("hello world ! this is a new Value");
    }

}

整体代码位置在github中 https://github.com/forReak/fiscoTest

 

如何使用:

官网中的教程太复杂了,这里简单点,直接在ide中进行运行main函数。

 HelloClient 中声明了几个方法

从上往下一次是初始化,部署合约,记录合约地址,读取合约地址,从合约属性中获取数据,设置数据及main方法。

首先进行部署合约。这时main'方法如下:

 如上图就说明已经成功部署,并打印出合约地址 0x1d00f910b4ce70b0229aa2aae2898672c8c41b38。这个时候一定要将这个地址存起来,程序里我放到addr属性中了。

然后调用合约。

 可见程序打印出合约中本身存储的数据。

然后重新设置值,调用 setAccount方法。

    public static void main(String[] args) throws Exception {
        HelloClient client = new HelloClient();
        client.initialize();
        //client.deploy();
        //client.getAmount();;
        client.setAccount("hello world ! this is a new Value");
    }

setAccount方法没有打印,等待一段时间后,重新运行getAmount 方法,即可看到新数据

 

至此,已经完成了如何使用sdk进行合约部署以及合约调用。

刚才的交易也可以在控制台中查到,如下:

 

 

三、控制台页面

控制台页面:

 

 

 

 

posted @ 2023-03-17 15:42  Furaooooo  阅读(1037)  评论(0)    收藏  举报