跳到主要内容

1. 摘要

本文档旨在指导用户如何开发第一个区块链应用;通过环境搭建、编写合约、部署合约、应用开发等步骤将一个简单的积分示例进行讲述。

2. 前提

2.1. 环境要求

3. 应用需求

区块链天然具有防篡改,可追溯等特性,这些特性决定其更容易受金融领域的青睐。本应用中,将会提供一个简易的积分管理的开发示例,并最终实现以下功能:

  • 能够在区块链上发行积分

  • 能够实现不同账户间的积分转移

  • 可以查询账户的积分余额

4. 设计

4.1. 合约设计

在区块链应用开发中,首要任务是根据业务需求设计智能合约。这包括确定合约需要存储的数据以及定义合约对外提供的接口。接下来,需要详细规划每个接口的具体功能和实现方式。这个过程确保了智能合约能够有效地满足业务需求,并提供稳健的功能接口。

  • 接口设计

积分发行在合约创建时进行初始化,合约提供积分转移、积分查询方法,如:

//积分发行
function init();

//积分转移接口
function transfer(to, value);

//积分余额查询接口
function balanceOf(address)
  • 合约示例代码
'use strict';

const CONTRACT_PRE = 'contract_info';
const ATP_PROTOCOL = 'BRC20';

/**
* 转移接口:交易发起人往 to 账户转移 value 数量的积分
* @to:转入账户
* @value:转入数量
*/
function transfer(to, value){
Utils.assert(Utils.addressCheck(to) === true, 'Arg-to is not a valid address.');
Utils.assert(Utils.stoI64Check(value) === true, 'Arg-value must be alphanumeric.');
Utils.assert(Utils.int64Compare(value, '0') > 0, 'Arg-value must be greater than 0.');
Utils.assert(Chain.msg.sender !== to, 'From cannot equal to address.');

let senderValue = Chain.load(Chain.msg.sender);
Utils.assert(senderValue !== false, 'Failed to get the balance of ' + Chain.msg.sender + ' from metadata.');
Utils.assert(Utils.int64Compare(senderValue, value) >= 0, 'Balance:' + senderValue + ' of sender:' + Chain.msg.sender + ' < transfer value:' + value + '.');

let toValue = Chain.load(to);
toValue = (toValue === false) ? value : Utils.int64Add(toValue, value);
Chain.store(to, toValue);

senderValue = Utils.int64Sub(senderValue, value);
Chain.store(Chain.msg.sender, senderValue);

Chain.tlog('Transfer', Chain.msg.sender, to, value);

return true;
}

/**
* 查询余额接口:查询账户 address 的积分余额
* @address:目标地址
*/
function balanceOf(address){
Utils.assert(Utils.addressCheck(address) === true, 'Arg-address is not a valid address.');
let value = Chain.load(address);
return value === false ? "0" : value;
}

function init(input_str){
let paramObj = JSON.parse(input_str).params;
Utils.assert(paramObj.name !== undefined && paramObj.name.length > 0, 'Param obj has no name.');
Utils.assert(paramObj.symbol !== undefined && paramObj.symbol.length > 0, 'Param obj has no symbol.');
Utils.assert(paramObj.describe !== undefined && paramObj.describe.length > 0, 'Param obj has no describe.');
Utils.assert(paramObj.decimals !== undefined && Utils.int64Compare(paramObj.decimals, '0') >= 0, 'Param obj decimals error.');
Utils.assert(paramObj.version !== undefined && paramObj.version.length > 0, 'Param obj has no version.');
Utils.assert(paramObj.supply !== undefined && Utils.int64Compare(paramObj.supply, '0') >= 0, 'Param obj supply error.');
Utils.assert(paramObj.protocol !== undefined && paramObj.protocol.length > 0 && paramObj.protocol.toLowerCase() === ATP_PROTOCOL, 'Param obj protocol must be atp20.');

Chain.store(CONTRACT_PRE, JSON.stringify(paramObj));
Chain.store(Chain.msg.sender, paramObj.supply);

Chain.tlog('Transfer', "0x", Chain.msg.sender, paramObj.supply);
}

function main(input_str){
let input = JSON.parse(input_str);

if(input.method === 'transfer'){
transfer(input.params.to, input.params.value);
}
else{
throw '<Main interface passes an invalid operation type>';
}
}

function query(input_str){
let result = {};
let input = JSON.parse(input_str);

if(input.method === 'contractInfo'){
result = JSON.parse(Chain.load(CONTRACT_PRE));
}
else if(input.method === 'balanceOf'){
result.balance = balanceOf(input.params.address);
}
else{
throw '<Query interface passes an invalid operation type>';
}
return JSON.stringify(result);
}


4.2. 应用流程设计

通过Java SDK实现与底层链的交互, 完成积分发行,积分转移、积分查询业务

1-应用流程设计

5. 应用开发

应用开发主要分以下几个步骤:

  • 创建区块链账户
  • 部署合约并初始化积分
  • 积分转移
  • 查询积分余额

在开始开发之前,确保环境已搭建,目前给的示例使用的是java来实现应用开发的,

先引入Java SDK 在pom文件加入依赖:

<dependency>
<groupId>cn.bubi.sdk</groupId>
<artifactId>bubichain-sdk</artifactId>
<version>4.1.3</version>
</dependency>

设置区块链网络URL(本示例以体验网为例),创建SDK实例。

String url = "https://seed1-node.bubi.cn";//可以替换成自己部署好的链地址
SDK sdk = SDK.getInstance(url);

5.1. 创建区块链账户

首先,我们需要创建区块链账户来进行交易,并且账户可以和你真实的业务账户绑定

@Test
public void createAccount() {
//1、生成区块链地址
AccountCreateResponse response = sdk.getAccountService().create();
if (response.getErrorCode() != 0) {
throw new RuntimeException(response.getErrorDesc());
}
AccountCreateResult accountInfo = response.getResult();
String address = accountInfo.getAddress();
String publicKey = accountInfo.getPublicKey();
String privateKey = accountInfo.getPrivateKey();
System.out.println("address:"+ address);
System.out.println("publicKey:"+ publicKey);
System.out.println("privateKey:"+ privateKey);
//2、执行链上激活账户操作
//交易发起人 链上存在的账户且有燃料, 注意自己部署的链 如果没有已存在的账户,需要使用链的创世账户来操作
String txSender = ExampleData.ACCOUNT_ADDRESS;
String txSenderPrivateKey = ExampleData.ACCOUNT_PRIVATE_KEY;
//给目标账户初始化的燃料
Long initBalance = ToBaseUnit.ToUGas("0.2");
//交易的费用
Long feeLimit = ToBaseUnit.ToUGas("0.01");
// 获取交易发起人发起人的nonce
long nonce = ContractHelper.getAccountNonce(txSender);
//组装链上激活账户操作
BaseOperation[] operations = TxHelper.buildCreateAccOperations( txSender,address, initBalance);
String transactionBlob = TxHelper.buildTransactionBlob(txSender,nonce,feeLimit,operations);
//发起人对交易进行签名
Signature[] signatures = TxHelper.signTransaction(txSenderPrivateKey, transactionBlob);

System.out.println("signData: " + signatures[0].getSignData());
System.out.println("publicKey: " + signatures[0].getPublicKey());
String hash = TxHelper.submitTransaction(transactionBlob, signatures);
System.out.println("hash: " + hash);
// 模拟延迟
try {
Thread.sleep(3500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 查询交易结果
int status = TxHelper.checkTransactionStatus(hash);
System.out.println("status: " + status);
}

5.2. 部署合约并初始化积分

接下来,我们需要将合约部署到链上,并将积分初始化到合约中

@Test
public void deployContract() {
// 账户部署合约所用的私钥
String creatorPrivateKey = ExampleData.ACCOUNT_PRIVATE_KEY;

// 部署BRC20 token的账户地址
String creatorContractAddress = ExampleData.ACCOUNT_ADDRESS;
// 合约初始化的Gas,单位UGas,1Gas = 10^8 UGas
Long initBalance = ToBaseUnit.ToUGas("0.1");
// token 名称
String name = "Global";
// token 代码
String symbol = "GLA";
// token 总供应量,包含小数位
// 发行10亿个token,小数位为2,则需要1000000000 * 10^2
String totalSupply = "100000000000";
// 积分的小数位
Integer decimals = 2;
String version = "1.0";
// 合约源代码
String payload = ExampleData.CONTRACT_BRC20_CODE;

// Init initInput
JSONObject initInput = new JSONObject();
JSONObject params = new JSONObject();
params.put("name", name);
params.put("symbol", symbol);
params.put("decimals", decimals);
params.put("version", version);
params.put("supply", totalSupply);
initInput.put("params", params);

String metadata = "deploy BRC20 contracts";
// 获取合约发行方账户的nonce
long nonce = ContractHelper.getAccountNonce(creatorContractAddress);
BaseOperation[] operations = ContractHelper.buildCreateContractOperations(creatorContractAddress, payload, initInput.toJSONString(), initBalance, metadata);
// 发行积分方账户地址
String senderAddress = ExampleData.ACCOUNT_ADDRESS;
// 设置最大花费10.1Gas,单位UGas
Long feeLimit = ToBaseUnit.ToUGas("10.1");

String transactionBlob = ContractHelper.buildTransactionBlob(senderAddress,nonce, feeLimit, operations);
Signature[] signatures = ContractHelper.signTransaction(creatorPrivateKey, transactionBlob);
System.out.println("signData: " + signatures[0].getSignData());
System.out.println("publicKey: " + signatures[0].getPublicKey());
String hash = ContractHelper.submitTransaction(transactionBlob, signatures);
System.out.println("hash: " + hash);

// 模拟延迟
try {
Thread.sleep(3500);
} catch (InterruptedException e) {
e.printStackTrace();
}

int status = TxHelper.checkTransactionStatus(hash);
System.out.println("status: " + status);

// 获取合约地址, 需要做延迟处理,才可以获取到合约地址
System.out.println("contractAddress: " + ContractHelper.getContractAddress(hash));
}

5.3. 积分转移

部署合约后,我们可以使用智能合约的转移积分功能。

 @Test
public void transfer() {
// 交易发起人地址 根据业务可以使用不同的账户地址
String invokerAddress = ExampleData.ACCOUNT_ADDRESS;
// 交易发起人私钥
String invokePrivateKey = ExampleData.ACCOUNT_PRIVATE_KEY;
// 合约地址5.2返回的合约地址
String contractAddress = ExampleData.CONTRACT_ADDRESS;
// 接收方账户地址
String destAddress = ExampleData.RECEIVER_ADDRESS;
// 给合约账户的燃料,本合约可以为0
long amount = 0L;
// 交易的燃料手续费用
Long feeLimit = ToBaseUnit.ToUGas("0.01");
// 给目标账户转的积分数量(需要根据发行的时候的精度进行计算,合约里面都是使用整数)
long toAmount = 100L;
// 组装调用合约的方法
JSONObject input = new JSONObject();
JSONObject params = new JSONObject();
params.put("to", destAddress);
params.put("value", toAmount+"");
input.put("params", params);
input.put("method", "transfer");
String inputMethod = input.toJSONString();
// 获取交易发起人发起人的nonce
Long nonce = TxHelper.getAccountNonce(invokerAddress);
// 组装调用合约的操作
BaseOperation[] operations = ContractHelper.buildInvokeContractOperations(invokerAddress, contractAddress, amount, inputMethod);
// 对交易操作进行序列化
String transactionBlob = TxHelper.buildTransactionBlob(invokerAddress,nonce, feeLimit, operations);
// 对交易blob进行签名
Signature[] signatures = TxHelper.signTransaction(invokePrivateKey, transactionBlob);
String txHash = TxHelper.submitTransaction(transactionBlob, signatures);
System.out.println("txHash: " + txHash);
// 模拟延迟
try {
Thread.sleep(3500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 查询交易结果
int status = TxHelper.checkTransactionStatus(txHash);
System.out.println("status: " + status);

}

5.4. 积分查询

最后,我们可以查询指定账户的积分余额。

 @Test
public void transfer() {
// 交易发起人地址 根据业务可以使用不同的账户地址
String invokerAddress = ExampleData.ACCOUNT_ADDRESS;
// 交易发起人私钥
String invokePrivateKey = ExampleData.ACCOUNT_PRIVATE_KEY;
// 合约地址5.2返回的合约地址
String contractAddress = ExampleData.CONTRACT_ADDRESS;
// 接收方账户地址
String destAddress = ExampleData.RECEIVER_ADDRESS;
// 给合约账户的燃料,本合约可以为0
long amount = 0L;
// 交易的燃料手续费用
Long feeLimit = ToBaseUnit.ToUGas("0.01");
// 给目标账户转的积分数量(需要根据发行的时候的精度进行计算,合约里面都是使用整数)
long toAmount = 100L;
// 组装调用合约的方法
JSONObject input = new JSONObject();
JSONObject params = new JSONObject();
params.put("to", destAddress);
params.put("value", toAmount+"");
input.put("params", params);
input.put("method", "transfer");
String inputMethod = input.toJSONString();
// 获取交易发起人发起人的nonce
Long nonce = TxHelper.getAccountNonce(invokerAddress);
// 组装调用合约的操作
BaseOperation[] operations = ContractHelper.buildInvokeContractOperations(invokerAddress, contractAddress, amount, inputMethod);
// 对交易操作进行序列化
String transactionBlob = TxHelper.buildTransactionBlob(invokerAddress,nonce, feeLimit, operations);
// 对交易blob进行签名
Signature[] signatures = TxHelper.signTransaction(invokePrivateKey, transactionBlob);
String txHash = TxHelper.submitTransaction(transactionBlob, signatures);
System.out.println("txHash: " + txHash);
// 模拟延迟
try {
Thread.sleep(3500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 查询交易结果
int status = TxHelper.checkTransactionStatus(txHash);
System.out.println("status: " + status);

}

如果你对源代码感兴趣,可以通过以下链接进行下载。示例代码

6. 总结

开发区块链应用需要经历环境搭建、编写合约、部署合约和应用开发等步骤。在本示例中,通过实现积分管理的功能,展示了区块链在实际应用中的潜力。具体包括创建区块链账户、部署合约并初始化积分、进行积分转移以及查询积分余额等操作。希望这个示例能为用户开发自己的区块链应用提供一些参考和启发。