如何在Spring Boot中玩轉智慧合約
本文是由鏈博科技 ChainBoard.IO 為大家帶來的web3j 對智慧合約的呼叫。讓 java 程式可以和我們的智慧合約愉快的互動起來~
一、什麼是 web3j
web3j是一個高度模組化、響應式、型別安全的Java和Android庫,用於與智慧合約互動,並與Ethereum網路的客戶端(節點)整合。
二、準備工作
1.新建一個spring-boot的專案,在 pom 檔案中新增
<dependency> <groupId>org.web3j</groupId> <artifactId>web3j-spring-boot-starter</artifactId> <version>1.6.0</version></dependency>
2.開啟以太坊客戶端 (如果有可以直接連線的客戶端,可以忽略此步驟)注意:開啟客戶端的時候需要加上 --rpc 引數。否則無法呼叫。並且需要開啟你的礦工,來完成智慧合約部署呼叫等工作。
geth --rpc --datadir "./chain" --nodiscover console 2>>ouput.logminer.start()
3.生成智慧合約的封裝器 (1) 下載 web3j 的 Command Line Tool: Homebrew
brew tap web3j/web3jbrew install web3j
下載zip檔案:下載地址
unzip web3j-<version>.zip./web3j-<version>/bin/web3j
(2) 生成封裝器 需要先用 solc 編譯生成 .bin .abi 檔案
solc 安裝命令:
npm install-g solc
命令:
> solcjs <Solidity檔案地址>.sol --bin --abi --optimize -o <輸出資料夾路徑>/
例項:這裡以 Compute.sol 檔案為例,示例合約見文章最後一節
> solcjs Compute.sol --abi --bin -o ./
會生成四個檔案,如下:
注意:因為合約中有 Compute、Owner 兩個函式,所以兩個函式的檔案都會生成出來。但是,由於 Compute 函式繼承了 Owner 函式的方法,所以實際上我們只需要用到 Computesol
用 web3j 生成 java 封裝器 命令:
web3j solidity generate --solidityTypes <智慧合約編譯之後的.bin檔案的地址>.bin <智慧合約編譯之後的.abi檔案的地址>.abi -o /path/to/src/main/java -p com.your.organisation.name
-o
後接生成好的java檔案放置的位置, -p
後接生成的java檔案的包名注意:.bin .abi檔案順序不能反,否則會報錯
例項:使用我們之前生成的檔案,將 java 檔案生成到我們的專案中:
web3j solidity generate --solidityTypes Compute_sol_Compute.bin Compute_sol_Compute.abi -o ./project/src/main/java -p com.demo
輸出如下資訊後,可以在我們指定的路徑看見生成好的 java 檔案 Compute_sol_Compute.java
:
三、web3j 基礎命令
1.建立以太坊連線
Web3j web3j = Web3j.build(new HttpService());
預設的連線地址是 http://localhost:8545/
,也可以改變地址,連線其他客戶端。
2.載入賬戶資訊 賬戶檔案可以在私鏈資料資料夾中的 keystore 資料夾中找到
Credentials credentials = WalletUtils.loadCredentials( "123", "/datadir/chain/keystore/UTC--2018-03-14T14-46-38.646997441Z--c2acba996f709d4b806d3330996f49d50f088258");
第一個變數填入賬戶的密碼,第二個變數填入賬戶檔案的 path
3.獲取賬戶餘額
Web3j web3j = Web3j.build(new HttpService());String address = "0xa6fd2ebac389773f5bd34d0738bc5fdbd1bea01b";EthGetBalance ethGetBalance = web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send();if(ethGetBalance!=null){ // 列印賬戶餘額 System.out.println(ethGetBalance.getBalance()); // 將單位轉為以太,方便檢視 System.out.println(Convert.fromWei(ethGetBalance.getBalance().toString(), Convert.Unit.ETHER));}
四、使用 Java 部署智慧合約
部署智慧合約的命令:
YourSmartContract contract = YourSmartContract.deploy( <web3j>, <credentials>, GAS_PRICE, GAS_LIMIT, [<initialValue>,] <param1>, ..., <paramN>).send();
例項:部署 Compute_sol_Compute.java
// 建立一個 web3j 的連線Web3j web3j = Web3j.build(new HttpService());// 部署的時候需要用到該賬戶的 gas,務必保證該賬戶餘額充足Credentials credentials = WalletUtils.loadCredentials( "123", "/datadir/chain/keystore/UTC--2018-03-14T14-46-38.646997441Z--c2acba996f709d4b806d3330996f49d50f088258"); // 部署合約 Compute_sol_Compute compute_sol_compute = Compute_sol_Compute.deploy(web3j, credentials, BigInteger.valueOf(200000), BigInteger.valueOf(20000000)).send();// 部署完成後打印合約地址System.out.println(compute_sol_compute.getContractAddress());
五、使用 Java 呼叫智慧合約
這裡,我們還是使用第二篇中編寫的智慧合約為例
1.載入你的智慧合約 命令:
YourSmartContract contract = YourSmartContract.load( "0x<address>|<ensName>", web3j, credentials, GAS_PRICE, GAS_LIMIT);
例項:
// 填入剛才部署後打印出來的合約地址String address = "0x9b0851112b41664171338abaf0df86e040c34d07";Compute_sol_Compute compute_sol_compute = Compute_sol_Compute.load( address, web3j, credentials, BigInteger.valueOf(200000), BigInteger.valueOf(20000000));
2.驗證合約是否可用 命令:
contract.isValid();
例項:驗證已部署的智慧合約是否可用
System.out.println(compute_sol_compute.isValid());
3.呼叫智慧合約 命令:
Type result = contract.someMethod(<param1>, ...).send();
例項:呼叫 Compute_sol_Compute.java 中的方法 1.呼叫 getLCM 方法
// 呼叫是能合約函式Uint256 first = new Uint256(2);Uint256 second = new Uint256(3);TransactionReceipt transactionReceipt = compute_sol_compute.getLCM(first, second).send();System.out.println(transactionReceipt);
執行成功後會返回打印出來本次交易的資訊。
2.呼叫 getRecord 方法
Uint256 index = new Uint256(0);List<Uint256> result = compute_sol_compute.getRecord(index).send().getValue();for (Uint256 uint256 : result) { System.out.println(uint256.getValue());}
結果:
236
3.使用監聽事件,獲取合約結果
Uint256 first = new Uint256(2);Uint256 second = new Uint256(3);TransactionReceipt transactionReceipt = compute_sol_compute.getLCM(first, second).send();Compute_sol_Compute.GetLCMEventResponse result = compute_sol_compute.getGetLCMEvents(transactionReceipt).get(0);System.out.println(result.first.getValue());System.out.println(result.second.getValue());System.out.println(result.result.getValue());
使用一個監聽事件等待到結果返回,因為是同步的,所以執行的時間會比較長。最後,可以拿回本次智慧合約執行的結果。
六、示例合約
pragma solidity ^0.4.19;contract Owner { //合約擁有者 address public owner; //建構函式,將合約的所有權給予釋出者 function Owner() public { owner = msg.sender; } //僅有合約的擁有者可以操作 modifier onlyOwner() { require(msg.sender == owner); _; } //onlyOwner作為函式執行的前置條件,僅有合約擁有者可以更換所屬權 function setOwner(address to) public onlyOwner { if(to != address(0)) { owner == to; } }}//通過is使Compute繼承Owner合約contract Compute is Owner { //建立一個儲存於區塊鏈上的二維陣列,儲存每一次計算的輸入以及結果 uint[3][] records; //比較大小,solidity允許返回兩個值 function compare(uint first, uint second) internal pure returns(uint bigOne, uint smallOne) { if(first > second) { return (first, second); } else { return (second, first); } } //建立事件去監聽每一次計算並記錄日誌 event GetLCM(uint first, uint second, uint result); function getLCM(uint first, uint second) external onlyOwner returns(uint) { if (first == second) { return first; } else { uint bigOne;uint smallOne; (bigOne, smallOne) = compare(first, second); uint i = 1; while(true) { uint mul = i * bigOne; if(mul % smallOne == 0) { uint index = records.push([first, second, mul]) - 1; //呼叫事件 GetLCM(first, second, mul); return index; } i++; } } } //根據索引獲取遊戲記錄 function getRecord(uint index) external onlyOwner view returns(uint[3]) { return records[index]; }}
最後,給大家介紹一下:
ChainBoard 核心團隊利用其在區塊鏈技術研發上沉澱的豐富經驗,圍繞專案的需求持續創新,與合作伙伴開放共贏、深度融合,共同打造在金融科技、遊戲、眾籌互助、醫療保健、物流等領域的區塊鏈應用。 主要輸出智慧合約開發、公鏈開發、聯盟鏈開發及互動應用開發等能力,助力專案迅速取得先發優勢。目前團隊已經在區塊鏈+遊戲及區塊鏈+金融與國內知名遊戲運營商和海外金融機構展開深度合作。
歡迎對ChainBoard實戰經驗感興趣的朋友和手裡有行業資源準備佈局區塊鏈的大佬關注我們的公眾號並和我們取得聯絡:(原創文章,轉載請註明出處,歡迎讀者分享到朋友圈)
推薦閱讀
深入交流、更多福利
掃碼加入我的知識星球
點選 “閱讀原文” 看看本號其他精彩內容