FISCO BCOS | 構建第一個區塊鏈應用程式

BSN研習社發表於2023-10-12
本章將介紹基於FISCO BCOS區塊鏈的業務應用場景開發的全流程。介紹包括業務場景分析、合約設計實現、合約編譯、區塊鏈開發等。最後,我們介紹一個應用模組實現,即透過我們提供的Java SDK實現對區塊鏈上合約的呼叫訪問。

本教程要求使用者熟悉Linux操作環境,具備Java開發的基本技能,能夠使用Gradle工具,熟悉Solidity語法。
透過本教程,您將瞭解以下內容:

  1. 如何以契約的形式表達業務場景的邏輯
  2. 如何將Solidity合約轉換為Java類
  3. 如何配置Java開發工具包
  4. 如何構建應用程式並將Java SDK整合到應用程式工程中
  5. 如何透過Java SDK呼叫合約介面,並理解其原理

本教程中提供了該示例的完整專案原始碼,使用者可以基於它快速開發自己的應用程式。
示例應用程式要求
區塊鏈自然是防篡改和可追溯的。這些特點使其對金融部門更具吸引力。本文將提供資產管理開發的簡單示例,並最終實現以下功能:

  • 能夠在區塊鏈上註冊資產
  • 能夠從不同的賬戶轉移資金
  • 能夠檢查賬戶中的資產數量

合同設計和實施
在區塊鏈上開發應用時,為了結合業務需求,首先需要設計相應的智慧合約,確定合約需要的儲存資料,並在此基礎上確定智慧合約提供的介面。最後,具體實現每個介面。
儲存設計
FISCO BCOS提供了合約CRUD介面開發模型,可以透過合約建立表,並對建立的表進行新增、刪除和修改。對於此應用程式,我們需要設計一個用於儲存資產管理的表。該表的欄位如下所示:t_asset

  • 賬戶:主鍵、資產賬戶(字串型別)
  • asset_value:資產金額(UINT256型)

account是主鍵,是操作表時需要傳遞的欄位。區塊鏈根據主鍵欄位查詢表中的匹配記錄。表的示例如下:t_assett_asset

undefined

介面設計
根據業務的設計目標,需要實現資產登記、轉賬、查詢功能。對應函式的介面如下:
    // query the amount of assetsfunction select(string account) public constant returns(int256, uint256)// asset registrationfunction register(string account, uint256 amount) public returns(int256)// asset transferfunction transfer(string from_asset_account, string to_asset_account, uint256 amount) public returns(int256)
    完整來源
      
      pragma solidity ^
      0.4
      .24;
      
      
      
      import "./Table.sol";
      contract Asset {     // event     event RegisterEvent( int256 ret, string account, uint256 asset_value);     event TransferEvent( int256 ret, string from_account, string to_account, uint256 amount);
         constructor() public {         // create a t_asset table in the constructor        createTable();    }
          function createTable() private {        TableFactory tf = TableFactory( 0x1001);         // asset management table, key : account, field : asset_value         // |  account(primary key)   |  amount       |         // |-------------------- |-------------------|         // |        account      |    asset_value    |         // |---------------------|-------------------|         //         // create table        tf.createTable( "t_asset", "account", "asset_value");    }
          function openTable() private returns( Table) {        TableFactory tf = TableFactory( 0x1001);        Table table = tf.openTable( "t_asset");         return table;    }
          /*    description: query asset amount according to asset account
         parameter:            account: asset account
         return value:            parameter1: successfully returns 0, the account does not exist and returns -1            parameter2: valid when the first parameter is 0, the amount of assets    */     function select( string account) public constant returns( int256, uint256) {         // open table        Table table = openTable();         // query        Entries entries = table. select(account, table.newCondition());        uint256 asset_value = 0;         if ( 0 == uint256(entries.size())) {             return ( -1, asset_value);        } else {            Entry entry = entries. get( 0);             return ( 0, uint256(entry.getInt( "asset_value")));        }    }
          /*    description : asset registration    parameter :            account : asset account            amount  : asset amount    return value:            0  regist successfully            -1 asset account already exists            -2 other error    */     function register( string account, uint256 asset_value) public returns( int256){        int256 ret_code = 0;        int256 ret= 0;        uint256 temp_asset_value = 0;         // to query whather the account exists        (ret, temp_asset_value) = select(account);         if(ret != 0) {            Table table = openTable();
                 Entry entry = table.newEntry();            entry. set( "account", account);            entry. set( "asset_value", int256(asset_value));             // insert             int count = table.insert(account, entry);             if (count == 1) {                 // true                ret_code = 0;            } else {                 // false. no permission or other error                ret_code = -2;            }        } else {             // account already exists            ret_code = -1;        }
              emit RegisterEvent( ret_code, account, asset_value);
              return ret_code;    }
          /*    description : asset transfer    parameter :            from_account : transferred asset account            to_account :received asset account            amount : transferred amount    return value:            0  transfer asset successfully            -1 transfe asset account does not exist            -2 receive asset account does not exist            -3 amount is insufficient            -4 amount is excessive            -5 other error    */     function transfer( string from_account, string to_account, uint256 amount) public returns( int256) {         // query transferred asset account information         int ret_code = 0;        int256 ret = 0;        uint256 from_asset_value = 0;        uint256 to_asset_value = 0;
              // whather transferred asset account exists?        (ret, from_asset_value) = select(from_account);         if(ret != 0) {            ret_code = -1;             // not exist             emit TransferEvent( ret_code, from_account, to_account, amount);             return ret_code;
             }
              // whather received asset account exists?        (ret, to_asset_value) = select(to_account);         if(ret != 0) {            ret_code = -2;             // not exist             emit TransferEvent( ret_code, from_account, to_account, amount);             return ret_code;        }
              if(from_asset_value < amount) {            ret_code = -3;             // amount of transferred asset account is insufficient             emit TransferEvent( ret_code, from_account, to_account, amount);             return ret_code;        }
              if (to_asset_value + amount < to_asset_value) {            ret_code = -4;             // amount of received asset account is excessive             emit TransferEvent( ret_code, from_account, to_account, amount);             return ret_code;        }
             Table table = openTable();
             Entry entry0 = table.newEntry();        entry0. set( "account", from_account);        entry0. set( "asset_value", int256(from_asset_value - amount));         // update transferred account         int count = table.update(from_account, entry0, table.newCondition());         if(count != 1) {            ret_code = -5;             // false? no permission or other error?             emit TransferEvent( ret_code, from_account, to_account, amount);             return ret_code;        }
             Entry entry1 = table.newEntry();        entry1. set( "account", to_account);        entry1. set( "asset_value", int256(to_asset_value + amount));         // update received account        table.update(to_account, entry1, table.newCondition());
              emit TransferEvent( ret_code, from_account, to_account, amount);
              return ret_code;    } }
      注意: 合約的實現需要引入FISCO BCOS提供的系統合約介面檔案。系統合約檔案的介面由底層FISCO BCOS實現。當業務合約需要操作CRUD介面時,需要引入介面合約檔案。合同詳細介面參考在這裡:
      https://fisco-bcos-documentation.readthedocs.io/en/latest/docs/manual/smart_contract.html#crud
      Asset.solTable.solTable.sol
      合同編制
      在上一節中,我們根據業務需求設計了合約的儲存和介面,並將它們完整地實現了。但是,Java程式不能直接呼叫Solidity合約。Solidity合約檔案需要先編譯成Java檔案。Asset.sol
      控制檯提供了一個編譯工具,用於將合約檔案儲存在目錄中。使用控制檯目錄中提供的指令碼進行編譯,如下所示:
      Asset.solconsole/contract/soliditysol2java.sh
        $ mkdir -p ~/fisco# download console$ cd ~/fisco && curl -#LO ~/fisco/console/# compile the contract, specify a Java package name parameter later, you can specify the package name according to the actual project path.$ ./sol2java.sh -p org.fisco.bcos.asset.contract
        操作成功後,java、abi、bin目錄將生成如下圖。console/contracts/sdkdirectory
          |-- abi # The generated abi directory, which stores the abi file generated by Solidity contract compilation.|   |-- Asset.abi|   |-- Table.abi|-- bin # The generated bin directory, which stores the bin file generated by Solidity contract compilation.|   |-- Asset.bin|   |-- Table.bin|-- contracts # The source code file that stores Solidity contract. Copy the contract that needs to be compiled to this directory.|   |-- Asset.sol # A copied Asset.sol contract, depends on Table.sol|   |-- Table.sol # The contract interface file that implements the CRUD operation|-- java  # Storing compiled package path and Java contract file|   |-- org|        |--fisco|             |--bcos|                  |--asset|                       |--contract|                             |--Asset.java  # Java file generated by the Asset.sol contract|                             |--Table.java  # Java file generated by the Table.sol contract|-- sol2java.sh
          包路徑目錄在java目錄中生成。該目錄包含兩個檔案和,其中是Java應用程式呼叫Asset.sol合約所需的檔案。
          org/fisco/bcos/asset/contract/Asset.javaTable.javaAsset.java
          Asset.java的主介面:
            
            
            package org.fisco.bcos.asset.contract;
            
            
            
            public class Asset extends Contract {     // Asset.sol contract  transfer interface generation     public TransactionReceipt transfer (String from_account, String to_account, BigInteger amount);     // Asset.sol contract  register interface generation     public TransactionReceipt register (String account, BigInteger asset_value);     // Asset.sol contract  select interface generation     public Tuple2<BigInteger, BigInteger> select (String account) throws ContractException;
                // Load the Asset contract address, to generate Asset object     public static Asset load (String contractAddress, Client client, CryptoKeyPair credential);
                // Deploy Assert.sol contract, to generate Asset object     public static Asset deploy (Client client, CryptoKeyPair credential) throws ContractException; }
            載入和部署函式用於構造Asset物件,其他介面用於呼叫相應solidity協定的介面。具體用法將在下面介紹。
            開發工具包配置
            我們為開發提供了一個Java工程專案。首先,獲取Java工程專案:
              $ mkdir -p ~/fisco# get the Java project project archive$ cd ~/fisco$ curl -#LO 
              如果資產app.tar.gz由於網路問題長時間無法下載,請嘗試:
              curl-#LO
              資產應用專案的目錄結構如下:
                |-- build.gradle // gradle configuration file|-- gradle|   |-- wrapper|       |-- gradle-wrapper.jar //  related code implementation for downloading Gradle|       |-- gradle-wrapper.properties //  Configuration information used by the wrapper, such as the version of gradle|-- gradlew // shell script for executing wrapper commands under Linux or Unix|-- gradlew.bat // batch script for executing wrapper commands under Windows|-- src|   |-- main|   |   |-- java|   |         |-- org|   |             |-- fisco|   |                   |-- bcos|   |                         |-- asset|   |                               |-- client // the client calling class|   |                                      |-- AssetClient.java|   |                               |-- contract // the Java contract class|   |                                      |-- Asset.java|   |-- test|       |-- resources // resource files|           |-- applicationContext.xml // project configuration file|           |-- contract.properties // file that stores the deployment contract address|           |-- log4j.properties // log configuration file|           |-- contract // Solidity contract files|                   |-- Asset.sol|                   |-- Table.sol||-- tool    |-- asset_run.sh // project running script
                專案介紹Java SDK
                該專案的檔案已引入Java SDK,無需修改 介紹方法如下:

                • 您需要將maven遠端儲存庫新增到檔案中:build.gradle

                  repositories {    mavenCentral()    maven {        url "}    maven { url "}}

                  • 介紹Java SDKjar包

                    compile ('org.fisco-bcos.java-sdk:fisco-bcos-java-sdk:2.7.2')
                    證照和配置檔案

                    • 區塊鏈節點證照配置

                    複製區塊鏈節點對應的SDK證照
                      # go to the ~ directory# copy the node certificate to the project's resource directory$ cd ~/fisco$ cp -r nodes/127.0.0.1/sdk/* asset-app/src/test/resources/conf# if you want to run this app in IDE, copy the certificate to the main resource directory$ mkdir -p asset-app/src/main/resources/conf$ cp -r nodes/127.0.0.1/sdk/* asset-app/src/main/resources/conf

                      • 應用程式上下文.xml

                      注意:
                      如果鏈中設定的channel_listen_ip(如果節點版本低於v2.3.0,勾選listen_ip)為127.0.0.1或0.0.0.0,channel_listen_port為20200,則無需修改配置。如果區塊鏈節點的配置發生變化,則需要修改。
                      applicationContext.xmlapplicationContext.xml
                      業務發展
                      我們已經介紹瞭如何在您自己的專案中引入和配置Java SDK。本節介紹如何透過Java程式呼叫合約,以及一個示例資產管理說明。資產應用專案已包含示例的完整原始碼,使用者可以直接使用。現在介紹核心類的設計和實現。AssetClient
                      AssetClient.java:合約的部署和呼叫是透過呼叫來實現的,路徑、初始化和呼叫過程都在這個類中。
                      Asset.java/src/main/java/org/fisco/bcos/asset/client

                      • 初始化

                      初始化程式碼的主要功能是構造Web3j和憑證的物件,在建立相應的合約類物件(呼叫合約類的部署或載入函式)時需要使用這些物件。
                        @SuppressWarnings("resource")ApplicationContext context =        new ClassPathXmlApplicationContext("classpath:applicationContext.xml");bcosSDK = context.getBean(BcosSDK.class);// init the client that can send requests to the group oneclient = bcosSDK.getClient(1);// create the keyPaircryptoKeyPair = client.getCryptoSuite().createKeyPair();client.getCryptoSuite().setCryptoKeyPair(cryptoKeyPair);logger.debug("create client for group1, account address is " + cryptoKeyPair.getAddress());

                        • 構造協定類物件

                        合約物件可以使用部署或載入函式進行初始化,這些函式在不同場景下使用。前者適用於初始部署協定,後者在協定已部署且合約地址已知時使用。
                          // deploy contractAsset asset = Asset.deploy(client, cryptoKeyPair);// load contract addressAsset asset = Asset.load(contractAddress, client, cryptoKeyPair);

                          • 介面呼叫

                          使用合約物件呼叫對應的介面,處理返回的結果。
                            // select interface calling Tuple2<BigInteger, BigInteger> result = asset.select(assetAccount);// register interface callingTransactionReceipt receipt = asset.register(assetAccount, amount);// transfer interfaceTransactionReceipt receipt = asset.transfer(fromAssetAccount, toAssetAccount, amount);
                            執行
                            到目前為止,我們已經介紹了使用區塊鏈的資產管理應用程式的所有流程並實現了這些功能。然後我們可以執行專案並測試函式是否正常。

                            • 彙編

                              # switch to project directory$ cd ~/asset-app# compile project$ ./gradlew build
                              編譯成功後,將在專案根目錄下生成目錄。dist目錄中有一個指令碼來簡化專案操作。現在,讓我們首先驗證本文中列出的要求。distasset_run.sh

                              • 部署合約Asset.sol

                                # enter dist directory$ cd dist$ bash asset_run.sh deployDeploy Asset successfully, contract address is 0xd09ad04220e40bb8666e885730c8c460091a4775

                                • 註冊資產

                                  $ bash asset_run.sh register Alice 100000Register account successfully => account: Alice, value: 100000$ bash asset_run.sh register Bob 100000Register account successfully => account: Bob, value: 100000

                                  • 查詢資產

                                    $ bash asset_run.sh query Aliceaccount Alice, value 100000$ bash asset_run.sh query Bobaccount Bob, value 100000

                                    • 轉移資產

                                      $ bash asset_run.sh transfer Alice Bob  50000Transfer successfully => from_account: Alice, to_account: Bob, amount: 50000$ bash asset_run.sh query Aliceaccount Alice, value 50000$ bash asset_run.sh query Bobaccount Bob, value 150000
                                      總結: 到目前為止,我們已經透過合約開發、合約編譯、SDK配置和業務開發,構建了基於FISCO BCOS聯盟區塊鏈的應用。


                                      來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70012206/viewspace-2988490/,如需轉載,請註明出處,否則將追究法律責任。

                                      相關文章