Fisco Bcos学习 - 开发第一个区块链应用
在区块链技术快速发展的今天,如何将其应用于实际业务场景成为开发者关注的重点。FISCO BCOS 作为国产优秀的联盟链平台,为企业级区块链应用开发提供了强大支持。本文将跟随官方教程,详细记录基于 FISCO BCOS 构建第一个区块链应用的全过程,涵盖从业务分析到最终实现的完整流程。
文章目录
一、前言
在区块链技术快速发展的今天,如何将其应用于实际业务场景成为开发者关注的重点。FISCO BCOS作为国产优秀的联盟链平台,为企业级区块链应用开发提供了强大支持。本文将跟随官方教程,详细记录基于FISCO BCOS构建第一个区块链应用的全过程,涵盖从业务分析到最终实现的完整流程。
二、业务场景分析:简易资产管理系统
区块链技术因其防篡改、可追溯的特性,在金融领域有着天然优势。本次实践选择开发一个简易的资产管理系统,主要实现以下核心功能:
- 资产注册:在区块链上登记资产账户及初始金额
- 资产转账:实现不同账户间的资产转移
- 资产查询:查询指定账户的资产余额
这个场景虽然简单,但涵盖了区块链应用开发的核心流程,非常适合作为入门案例。
三、智能合约设计与实现
3.1 存储结构设计
FISCO BCOS提供了合约CRUD接口开发模式,允许通过合约创建表结构并进行数据操作。针对资产管理需求,设计了名为t_asset
的表:
account
:资产账户,作为主键(string类型)asset_value
:资产金额(uint256类型)
该表结构示例如下:
account | asset_value |
---|---|
Alice | 10000 |
Bob | 20000 |
3.2 接口设计
根据业务目标,定义了三个核心接口:
select(string account)
:查询资产金额register(string account, uint256 amount)
:资产注册transfer(string from_asset_account, string to_asset_account, uint256 amount)
:资产转移
3.3 完整合约代码
pragma solidity ^0.4.24;
import "./Table.sol";
contract Asset {
// 事件定义,用于记录关键操作
event RegisterEvent(int256 ret, string account, uint256 asset_value);
event TransferEvent(int256 ret, string from_account, string to_account, uint256 amount);
constructor() public {
// 构造函数中创建表
createTable();
}
function createTable() private {
TableFactory tf = TableFactory(0x1001);
// 创建t_asset表,指定主键和字段
tf.createTable("t_asset", "account", "asset_value");
}
function openTable() private returns (Table) {
TableFactory tf = TableFactory(0x1001);
Table table = tf.openTable("t_asset");
return table;
}
// 查询资产金额
function select(string account) public constant returns (int256, uint256) {
Table table = openTable();
Entries entries = table.select(account, table.newCondition());
uint256 asset_value = 0;
if (0 == uint256(entries.size())) {
return (-1, asset_value);
} else {
Entry entry = entries.get(0);
return (0, uint256(entry.getInt("asset_value")));
}
}
// 资产注册
function register(string account, uint256 asset_value) public returns (int256) {
int256 ret_code = 0;
int256 ret = 0;
uint256 temp_asset_value = 0;
(ret, temp_asset_value) = select(account);
if (ret != 0) {
Table table = openTable();
Entry entry = table.newEntry();
entry.set("account", account);
entry.set("asset_value", int256(asset_value));
int count = table.insert(account, entry);
if (count == 1) {
ret_code = 0;
} else {
ret_code = -2;
}
} else {
ret_code = -1;
}
emit RegisterEvent(ret_code, account, asset_value);
return ret_code;
}
// 资产转移
function transfer(string from_account, string to_account, uint256 amount) public returns (int256) {
int ret_code = 0;
int256 ret = 0;
uint256 from_asset_value = 0;
uint256 to_asset_value = 0;
// 检查转出账户是否存在
(ret, from_asset_value) = select(from_account);
if (ret != 0) {
ret_code = -1;
emit TransferEvent(ret_code, from_account, to_account, amount);
return ret_code;
}
// 检查转入账户是否存在
(ret, to_asset_value) = select(to_account);
if (ret != 0) {
ret_code = -2;
emit TransferEvent(ret_code, from_account, to_account, amount);
return ret_code;
}
// 检查余额是否充足
if (from_asset_value < amount) {
ret_code = -3;
emit TransferEvent(ret_code, from_account, to_account, amount);
return ret_code;
}
// 检查金额是否溢出
if (to_asset_value + amount < to_asset_value) {
ret_code = -4;
emit TransferEvent(ret_code, from_account, to_account, amount);
return ret_code;
}
Table table = openTable();
Entry entry0 = table.newEntry();
entry0.set("account", from_account);
entry0.set("asset_value", int256(from_asset_value - amount));
int count = table.update(from_account, entry0, table.newCondition());
if (count != 1) {
ret_code = -5;
emit TransferEvent(ret_code, from_account, to_account, amount);
return ret_code;
}
Entry entry1 = table.newEntry();
entry1.set("account", to_account);
entry1.set("asset_value", int256(to_asset_value + amount));
table.update(to_account, entry1, table.newCondition());
emit TransferEvent(ret_code, from_account, to_account, amount);
return ret_code;
}
}
注意:该合约依赖FISCO BCOS提供的系统合约
Table.sol
,实现对表的CRUD操作
四、合约编译与Java接口生成
Solidity合约无法被Java程序直接调用,需要通过控制台工具将其编译为Java类。具体步骤如下:
- 将
Asset.sol
和Table.sol
放在console/contracts/solidity
目录 - 执行编译脚本:
# 切换到console目录
cd ~/fisco/console/
# 编译合约,指定Java包名
./sol2java.sh org.fisco.bcos.asset.contract
编译成功后会在console/contracts/sdk
目录生成:
abi
目录:存放合约ABI文件bin
目录:存放合约字节码文件java
目录:存放生成的Java合约类
生成的Asset.java
包含了与Solidity合约对应的Java接口:
package org.fisco.bcos.asset.contract;
public class Asset extends Contract {
// 转账接口
public RemoteCall<TransactionReceipt> transfer(String from_account, String to_account, BigInteger amount);
// 注册接口
public RemoteCall<TransactionReceipt> register(String account, BigInteger asset_value);
// 查询接口
public RemoteCall<Tuple2<BigInteger, BigInteger>> select(String account);
// 加载合约
public static Asset load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider);
// 部署合约
public static RemoteCall<Asset> deploy(Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider);
}
五、SDK配置与项目搭建
5.1 获取Java工程项目
# 下载项目压缩包
cd ~
curl -LO https://github.com/FISCO-BCOS/LargeFiles/raw/master/tools/asset-app.tar.gz
# 解压项目
tar -zxf asset-app.tar.gz
5.2 项目目录结构
asset-app/
├── build.gradle # Gradle配置文件
├── gradle/
│ ├── wrapper/
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew # Linux执行脚本
├── gradlew.bat # Windows执行脚本
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── org/
│ │ │ └── fisco/
│ │ │ └── bcos/
│ │ │ └── asset/
│ │ │ ├── client/ # 客户端调用类
│ │ │ │ └── AssetClient.java
│ │ │ └── contract/ # 合约Java类
│ │ │ └── Asset.java
│ │ └── resources/
│ │ ├── applicationContext.xml # 项目配置文件
│ │ ├── contract.properties # 合约地址配置
│ │ ├── log4j.properties # 日志配置
│ │ └── contract/
│ │ ├── Asset.sol # Solidity合约
│ │ └── Table.sol
├── tool/
│ └── asset_run.sh # 运行脚本
5.3 引入Web3SDK
项目已在build.gradle
中配置好Web3SDK依赖:
repositories {
maven {
url "http://maven.aliyun.com/nexus/content/groups/public/"
}
maven { url "https://dl.bintray.com/ethereum/maven/" }
mavenCentral ()
}
dependencies {
compile('org.fisco-bcos:web3sdk:2.1.0')
}
5.4 证书与配置文件
拷贝区块链节点的SDK证书到项目资源目录:
cp fisco/nodes/127.0.0.1/sdk/* asset-app/src/test/resources/
六、业务开发:Java客户端实现
6.1 核心类设计:AssetClient
AssetClient.java
是业务逻辑的核心,负责合约的部署与调用,主要包含以下功能模块:
6.1.1 初始化
// 初始化Web3j和Credentials对象
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Service service = context.getBean(Service.class);
service.run();
ChannelEthereumService channelEthereumService = new ChannelEthereumService();
channelEthereumService.setChannelService(service);
Web3j web3j = Web3j.build(channelEthereumService, 1);
Credentials credentials = Credentials.create(Keys.createEcKeyPair());
6.1.2 合约对象创建
// 部署合约
Asset asset = Asset.deploy(web3j, credentials, new StaticGasProvider(gasPrice, gasLimit)).send();
// 加载已部署合约
Asset asset = Asset.load(contractAddress, web3j, credentials, new StaticGasProvider(gasPrice, gasLimit));
6.1.3 接口调用
// 查询资产
Tuple2<BigInteger, BigInteger> result = asset.select(assetAccount).send();
// 注册资产
TransactionReceipt registerReceipt = asset.register(assetAccount, amount).send();
// 资产转账
TransactionReceipt transferReceipt = asset.transfer(fromAccount, toAccount, amount).send();
七、项目运行与功能验证
7.1 编译项目
# 切换到项目目录
cd ~/asset-app
# 编译项目
./gradlew build
编译成功后在dist
目录生成运行脚本。
7.2 部署合约
cd dist
bash asset_run.sh deploy
# 输出示例:Deploy Asset successfully, contract address is 0xd09ad04220e40bb8666e885730c8c460091a4775
7.3 注册资产
bash asset_run.sh register Alice 100000
# 输出:Register account successfully => account: Alice, value: 100000
bash asset_run.sh register Bob 100000
# 输出:Register account successfully => account: Bob, value: 100000
7.4 查询资产
bash asset_run.sh query Alice
# 输出:account Alice, value 100000
bash asset_run.sh query Bob
# 输出:account Bob, value 100000
7.5 资产转移
bash asset_run.sh transfer Alice Bob 50000
# 输出:Transfer successfully => from_account: Alice, to_account: Bob, amount: 50000
bash asset_run.sh query Alice
# 输出:account Alice, value 50000
bash asset_run.sh query Bob
# 输出:account Bob, value 150000
八、参考资料
- FISCO BCOS文档:https://www.bookstack.cn/read/fisco-bcos-2.4-zh
更多推荐
所有评论(0)