蚂蚁区块链BaaS平台应用开发指南(四):JavaSDK的接入

在尝试本节的样例代码前,需要保证目标智能合约已经按照蚂蚁区块链BaaS平台应用开发指南(三):从一个简单合约开始中的做法编译部署成功。

基于JavaSDK的接入

在上一节里,我们通过Cloud IDE部署了一个最简单的智能合约,并且通过Cloud IDE成功的调用了合约的方法。拿传统应用的开发来类比,这就像在数据库上增加了一个存储过程,然后通过外部应用来触发这个存储过程的执行。那么,对于区块链来说,外部应用又如何来调用部署好的智能合约?在这一节中,我们将会通过蚂蚁区块链提供到JavaSDK来接入我们的链,然后调用上一节中部署的合约。官网的文档提供了详细的步骤和一个略微有些复杂的Demo,具体信息可以参见这里
在这篇文章里,我们的目标更简单:接入目标链,调用已部署的合约。

环境准备:证书!

和Cloud IDE一样,一个Java程序要和链进行通信必须满足:(1)客户端能确认链的身份;(2)链能确认调用者的身份。

  • 客户端要确认链的身份, 需要提前获取对应的CA证书。
    对于Java程序,需要在代码中提供trustCa文件,trustCa文件的在链卡片右上角···按钮->下载TrustCA获取。
  • 链要能确认客户端的身份,需要客户端提供用户(机构)的×××书 client.crtclient.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程序

  1. 使用 Intellij IDEA 创建一个基于 Maven 构建的空项目。
  2. 把第一节中获取到client.crtclient.keytrustCauser.key 文件放入到resources 目录中。
  3. 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>
  1. 新建一个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程序来调用上一节中部署好的合约:

  1. 调用get方法获取storedData;
  2. 调用set方法给storedData设置一个新的值。

按需修改的配置项:


clientKeyPassword:用户证书密钥(client.key)的密码
userPassword:账户私钥(user.key)的密码
accountName:账户名
contractName:要调用的目标合约名
trustStorePassword:目前默认为mychain,没有特殊说明不用修改。
hostport:链的接入节点 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中)

原文地址:https://blog.51cto.com/14321927/2418712

时间: 2024-10-29 09:01:28

蚂蚁区块链BaaS平台应用开发指南(四):JavaSDK的接入的相关文章

蚂蚁区块链BaaS平台应用开发指南(一):前言

前言 2018年疯狂的币市把区块链技术推到了风口浪尖,随着潮水的退去,越来越多的技术人和市场开始沉淀到实际的问题上.如何利用区块链技术带来的新特性(去中心,可追溯,不可篡改等)去解决现实世界的实际需求成为了探讨的热点.蚂蚁金服作为区块链技术的先锋,已经陆陆续续的推出了十大解决方案和对应的落地案例如下: 司法区块链 合同存证 供应链金融 电子票据 商保快赔 处方流转 智慧租房 通用溯源 安全多方计算解决方案(联合营销,联合风控) 跨境支付 未来,会有越来越多的传统行业和新兴业务拥抱区块链技术.可以

蚂蚁区块链BaaS平台应用开发指南(二):准备工作

准备工作 获取到开发者资格后,开发者可以到新手引导板块进行区块链接入开发的准备工作:证书密钥的准备 以及 环境和工具的熟悉. 如果是BaaS平台上拥有独立联盟链的开发者,请到我的联盟中查看所拥有到链:如果是区块链创新大赛到参与开发者,请到区块链创新大赛板块中查看所提供的链. 蚂蚁区块链目前提供存证链和合约链两种类型的链,链的差异主要体现在SDK和API上.合约链的SDK和API更丰富,同时也提供了存证链接口,可以认为合约链是存证链的超集.因此,本博客将主要介绍基于合约链的开发. 证书申请 新手引

蚂蚁区块链BaaS平台应用开发指南(三):从一个简单合约开始

Could IDE的入口 新版的Cloud IDE已经去除证书配置的要求,开发者开通区块链之后可直接开始智能合约的开发.在本节中,我们将会使用Could IDE来进行合约的编写.编译和调试的工作.如果是体验链,请在新手引导引导界面,找到合约体验链卡片,点击调试合约. 如果是正式的托管链或区块链创新大赛的链,通过合约管理>新建工程或编辑已有工程进入Cloud IDE. 从一个最简单的合约开始 选择目标链 编译部署合约前,要指定好所要部署的链以及部署用的账户:在右边栏中,点击环境配置: 在本例中,选

plustoken钱包系统开发plustoken区块链钱包平台商城开发

plus Token的高附加值,科技与区块链完美融合,成就通证经济时代杰出代表.每季度利润30%用于回购plus进行销毁,销毁过程在区块链上进行,公开不可逆,直到总量控制在1亿枚.plustoken钱包系统开发plustoken区块链钱包平台商城开发[孙女士 微电: 18*6156*140*62 扣扣:28*203*087*34]非平台客服,玩家勿扰!!! 非平台客服,玩家勿扰!!! 非平台客服,玩家勿扰!!! 每天中午12点结算利息和奖金,钱包货币可随时兑换eth转出提现有短信验证提醒,一次性

蚂蚁区块链平台BaaS技术解析与实践

摘要: 以"数字金融新原力(The New Force of Digital Finance)"为主题,蚂蚁金服ATEC城市峰会于2019年1月4日在上海如期举办.在ATEC区块链行业研讨会分论坛上,蚂蚁金服区块链BaaS技术总监李书博做了主题为<BaaS入门到精通:区块链技术如此简单>的精彩分享. 演讲中,李书博首先从技术方面介绍了蚂蚁区块链BaaS平台,随后从实践的角度介绍了客户如何快速地实现上链,最后带领大家一起详细地了解了平台的合作服务流程. 李书博 蚂蚁金服区块链

深圳区块链Baas企业应用解决方案服务平台

区块链作为2018年热潮之一,各界领头企业都在布局区块链生态.BAT这些互联网巨头公司也前前后后推出各种区块链产品.但最瞩目的还是区块链BaaS系统.区块链Baas即区块链服务. 区块链服务是指根据公链提供的基础设施开发公链应用,并运行应用提供服务..目前在区块链领域,只有各大公链的区块浏览器能称之为区块链服务,以及部分公链衍生应用:存证型-Factom,数字身份型-uPort等. 通俗一点讲,区块链服务就是在公链的基础上开发应用,比如在以太坊上开发DAPP.区块链BAAS系统可以让用户在此平台

网络支付区块链开发,搭建区块链支付平台

随着区块链支付的发展,区块链分布式账本技术将区块链上数字资.产流动与现实的现.金支付相连接.在全球互联网市场上能够发挥出传统金融机构无法替代的高效率.低成本的价值传递的作用.每个人的密码钱.包就可以发展成一个"自.金.融"平台,可以用于P.2.P 的支.付.存.款.转.账.换.汇.借.贷以及全.网.记.账.清.算.支付是金.融交易中必不可少的一个环节.在信息技术和金融业务不断融合的大趋势下,网络支付体系和网络支付的方法发生了深刻的变化,网络支付的理论和实践正在向泛中心化的网络支付聚焦,

四大区块链开源平台对比,中国BCOS如何立足?

近年来,以多方参与.智能协同.与价值分享等为主要特征的分布式商业逐渐兴起,区块链和分布式账本等技术实现了分布式商业中的对等.共享与透明规则,逐渐获得认可,并成为了前沿金融科技的核心代表,同时也越来越受到政府机关和国际组织的重视.在国务院印发的<"十三五"国家信息化规划>中,区块链技术亦被首次列入,迎来重大的发展机遇. 2017年7月31日,深圳前海微众银行.上海万向区块链股份公司.矩阵元技术(深圳)有限公司联合宣布将三方研发的区块链底层平台BCOS(BlockChainOp

云计算+区块链=BaaS

云计算+区块链=BaaS 本文来自于:https://www.toutiao.com/i6540096399017509389/ 云计算和区块链的区别 云计算现在已经是一个成熟的技术和应用了,美国国家标准与技术研究院(NIST)给出的定义是:云计算是一种按使用量付费的模式,这种模式提供可用的.便捷的.按需的网络访问, 进入可配置的计算资源共享池(资源包括网络.服务器.存储.应用软件.服务),这些资源能够被快速提供,只需投入很少的管理工作,或与服务供应商进行很少的交互.这个解释还是全面和易懂的,千