1 、概述
1.1 前言
本文档主要通过实例指导如何在卫生部主网上发布智能合约教程。
文档阅读人员:社区开发人员、测试人员、运维人员。
1.2 学习准备
1.2.1 Solidity语言
固态是一种开源的智能合约高级语言,可以运行支持开源的以太坊虚拟机(EVM)之上。具体的语言介绍和学习入门,在以下的网址中有详细介绍:https://solidity . readthedocs . io/en/v 0 . 5 . 2/.
1.2.2 Remix
支持智能合约开发IDE,可以在浏览器中快速部署测试智能合约,支持固态语言。访问和使用地址:http://remix.ethereum.org或者http://remix.hpb.io/。
1.2.3 HPB主链接入指导
详情请前往卫生部官网的接入详情界面,会指导你如何通过RPC,SDK等方式和主链交互https://www.hpb.io/client .
1.2.4 智能合约Demo地址
https://github.com/loglos/web3-hpb-test.git
2 、开发智能合约
2.1 环境准备
2.1.1 通过在线Remix在线编译器进行智能合约开发
请访问链接进行智能合约开发http://remix.ethereum.org具体智能合约的开发请见第3章节。
2.1.2 通过下载Remix开源代码搭建本地开发环境
下载地址:https://github。com/ether eum/remix-ide
安装步骤
安装新公共管理和node.js(参见https://份文件。npmjs。com/getting-started/installing-node),然后安装do:
混合集成电路已经作为新公共管理模块:发布
新公共管理安装remix-ide -gremix-ideOr如果要克隆开源代码库库(需要先安装wget):
饭桶克隆https://github.com/ethereum/remix-ide.gitgit克隆https://github.com/ethereum/remix.git #仅当你计划链接再搅拌和混合集成电路库并在其上开发时cd remix-idenpm installnpm运行setupremix #仅当你计划链接再搅拌和混合集成电路库并在其上开发时npm启动DEVELOPING:
运行npm开始并在浏览器中打开http://127 .0 .0 .33608080。
然后打开你的文本编辑器,开始开发。保存文件时,浏览器会自动刷新。
大部分时间与其他模块(如调试器等)一起工作。)托管在再搅拌存储库中。
疑难排解建置如果您在建置套件:时遇到问题,请考虑一些事项
确保您拥有正确版本的节点、npm和nvm .您可以通过查看构建结果中的日志来找到在特拉维斯CI上测试的版本Run:
节点版本下午
--versionnvm --versionIn Debian based OS such as Ubuntu 14.04LTS you may need to run apt-get install build-essential. After installing build-essential run npm rebuild.Unit Testing Register new unit test files in test/index.js. The tests are written using tape.
Run the unit tests via: npm test
For local headless browser tests run npm run test-browser (requires Selenium to be installed - can be done with npm run selenium-install)
Running unit tests via npm test requires at least node v7.0.0
Browser Testing
To run the Selenium tests via Nightwatch serve the app through a local web server:
npm run serve # starts web server at localhost:8080Then you will need to either:
1.Have a Selenium server running locally on port 4444.
Run: npm run test-browser2.Or, install and run SauceConnect.
Run: sc -u <USERNAME> -k <ACCESS_KEY> (see .travis.yml for values)Run: npm run browser-test-scUsage as a Chrome Extension
If you would like to use this as a Chrome extension, you must either build it first or pull from the gh-pages branch, both described above. After that, follow these steps:
1.Browse to chrome://extensions/2.Make sure 'Developer mode' has been checked3.Click 'Load unpacked extension...' to pop up a file-selection dialog4.Select your remix-ide folderDocumentation
To see details about how to use Remix for developing and/or debugging Solidity contracts, please see our documentation page https://remix.readthedocs.io/en/latest/
2.2 使用Remix进行开发智能合约
2.2.1 合约准备
这里提供了一份发行ERC20标准token的合约代码。见文章末尾附件(1)
打开solidity智能合约在线编译器或者本地编辑器:
https://remix.ethereum.org/#optimize=true&version=soljson-v0.4.11+commit.68ef5810.js
粘贴代码到编辑器中,选择编译器编译版本为合约代码中指定的版本。
2.2.2 重新编译ERC20 Token源代码
重新编译ERC20源代码,然后复制编译通过的代码的ABI数据,为后续部署到HPB主链做准备。
点击查看详情。
2.2.3 发布合约到HPB主链
发布合约到HPB主链,有两种方式,一种是通过HPB官方提供JAVA SDK来发布;一种是通过安装节点后通过命令行来发布。
2.2.3.1 准备环境
通过JAVA SDK发布智能合约教程,请见HPB主网最佳实践之JAVA版本。
请查看Java最佳实践这篇文章
演示工程代码下载地址: https://github.com/loglos/web3-hpb-test.git
这里提醒开发者,需要新建合约对应的智能合约代码对应的 “.abi”和 “.bin” 文件并把以上复制的abi和bin代码复制到对应的该文件中。这里再简单讲解下关键代码。
源码工程下载后需要转为maven类型工程,注意在pom.xml文件中,引入了JAVA SDK的包。这里提醒下,请使用最新的2.0.3版本,修复了一些生成Java代码的缺陷。
<dependency><groupId>io.hpb.web3</groupId><artifactId>web3-hpb</artifactId><version>2.0.3</version></dependency>2.2.3.2 演示源码说明
演示的程序在这个package下面
package io.hpb.web3.test;ackage里面演示的Demo java类这里进行下说明:
UFOToken.sol:本次演示的ERC20的智能合约源码。
UFOToken.abi:智能合约编译后的abi源码。
UFOToken.bin:智能合约编译后的bin文件。
UFOTokenRun.java:Java生成智能合约对象类的执行类。
UFOToken.java:系统自动生成的智能合约映射的java源码。
2.2.3.3 UFOTokenRun执行说明
开发者可以直接执行UFOTokenRun.java里面的main方法,来进行调试。其中相关的地址的路径,请根据自身的开发环境的实际调用地址和HPB账号信息填写。
package io.hpb.web3.test;
import java.math.BigDecimal;import java.math.BigInteger;import java.util.Arrays;
import io.hpb.web3.codegen.SolidityFunctionWrapperGenerator;import io.hpb.web3.crypto.Credentials;import io.hpb.web3.crypto.WalletUtils;import io.hpb.web3.protocol.Web3;import io.hpb.web3.protocol.Web3Service;import io.hpb.web3.protocol.admin.Admin;import io.hpb.web3.protocol.core.DefaultBlockParameterName;import io.hpb.web3.protocol.core.methods.response.TransactionReceipt;import io.hpb.web3.protocol.http.HttpService;import io.hpb.web3.tx.ChainId;import io.hpb.web3.tx.RawTransactionManager;import io.hpb.web3.utils.Convert;import okhttp3.OkHttpClient;import io.hpb.web3.abi.datatypes.Address;import io.hpb.web3.abi.datatypes.generated.Uint256;
public class UFOTokenRun {
//发布智能合约账号的,keystore全路径,请根据实际地址来修改 private static String keyStoreAbsolutePath = "/path/UTC--2018-5cb988b9ce48fd3b5a328b582dd64f5c10d0e114"; //发布智能合约的账号密码,请使用你自己的账号和密码 private static String fromPassword = "demo111"; //发布智能合约的地址:这是目前已发布到主网的UFOToken智能合约地址,用户可以进行查询,但是不能转账,转账需要有HPB余额才能转账 private static String address = "0xfbbe0ba33812b531aced666d0bb2450216c11d11"; //开放的HPB节点URL地址,也可以自行搭建节点;此节点是HPB正式网开放的节点 private static String blockChainUrl = "http://pub.node.hpb.io/"; //系统默认参数设置 private static BigInteger GAS_PRICE = new BigInteger("18000000000");
private static BigInteger GAS_LIMIT = new BigInteger("100000000"); public static void main(String<> args) { //指定生成智能合约java映射类的package路径 String packageName = "io.hpb.web3.test"; //指定生成智能合约java映射类源码的本地存放地址 String outDirPath = "//erc20//UFO//java"; //指定智能合约源码的本地地址,这两个文件也放在本类的package下面,读者可以自行处理 String binFilePath = "//erc20//UFO//bin//UFOToken.bin"; String abiFilePath = "//erc20//UFO//bin//UFOToken.abi";
//1、通过io.hpb.web3来生成智能合约sol源码的的映射类;然后把映射的类放到对应的package中 GenContractJavaCode(packageName, binFilePath, abiFilePath, outDirPath) ;
//2、发布智能合约,并获取地址 String address = depolyUFOTokenTest; //3、得到智能合约的余额 getUFOTokenContractBalance; //查询指定地址的erc20余额 String queryAddress = "0xd8ACcED8A7A92b334007f9C6127A848fE51D3C3b"; //4、校验智能合约并打印相关的信息 checkUFOTokenContract(queryAddress); //5、转账 String toAddress = "0x6cb988b9ce48Fd3b5a328B582dd64F5C10d0E114"; transferUFOTokenContract(toAddress,"13333333000000000000000000"); //5.2 查询余额 //checkUFOTokenContract(contractAddress,toAddress); }
/** * 通过智能合约的源码.sol文件和编译后的.bin文件生成java的源码 * String packageName:java源码的packagename * String binFileName:存放智能合约bin文件地址 * String abiFileName:存放智能合约的abi文件地址 * String outDirPath :java源码输出地址 * * **/ public static void GenContractJavaCode(String packageName,String binFilePath,String abiFilePath,String outDirPath) { try { String SOLIDITY_TYPES_ARG = "--solidityTypes"; SolidityFunctionWrapperGenerator.main(Arrays.asList(SOLIDITY_TYPES_ARG, binFilePath,abiFilePath,"-p",packageName, "-o", outDirPath).toArray(new String<0>)); } catch (Exception e) { e.printStackTrace; }
}
/** * 通过编译智能合约源码得到合约映射的java类 * * **/ public static String depolyUFOTokenTest{ Credentials credentials = ; Admin admin = ;
String contractAddress = ""; try{ Web3Service web3Service = new HttpService(blockChainUrl, new OkHttpClient.Builder.build, true); admin = Admin.build(web3Service); credentials = WalletUtils.loadCredentials(fromPassword, keyStoreAbsolutePath); RawTransactionManager transactionManager=new RawTransactionManager(admin, credentials, ChainId.MAINNET);
// 1.发布 TOKEN UFOToken contract = UFOToken.deploy(admin, transactionManager, GAS_PRICE, GAS_LIMIT).send; System.out.println("合约地址:" + contract.getContractAddress); contractAddress = contract.getContractAddress; }catch (Exception e){ e.printStackTrace; } return contractAddress; } /** * 查询余额 * * **/ public static BigDecimal getUFOTokenContractBalance{ Credentials credentials = ; Admin admin = ; BigDecimal balanceWeiAmt = ;
try{ Web3Service web3Service = new HttpService(blockChainUrl, new OkHttpClient.Builder.build, true); admin = Admin.build(web3Service); BigInteger balance = admin.hpbGetBalance(address, DefaultBlockParameterName.LATEST).send.getBalance; balanceWeiAmt = Convert.fromWei(balance.toString, Convert.Unit.HPB); System.out.println(address + "账户余额:" + balanceWeiAmt); }catch (Exception e){ e.printStackTrace; } return balanceWeiAmt; } /** * 校验智能合约并打印地址ERC20余额 * * **/ public static void checkUFOTokenContract(String queryAddress){ Credentials credentials = ; Admin admin = ; try{ Web3Service web3Service = new HttpService(blockChainUrl, new OkHttpClient.Builder.build, true); admin = Admin.build(web3Service); credentials = WalletUtils.loadCredentials(fromPassword, keyStoreAbsolutePath); RawTransactionManager transactionManager=new RawTransactionManager(admin, credentials, ChainId.MAINNET); //检查合约是否可用
UFOToken contract = UFOToken.load(address, admin, transactionManager, GAS_PRICE, GAS_LIMIT); System.out.println("验证合约是否有效:" +contract.isValid ); if(contract.isValid) { BigInteger totalSupply = contract.totalSupply.send.getValue.divide(new BigInteger("1000000000000000000")); System.out.println("UFOtoken总供给量:"+totalSupply); System.out.println(address+" UFOToken余额:"+contract.balanceOf(new Address(address)).sendAsync.get.getValue.divide(new BigInteger("1000000000000000000"))); System.out.println(queryAddress+" UFOToken余额:"+contract.balanceOf(new Address(queryAddress)).sendAsync.get.getValue.divide(new BigInteger("1000000000000000000"))); }
}catch (Exception e){ e.printStackTrace; } } public String toDecimal(int decimal, BigInteger integer) { StringBuffer sbf = new StringBuffer("1"); for (int i = 0; i < decimal; i++) { sbf.append("0"); } String balance = new BigDecimal(integer).divide(new BigDecimal(sbf.toString), 18, BigDecimal.ROUND_DOWN).toPlainString; return balance; }
/** * 转账 * * **/ public static void transferUFOTokenContract(String toAddress,String toValue){ //keystore全路径 Credentials credentials = ; Admin admin = ; try{ Web3Service web3Service = new HttpService(blockChainUrl, new OkHttpClient.Builder.build, true); admin = Admin.build(web3Service); credentials = WalletUtils.loadCredentials(fromPassword, keyStoreAbsolutePath); RawTransactionManager transactionManager=new RawTransactionManager(admin, credentials, ChainId.MAINNET);
// 转账交易 UFOToken contract = UFOToken.load(address, admin, transactionManager, GAS_PRICE, GAS_LIMIT); TransactionReceipt receipt = contract.transfer(new Address(toAddress), new Uint256(new BigInteger(toValue))).send; System.out.println("交易Hash::::::"+receipt.getTransactionHash); }catch (Exception e){ e.printStackTrace; } }
}//指定使用solidity开发语言版本pragma solidity ^0.4.19;/** * @title SafeMath * @dev Math operations with safety checks that throw on error */library SafeMath { //基本的算术方法库 // function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; assert(c / a == b); return c; } //除 function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } //减 function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } //加 function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; }}/** * @title ERC20Basic * @dev Simpler version of ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/179 */contract ERC20Basic { //总供给量,也就是发币总量 uint256 public totalSupply; //查询指定地址余额 function balanceOf(address who) public view returns (uint256); //推荐的转账方法可以安全写入 function transfer(address to, uint256 value) public returns (bool); //记录日志 //address indexed fromaddress indexed to, uint256 value: event Transfer(address indexed from, address indexed to, uint256 value);}/** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */contract ERC20 is ERC20Basic { //, _owner _spender查询能够从地址 _owner 提出的代币数量 function allowance(address owner, address spender) public view returns (uint256); // A-B从地址 _from 转移代币到地址 _to,必须触发 Transfer 事件 function transferFrom(address from, address to, uint256 value) public returns (bool); // _spender _value成功调用 approve 时触发 function approve(address spender, uint256 value) public returns (bool); //触发事件记录日志 event Approval(address indexed owner, address indexed spender, uint256 value);}/** * @title Basic token * @dev Basic version of StandardToken, with no allowances. */contract BasicToken is ERC20Basic { //SafeMathLibrary, using SafeMath for uint256; // mapping(address => uint256) balances; //_valuetoken_to /** * @dev transfer token for a specified address * @param _to The address to transfer to. * @param _value The amount to be transferred. */ function transfer(address _to, uint256 _value) public returns (bool) { //require //address(0) 必须是有效地址 require(_to != address(0)); //balances: //msg.sender //发送者余额不为0 require(_value <= balances
http://wangxiaoming.com/
HPB 芯链官网
http://www.hpb.io/
汪晓明
HPB芯链创始人,巴比特专栏作家。十余年金融大数据、区块链技术开发经验,曾参与创建银联大数据。主创区块链教学视频节目《明说》30多期,编写了《以太坊官网文档中文版》,并作为主要作者编写了《区块链开发指南》,在中国区块链社区以ID“蓝莲花”知名。