3.快速接入星火链

本章教大家使用Java SDK 在体验网上部署、调用、 查询一个JavascriptSolidity智能合约,需要完成如下工作:

  1. 获取星火令来激活账户

  2. Java sdk的初始化

  3. 通过体验网部署合约

3.1 SDK使用

下载SDK源码或者使用maven引入SDK.

  1. 下载 Java SDK

​ 到 https://github.com/caict-4iot-dev/BIF-Core-SDK下载java版本的SDK。

  1. maven 引入 Java SDK

    <dependency>
        <groupId>bif.chain</groupId>
        <artifactId>bif-chain-sdk</artifactId>
        <version>1.0.2</version>
    </dependency>
    

3.1.1 账号创建

调用JavaSDK接口离线创建一个账户。

import cn.bif.model.crypto.KeyPairEntity;
KeyPairEntity entity = KeyPairEntity.getBidAndKeyPair();                //离线创建一个新账号
System.out.printf("BID address %s\n", entity.getEncAddress());          //账户地址, 可以公开
System.out.printf("privatekey %s\n", entity.getEncPrivateKey());      //账户私钥, 请妥善保管

3.2 获取星火令

账户需要拥有星火令才能正常使用星火链功能, 体验网星火令可以通过如下方式获取:

​ (1)通过星火插件钱包申请可信凭证获取100星火令

​ (2)星火可信企业可登录服务平台申请星火令

星火插件钱包下载地址:https://bitfactory.cn/szsf.html

服务平台体验网地址:http://test-bj-baas.bitfactory.cn

3.3 初始化SDK

通过配置星火链RPC地址连接SDK到星火链, 本次demo里链接到星火链体验网。

import cn.bif.api.BIFSDK;

public static final String NODE_URL = "http://test.bifcore.bitfactory.cn";  //星火链体验网RPC地址

BIFSDK sdk = BIFSDK.getInstance(NODE_URL);

3.4 查看账户状态

//构建查看账户请求
BIFAccountGetInfoRequest infoReq = new BIFAccountGetInfoRequest();
//要查看账户的地址
infoReq.setAddress(address);

//发出查询请求
BIFAccountGetInfoResponse infoRsp = sdk.getBIFAccountService().getAccount(infoReq);

if (infoRsp.getErrorCode() == 0) {
    //查询成功
    System.out.println(JsonUtils.toJSONString(infoRsp.getResult()));
} else {
    //查询失败
    System.out.println(infoRsp.getErrorDesc());
}

注意, 新创建的空白账户查询会失败, 需要转入星火令激活才能正常使用。

正常账户查询返回示例:

{
    "address":"did:bid:efKkF5uKsopAishxkYja4ULRJhrhrJQU",    //账户地址
    "balance":10000000000,                                   //账户余额
    "nonce":0                                                //账户发出的交易计数
}

3.5 部署调用一个Js合约

3.5.1 智能合约代码

  • Javascript智能合约代码如下:

    "use strict";
    
    function queryById(id) {                        //合约内部函数
        let data = Chain.load(id);
        return data;
    }
    
    function query(input) {                         //合约查询入口
        input = JSON.parse(input);
        let id = input.id;
        let object = queryById(id);
        return object;
    }
    
    function main(input) {                          //合约调用入口
        input = JSON.parse(input);
        Chain.store(input.id, input.data);
    }
    
    function init(input) {                          //初始化函数
        return;
    }
    

    该合约实现了一个简单的存储功能, 用户可以调用main接口存储自定义Key-Value信息, 然后通过query接口查询已经存入的Key-Value信息。

3.5.2 部署合约

合约编写完毕后, 需要将合约部署到链上, 注意这里需要账户内有足够的XHT, 部署代码如下:

// 初始化参数
String senderAddress = "did:bid:efzE8AcDgWUeNbgujA5hK3oUeuG9k19b";//用户在上面生成的账户地址
String senderPrivateKey = "priSPKiwJR7PwjN3bGPRvXRoC6Dny1H5H3kASHgTEugjtUDknW";//用户在上面生成的私钥
//合约代码,注意转义
String payload = "\"use strict\";function queryById(id) {    let data = Chain.load(id);    return data;}function query(input) {    input = JSON.parse(input);    let id = input.id;    let object = queryById(id);    return object;}function main(input) {    input = JSON.parse(input);    Chain.store(input.id, input.data);}function init(input) {    return;}";
Long initBalance = ToBaseUnit.ToUGas("0");

BIFContractCreateRequest request = new BIFContractCreateRequest();
request.setSenderAddress(senderAddress);
request.setPrivateKey(senderPrivateKey);
request.setInitBalance(initBalance);
request.setPayload(payload);
request.setRemarks("create contract");
//type,javascript合约type为0,solidity合约type为1
request.setType(0);
request.setFeeLimit(100060000L);

// 调用contractCreate接口
BIFContractCreateResponse response = sdk.getBIFContractService().contractCreate(request);
if (response.getErrorCode() == 0) {
    System.out.println(JsonUtils.toJSONString(response.getResult()));
} else {
    System.out.println(JsonUtils.toJSONString(response));
}

如果部署成功, 调用返回里会拿到这个交易的hash:

{
      "hash":"92313f8fb30b042a4d3f4d276ee2d4ce59f21994deed53e6cc6e2a2dcbe61f8f"
}
  • 交易信息和合约地址查询

    用SDK查询部署合约的交易详细信息, 可以从中获取到创建的合约地址。

    //部署返回hash
    String hash = "92313f8fb30b042a4d3f4d276ee2d4ce59f21994deed53e6cc6e2a2dcbe61f8f";
    BIFContractGetAddressRequest request = new BIFContractGetAddressRequest();
    request.setHash(hash);
    
    // 调用接口
    BIFContractGetAddressResponse response = sdk.getBIFContractService().getContractAddress(request);
    if (response.getErrorCode() == 0) {
        System.out.println(JsonUtils.toJSONString(response.getResult()));
    } else {
        System.out.println(JsonUtils.toJSONString(response));
    }
    

    合约部署信息示例如下:

    {
        "contract_address_infos":[
            {
                "contract_address":"did:bid:ef6ZAWV315UracvhKARdHz5CbQ6dULPp",
                "operation_index":0
            }
        ]
    }
    

    did:bid:ef6ZAWV315UracvhKARdHz5CbQ6dULPp即为刚刚创建的合约链上地址。

3.5.3 合约调用

合约成功部署并且获取到合约地址后, 就可以通过SDK发送交易调用合约接口, 我们存储一个Key-Value对到合约里:

调用合约input如下

{
    "id":"test",
    "data": "test"
}

调用合约代码如下:

// 初始化参数
String senderAddress = "did:bid:efzE8AcDgWUeNbgujA5hK3oUeuG9k19b";
String contractAddress = "did:bid:ef6ZAWV315UracvhKARdHz5CbQ6dULPp";
String senderPrivateKey = "priSPKiwJR7PwjN3bGPRvXRoC6Dny1H5H3kASHgTEugjtUDknW";
Long amount = 0L;
String input = "{\"id\":\"test\", \"data\": \"test\"}";
BIFContractInvokeRequest request = new BIFContractInvokeRequest();
request.setSenderAddress(senderAddress);
request.setPrivateKey(senderPrivateKey);
request.setContractAddress(contractAddress);
request.setBIFAmount(amount);
request.setRemarks("contract invoke");
request.setInput(input);

// 调用contractInvoke 接口
BIFContractInvokeResponse response = sdk.getBIFContractService().contractInvoke(request);
if (response.getErrorCode() == 0) {
    System.out.println(JsonUtils.toJSONString(response.getResult()));
} else {
    System.out.println(JsonUtils.toJSONString(response));
}

调用成功后,我们会得到调用交易的hash:

{
    "hash":"2fcb88fcab04af78e4d06ec263d462b2aacd00d9405159725918d9ca8144ea5d"
}

3.5.4 查询合约

不同于调用合约, 查询合约为只读操作, 因此不需要发出上链交易和耗费gas, 这里我们查询刚刚设置的key, 查询input为:

{
    "id":"test"
}

Java查询代码如下:

// 初始化参数
String contractAddress = "did:bid:ef6ZAWV315UracvhKARdHz5CbQ6dULPp";
String callInput = "{\"id\":\"test\"}";
BIFContractCallRequest request = new BIFContractCallRequest();
request.setContractAddress(contractAddress);
request.setInput(callInput);

// 调用contractQuery接口
BIFContractCallResponse response = sdk.getBIFContractService().contractQuery(request);
if (response.getErrorCode() == 0) {
    BIFContractCallResult result = response.getResult();
    System.out.println(JsonUtils.toJSONString(result));
} else {
    System.out.println(JsonUtils.toJSONString(response));
}

查询的返回如下:

{
    "query_rets":[
        {
            "result":
                {
                    "type":"string",
                    "value":"test"
                }
        }
    ]
}

3.6部署调用一个 Solidity合约

3.6.1 智能合约代码

  • Solidity智能合约代码如下:

    pragma solidity ^0.4.26;
    
    contract demo  {
    
      mapping(uint256 => string) private _datas;
    
      function queryById(uint256 id) public view returns (string) {                      
        
        return _datas[id];
      }
    
      function setById(uint256 id, string data) public {                      
        
        _datas[id] = data;
      }
    
    }
    

    该合约实现了一个简单的存储功能, 用户可以调用setById接口存储自定义Key-Value信息, 然后通过queryById接口查询已经存入的Key-Value信息。

3.6.2 部署合约

合约编写完毕后, 需要将合约部署到链上, 注意这里需要账户内有足够的XHT, 部署代码如下:

solidity智能合约和Javascript智能合约的部署,区别在于:

type的设置:0代表Javascript智能合约,1代表solidity智能合约。

setPayload时,设置的不是solidity智能合约代码本身,而是对合约代码进行编译之后,得到的bytecode中的object值。可以参考如何进行智能合约开发 - 3.Solidity合约介绍章节。

// 初始化参数
String senderAddress = "did:bid:efzE8AcDgWUeNbgujA5hK3oUeuG9k19b";
String senderPrivateKey = "priSPKiwJR7PwjN3bGPRvXRoC6Dny1H5H3kASHgTEugjtUDknW";
//合约代码
String payload = "608060405234801561001057600080fd5b5061031a806100206000396000f30060806040526004361061004c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063794bde4814610051578063baf8baab146100c4575b600080fd5b34801561005d57600080fd5b506100c260048036038101908080359060200190929190803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061016a565b005b3480156100d057600080fd5b506100ef60048036038101908080359060200190929190505050610195565b6040518080602001828103825283818151815260200191508051906020019080838360005b8381101561012f578082015181840152602081019050610114565b50505050905090810190601f16801561015c5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b806000808481526020019081526020016000209080519060200190610190929190610249565b505050565b60606000808381526020019081526020016000208054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561023d5780601f106102125761010080835404028352916020019161023d565b820191906000526020600020905b81548152906001019060200180831161022057829003601f168201915b50505050509050919050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061028a57805160ff19168380011785556102b8565b828001600101855582156102b8579182015b828111156102b757825182559160200191906001019061029c565b5b5090506102c591906102c9565b5090565b6102eb91905b808211156102e75760008160009055506001016102cf565b5090565b905600a165627a7a723058208e0076d636eb85f3b445a4b8d3f0f3e2583ada8d6501f2839586d782687926860029";
Long initBalance = ToBaseUnit.ToUGas("0");

BIFContractCreateRequest request = new BIFContractCreateRequest();
request.setSenderAddress(senderAddress);
request.setPrivateKey(senderPrivateKey);
request.setInitBalance(initBalance);
request.setPayload(payload);
request.setRemarks("create contract");
//type,javascript合约type为0,solidity合约type为1
request.setType(1);
request.setFeeLimit(100189100L);//根据实际情况调整

// 调用contractCreate接口
BIFContractCreateResponse response = sdk.getBIFContractService().contractCreate(request);
if (response.getErrorCode() == 0) {
    System.out.println(JsonUtils.toJSONString(response.getResult()));
} else {
    System.out.println(JsonUtils.toJSONString(response));
}

如果部署成功, 调用返回里会拿到这个交易的hash:

{
    "hash":"d525a576173e2606acb33103eca5e3cc5c0106c67833a2b75d67d3164cec6b12"
}
  • 交易信息和合约地址查询

//部署返回hash
String hash = "d525a576173e2606acb33103eca5e3cc5c0106c67833a2b75d67d3164cec6b12";
BIFContractGetAddressRequest request = new BIFContractGetAddressRequest();
request.setHash(hash);

BIFContractGetAddressResponse response = sdk.getBIFContractService().getContractAddress(request);
if (response.getErrorCode() == 0) {
    System.out.println(JsonUtils.toJSONString(response.getResult()));
} else {
    System.out.println(JsonUtils.toJSONString(response));
}

可以查询到刚刚创建的合约链上地址: did:bid:ef91sQXEXVfPFQXRZpgA3Rs1pTNbZndq

3.6.3 合约调用

合约成功部署并且获取到合约地址后, 就可以通过SDK发送交易调用合约接口, 我们存储一个Key-Value对到合约里:

调用合约input如下

{
    "id":123,
    "data": "abc"
}

调用合约代码如下:

// 初始化参数
String senderAddress = "did:bid:efzE8AcDgWUeNbgujA5hK3oUeuG9k19b";
String contractAddress = "did:bid:ef91sQXEXVfPFQXRZpgA3Rs1pTNbZndq";
String senderPrivateKey = "priSPKiwJR7PwjN3bGPRvXRoC6Dny1H5H3kASHgTEugjtUDknW";
Long amount = 0L;
String input = "{\"function\":\"setById(uint256,string)\", \"args\":\"123,'abc'\"}";
BIFContractInvokeRequest request = new BIFContractInvokeRequest();
request.setSenderAddress(senderAddress);
request.setPrivateKey(senderPrivateKey);
request.setContractAddress(contractAddress);
request.setBIFAmount(amount);
request.setRemarks("contract invoke");
request.setInput(input);

// 调用contractInvoke 接口
BIFContractInvokeResponse response = sdk.getBIFContractService().contractInvoke(request);
if (response.getErrorCode() == 0) {
    System.out.println(JsonUtils.toJSONString(response.getResult()));
} else {
    System.out.println(JsonUtils.toJSONString(response));
}

调用成功后,我们会得到调用交易的hash:

{
    "hash":"b7b0a8f8415ce8cf17627eb3da75f565e0e7cf9b54217121afce4fab2df52080"
}

3.6.4 查询合约

不同于调用合约, 查询合约为只读操作, 因此不需要发出上链交易和耗费gas, 这里我们查询刚刚设置的key, 查询input为:

{
    "id":123
}

Java查询代码如下:

//合约调用
String contractAddress = "did:bid:ef91sQXEXVfPFQXRZpgA3Rs1pTNbZndq";
String callInput = "{\"function\":\"queryById(uint256)\",\"args\":123,\"return\":\"returns(string)\"}";
BIFContractCallRequest request = new BIFContractCallRequest();
request.setContractAddress(contractAddress);
request.setInput(callInput);

BIFContractCallResponse response = sdk.getBIFContractService().contractQuery(request);
if (response.getErrorCode() == 0) {
    BIFContractCallResult result = response.getResult();
    System.out.println(JsonUtils.toJSONString(result));
} else {
    System.out.println(JsonUtils.toJSONString(response));
}

查询的返回如下:

{
    "query_rets":[
        {
            "result":{
                "data":"[abc]",
                "gasused":1097
            }
        }
    ]
}

3.7 查看交易信息

每次部署或者调用合约,都会产生相关都交易信息,您可以在区块链浏览器的交易详情里查看具体信息。

体验网浏览器地址:http://test-explorer.bitfactory.cn/

接下来

至此我们就完成了一个链上合约从部署到操作的全过程, 有关合约的更高阶开发和星火链体系模型, 请参见后续专栏。