蚂蚁区块链BaaS平台应用开发指南(四):JavaSDK的接入
2019-07-09 20:42
2026 查看
在尝试本节的样例代码前,需要保证目标智能合约已经按照
蚂蚁区块链BaaS平台应用开发指南(三):从一个简单合约开始中的做法编译部署成功。
基于JavaSDK的接入
在上一节里,我们通过Cloud IDE部署了一个最简单的智能合约,并且通过Cloud IDE成功的调用了合约的方法。拿传统应用的开发来类比,这就像在数据库上增加了一个存储过程,然后通过外部应用来触发这个存储过程的执行。那么,对于区块链来说,外部应用又如何来调用部署好的智能合约?在这一节中,我们将会通过蚂蚁区块链提供到JavaSDK来接入我们的链,然后调用上一节中部署的合约。官网的文档提供了详细的步骤和一个略微有些复杂的Demo,具体信息可以参见这里。
在这篇文章里,我们的目标更简单:接入目标链,调用已部署的合约。
环境准备:证书!
和Cloud IDE一样,一个Java程序要和链进行通信必须满足:(1)客户端能确认链的身份;(2)链能确认调用者的身份。
-
客户端要确认链的身份, 需要提前获取对应的CA证书。
对于Java程序,需要在代码中提供trustCa
文件,trustCa
文件的在链卡片右上角···按钮->下载TrustCA获取。
- 链要能确认客户端的身份,需要客户端提供用户(机构)的×××书
client.crt
和client.key
。 - 为了交易能够验证成功,用户需要拥有发起交易账户的私钥
user.key
。
准备好上述四个文件,在后续的Java工程中,程序需要读取配置上述文件。
再做一次说明,在蚂蚁区块链平台BaaS上,一个用户(机构)可以有多个账户。平台为每一个用户(机构)签发一张×××书(client.crt)和对应的私钥(client.key),用户可以创建多个账户,每个账户有自己的公私钥(user.key, user.pub)。
私钥以及证书的密码一定不要混淆。
开发环境配置
- 下载并解压JavaSDK(当前版本为0.10.2.4.2 )
Java环境要求:JDK 7+,Maven 3.5.4+
- 在 Maven 文件中引入 SDK 包
//安装 SDK 到本地仓库 mvn install:install-file -Dfile=mychainx-sdk-0.10.2.4.2.jar -DgroupId=com.alipay.mychainx -DartifactId=mychainx-sdk -Dversion=0.10.2.4.2 -Dpackaging=jar -DpomFile=pom.xml //安装 Netty 依赖到本地仓库,注意选择对应平台 netty-tcnative-openssl-static 版本,注意修改 classifier,macOS :osx-x86_64、linux:linux-x86_64、windows:windows-x86_64 mvn install:install-file -Dfile=netty-tcnative-openssl-static-2.0.17-Final-mychain-osx-x86_64.jar -DgroupId=io.netty -DartifactId=netty-tcnative-openssl-static -Dversion=2.0.17-Final-mychain -Dpackaging=jar -Dclassifier=<OS-classifiler>
注意按照开发平台修改OS-classifiler
更多信息见官方文档
开始第一个Java程序
- 使用 Intellij IDEA 创建一个基于 Maven 构建的空项目。
- 把第一节中获取到
client.crt
、client.key
、trustCa
及user.key
文件放入到resources
目录中。 - 在
pom.xml
中添加依赖。
<dependencies> <dependency> <groupId>com.alipay.mychainx</groupId> <artifactId>mychainx-sdk</artifactId> <!--请使用最新 SDK 版本 --> <version>0.10.2.4.2</version> </dependency> </dependencies> <build> <extensions> <extension> <groupId>kr.motd.maven</groupId> <artifactId>os-maven-plugin</artifactId> <version>1.6.1</version> </extension> </extensions> </build>
- 新建一个
CallContractDemo
的类,内容参考下面的代码:
import com.alipay.mychain.sdk.api.Mychain; import com.alipay.mychain.sdk.api.request.MychainParams; import com.alipay.mychain.sdk.api.request.contract.CallContractRequest; import com.alipay.mychain.sdk.api.result.MychainBaseResult; import com.alipay.mychain.sdk.config.ISslOption; import com.alipay.mychain.sdk.config.MychainEnv; import com.alipay.mychain.sdk.config.SslBytesOption; import com.alipay.mychain.sdk.domain.account.Identity; import com.alipay.mychain.sdk.message.response.ReplyTransactionReceipt; import com.alipay.mychain.sdk.message.response.Response; import com.alipay.mychain.sdk.network.ClientTypeEnum; import com.alipay.mychain.sdk.tools.codec.CodecTypeEnum; import com.alipay.mychain.sdk.tools.codec.contract.ContractParameters; import com.alipay.mychain.sdk.tools.codec.contract.ContractReturnValues; import com.alipay.mychain.sdk.tools.crypto.KeyLoder; import com.alipay.mychain.sdk.tools.hash.HashTypeEnum; import com.alipay.mychain.sdk.tools.log.LoggerFactory; import com.alipay.mychain.sdk.tools.sign.SignTypeEnum; import com.alipay.mychain.sdk.tools.utils.ByteUtils; import com.alipay.mychain.sdk.tools.utils.Utils; import org.slf4j.Logger; import java.io.InputStream; import java.math.BigInteger; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.security.PrivateKey; import java.util.ArrayList; import java.util.List; import java.util.Random; public class CallContractDemo { private static Mychain sdk; // 配置目标链的接入IP和端口号。 // 以上信息可以通过 BaaS平台 > 区块链卡片 > 详情 > 区块链浏览器 > 节点 找到。 // 参见:蚂蚁区块链BaaS平台应用开发指南(二):准备工作 查看区块链详情 小节 private static String host = "*.*.*.*"; private static int port = 18130; // 设置用户×××书,证书私钥和私钥密码。 private static String keyFilePath = "client.key"; private static String certFilePath = "client.crt"; private static String clientKeyPassword = "****"; // 设置trustCa证书。 // trustStore密码默认为mychain。 private static String trustStoreFilePath = "trustCa"; private static String trustStorePassword = "mychain"; // 设置交易发起者的账户私钥,私钥密码和账户名。 // 可以使用默认账户,即申请证书步骤一并申请的账户 private static String userPrivateKeyFile = "user.key"; private static String userPassword = "****"; private static String accountName = "Your Account Name"; // 设置调用合约名 // 合约名需要是第三节中所部署的合约名称 private static String targetContractName = "Target Contract Name"; private static MychainEnv env; private static void initMychainEnv() { // 默认值,不用修改 env = buildMychainEnv("test_sdk"); } private static void initLogger() { Logger logger = org.slf4j.LoggerFactory.getLogger(CallContractDemo.class); LoggerFactory.setInstance(logger); } private static MychainEnv buildMychainEnv(String identity) { InetSocketAddress inetSocketAddress = InetSocketAddress.createUnresolved(host, port); // build ssl option // 构建SSL选项 ISslOption sslOption = new SslBytesOption.Builder() .keyBytes(Utils.readFileToByteArray(CallContractDemo.class.getClassLoader().getResource(keyFilePath).getPath())) .certBytes(Utils.readFileToByteArray(CallContractDemo.class.getClassLoader().getResource(certFilePath).getPath())) .keyPassword(clientKeyPassword) .trustStorePassword(trustStorePassword) .trustStoreBytes(Utils.readFileToByteArray(CallContractDemo.class.getClassLoader().getResource(trustStoreFilePath).getPath())) .build(); // 可将同一条链的不同接入节点放入备份节点中。 List<SocketAddress> backupNodes = new ArrayList<SocketAddress>(); // backupNodes.add(InetSocketAddress.createUnresolved("47.100.27.162", 18130)); // backupNodes.add(InetSocketAddress.createUnresolved("106.14.218.105", 18130)); // backupNodes.add(InetSocketAddress.createUnresolved("47.100.115.103", 18130)); HashTypeEnum hashType = HashTypeEnum.SHA256; SignTypeEnum signType = SignTypeEnum.ECDSA; return MychainEnv.build(identity, ClientTypeEnum.TLS, hashType, signType, CodecTypeEnum.RLP, inetSocketAddress, sslOption, backupNodes); } private static void initSdk() { sdk = new Mychain(); MychainBaseResult<Response> initResult = sdk.init(env); if (!initResult.isSuccess()) { System.out.println("initSdk: sdk init failed."); } } public static void main(String[] args) throws Exception{ initLogger(); // 初始化环境。 initMychainEnv(); // 初始化SDK。 initSdk(); // Get the Identity of my account. // 获取账户的Identity Identity userIdentity = Utils.getIdentityByName(accountName, env); // Get the private key of the account. // 获取账户的私钥 // 注意检查userPrivateKeyFileInputStream是否为空。 InputStream userPrivateKeyFileInputStream = CallContractDemo.class.getClassLoader().getResourceAsStream(userPrivateKeyFile); PrivateKey userPrivateKey = KeyLoder.getPrivateKeyFromPKCS8(userPrivateKeyFileInputStream,userPassword); // Add the key to the key list. Note: in this demo, the account only have one single private key. // 把私钥添加到一个私钥列表中。注:本例中,账户只有一把私钥。 ArrayList<PrivateKey> userPrivateKeyArrayList = new ArrayList<PrivateKey>(); userPrivateKeyArrayList.add(userPrivateKey); // Build a CallContract request // Build a CallContract request - 1: build Mychain parameters // 构造CallContrace请求 - 1: 构造Mychain参数。 MychainParams mychainParams = new MychainParams.Builder() .gas(BigInteger.valueOf(4000000)) .privateKeyList(userPrivateKeyArrayList) .build(); // Build a CallContract request - 2: get the identity of the contract // 构造CallContrace请求 - 2: 获取合约Identity。 // 通过合约名获取合约实例的Identity,合约名和上一节中通过Cloud IDE部署的合约名一致。 Identity contractIdentity = Utils.getIdentityByName(targetContractName,env); // Build a CallContract request - 3: build contract parameters // 构造CallContrace请求 - 3: 构造调用方法参数。这里设置的是需要调用的方法名。 ContractParameters crtParameters = new ContractParameters("get()"); // Build a CallContract request - 4: build CallContract request // 构造CallContrace请求 - 4: 构造请求 CallContractRequest request = CallContractRequest.build( userIdentity, contractIdentity, crtParameters, BigInteger.ZERO, mychainParams ); // Call the contract // 调用合约 MychainBaseResult<ReplyTransactionReceipt> replyTransactionReceipt = sdk.getContractService().callContract(request); // Get the returned value from the raw bytes output. // 获取合约返回数据,并从返回的字节码中获取数据 String rawOutput = ByteUtils.toHexString(replyTransactionReceipt.getData().getTransactionReceipt().getOutput()); ContractReturnValues contractReturnValues = new ContractReturnValues(rawOutput); // 我们事先知道get方法会返回一个Uint类型的值 BigInteger storedData = contractReturnValues.getUint(); System.out.println("Returned storedData from the contract is: " + storedData); System.out.println("======================================"); System.out.println("Let us set a new value."); // Build anther CallContract request // 构造另外一个合约调用请求。 // 构造调用方法参数,这次我们需要调用set方法 ContractParameters crtParameters2 = new ContractParameters("set(uint256)"); // Give a random Integer // 设置一个随机数。 Random r = new Random(); Integer value = r.nextInt(100); // 设置set方法的传入参数值。 crtParameters2.addUint(BigInteger.valueOf(value)); // Build a CallContract request // 构造请求 CallContractRequest request2 = CallContractRequest.build( userIdentity, contractIdentity, crtParameters2, BigInteger.ZERO, mychainParams ); // Call the contract // 调用合约 MychainBaseResult<ReplyTransactionReceipt> replyTransactionReceipt2 = sdk.getContractService().callContract(request2); if (replyTransactionReceipt2.isSuccess()){ System.out.println("New value of the storedData is: " + value); }else{ System.out.println("Failed to set the value:" + value); } sdk.shutDown(); } }
上述代码中,我们通过Java程序来调用上一节中部署好的合约:
- 调用get方法获取storedData;
- 调用set方法给storedData设置一个新的值。
按需修改的配置项:
clientKeyPassword:用户证书密钥(client.key)的密码
userPassword:账户私钥(user.key)的密码
accountName:账户名
contractName:要调用的目标合约名
trustStorePassword:目前默认为
mychain,没有特殊说明不用修改。
host和
port:链的接入节点 IP和端口号。可通过
区块链卡片>
详情>
节点处获取,参见下图。选一个IP填入即可,其他的节点IP可以放入
backupNodes中。
程序运行结果
- 第一次运行:
Returned storedData from the contract is: 77 ====================================== Let us set a new value. New value of the storedData is: 16
- 第二次运行:
Returned storedData from the contract is: 16 ====================================== Let us set a new value. New value of the storedData is: 86
小结
至此,Java代码成功的调用到了链上合约的方法,并正确获取到了返回值。可以看到,调用一个合约的主要步骤为
- 初始化MychainEnv(配置好环境参数和客户端证书)
- 初始化MychainParams(配置好调用请求发起者的私钥列表)
- 构造ContractParameters(构造目标方法的签名,添加传入参数)
- 构造CallContractRequest(提供调用者ID,合约ID,环境参数,链的参数)
- 发起合约调用请求
- 解析调用结果(调用结果在ReplyTransactionReceipt中)
相关文章推荐
- Java Web应用快速开发平台OpenJWeb(v1.6)增删改查页面生成器操作手册
- openjweb1.8 java web应用快速开发平台产品白皮书
- JAVA微信开发-测试号接入指南
- OpenJWeb java web应用快速开发平台增删改查视频发布
- Java应用级产品开发平台APDPlat
- JAVA微信开发-新手接入指南
- JAVA项目接入腾讯应用宝YSDK平台之QQ微信登录接入模式详解
- openjweb1.8 java web应用快速开发平台产品白皮书
- 【淘宝网开放平台】_JAVA版本SDK开发包(闪电网提供)
- 手把手教您开发JAVA微信SDK-新手接入
- 微博开发平台java SDK demo学习之examples(demo)
- JAVA微信开发-新手接入指南
- Serverless 应用开发指南:基于 Serverless 与 Lambda 的微信公共平台
- AWS SDK for Java 的使用(适用于 Java 的 AWS 开发工具包开发人员指南)之配置aws凭证
- 2006-2007年度JAVA平台开发工具的应用状况
- OpenJWeb(1.8) Java Web应用快速开发平台
- DB2 9 应用开发(733 检验)认证指南,第 7 部分: Java 编程(3)
- TI DSP平台(Sitara系列ARM,ARM+DSP的Integra,Davinci DM系列)以及芯片应用手册开发指南
- Unity3d与iOS交互开发——接入平台SDK必备技能
- Unity3d与iOS交互开发——接入平台SDK必备技能