Springboot+web3j完成去中心化的区块链应用
去中心化的开发流程
1.基础准备
2.核心开发
2.1区块链
2.2java程序
3.具体案例
去中心化的开发流程
简单分析下本人的小小开发经历,当初自己学习的时候翻看很多资料也一头雾水,白花了很多力气,这里进行简要总结,希望为大家带来帮助。
1.基础准备
前期准备如下:
IDEA ,你的后台编辑器;
Geth ,你的以太坊区块链本地客户端;
不需要数据库,因为数据可以存储在区块链之上,当然你可以加上。
在线编辑器remix,用来使用solidity语言编写智能合约。
http://remix.hubwiz.com
下载安装web3j命令行。用来把.sol转为.java
2.核心开发
2. 1区块链
用solidity语言设计你的智能合约,数据结构用struct存储于以太坊区块链上,调试运行在remix的虚拟机上。
在本地geth客户端上创建创世区块。
首先配置私有区块链网络的初始状态。新建一个文件 genesis.json
{
“config”: {
“chainId”: 22,
“homesteadBlock”: 0,
“eip150Block”: 0,
“eip155Block”: 0,
“eip158Block”: 0,
“byzantiumBlock”: 0,
“constantinopleBlock”: 0,
“petersburgBlock”: 0,
“ethash”: {}
},
“difficulty”: “1”,
“gasLimit”: “0xffffffff”,
“alloc”: {}
}
其中需要修改的配置
•chainId 指定了独立的区块链网络 ID,不同 ID 网络的节点无法互相连接;
•ethash 使用ethash共识算法;
•difficulty 初始挖掘难度,难度越大越难挖到区块;
•gasLimit 用于限制单个块中可以进行多少EVM计算,这里值得一提,那就是我配置的最大,因为我的智能合约功能很复杂花费的gas很多。
•alloc 这将确定您在创世纪块中列出的地址有多少以太币可用;
启动区块链
用以下命令初始化区块链,生成创世区块和初始状态。
geth init genesis.json
然后用以下命令启动节点进入Javascript交互控制台
geth --nodiscover --rpc --rpcapi db,eth,net,web3 --allow-insecure-unlock console
各选项的含义如下:
•–nodiscover 关闭区块链节点发现机制;
•–rpc 开启以太坊HTTP-RPC 服务(默认端口8545);
•–rpcapi 默认情况下,出于安全原因,并非所有功能都通过RPC接口提供。需要指定需要开放的功能;
•–allow-insecure-unlock 允许不安全的解锁用户操作。
•console 进入Javascript交互控制台
使用web3j命令行工具转义智能合约(从.sol->.java)
编译合约生成Java包装器
首先我们需要将sol合约文件编译成EVM字节码和ABI,这里我们使用solc来编译合约,在当前目录会生成两个文件,分别是字节码文件和abi文件。如果solcjs命令不存在,需要先用npm安装solc。
#npm install -g solc 安装solc
solcjs xxx(你的智能合约).sol --bin --abi --optimize -o xxx(输出目录)
然后使用web3j命令行工具生成Java包装类。各命令的含义如下:
•-b 指定bin文件;
•-a 指定abi文件;
•-o 指定生成的Java文件路径;
•-p 指定生成Java文件的包名;
•-c 指定生成的类名
web3j solidity generate -b xxx.bin -a xxx.abi -o …/java -
2. 2java程序
创建springboot项目
配置pom.xml,加入web3j依赖
配置application.properties
#1.项目启动的端口
server.port=18902
#2.web3j配置
web3j.client-address: http://127.0.0.1:8545
web3j.admin-client=true
web3j.http-timeout-seconds=600
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML
spring.thymeleaf.cache=false
debug=true
#热部署,使得静态资源不需要重启
spring.devtools.restart.exclude=/static/
#重启目录
spring.devtools.restart.additional-paths=src/main/java
由于项目不需要数据库,注释说明。
在你的xxxApplication.java主函数上添加注释:
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
在java程序中使用web3j进行和区块链之间的交互。
展示几个关键的重要的(其中Diploma为我的智能合约类):
//部署智能合约
public Diploma deployContract()throws Exception {
Diploma diploma = Diploma.deploy(web3j,credentials,Contract.GAS_PRICE, Contract.GAS_LIMIT).send();
return diploma;
}
//加载智能合约
public Diploma loadContract() {
Diploma diploma = Diploma.load(ContractAddress, web3j, credentials, new BigInteger(“21000”), new BigInteger(“35000000000”));
return diploma;
}
//注册账户
public String Regist()throws Exception {
String walletFileName="";//文件名
String walletFilePath=“C:\Users\xxx\AppData\Roaming\Ethereum\keystore”;
//钱包文件保持路径,请替换位自己的某文件夹路径
walletFileName = WalletUtils.generateNewWalletFile(“123456”, new File(walletFilePath), false);
Credentials new_credentials = WalletUtils.loadCredentials(password, walletFilePath + “\” + walletFileName);
String address = new_credentials.getAddress();
return address;
}
调用自己智能合约提供的接口。
两种方式:交易(transaction)方式以及function方式。
transaction方式增删改查都可以使用,但是需要挖矿来处理数据,速度会慢。function方式只可以使用于查,但是速度快。因此建议所有的查询都用function方式。
transaction方式用法:
Diploma diploma=smartContract.loadContract();
TransactionReceipt a=diploma.comfirmEdu(address).sendAsync().get();
List b = diploma.getEventResponseLogEvents(a);
return b.get(0).message.equals(“ok”);
ps:transaction方式需要event证明接口执行状态。
function方式用法:
List inputParameters = new ArrayList<>();
inputParameters.add(new Address(AdminAccountID));
inputParameters.add(new Utf8String(id));
inputParameters.add(new Utf8String(password));
List> outputParameters= new ArrayList<>();
TypeReferencebool=new TypeReference() {
};
outputParameters.add(bool);
Function function = new Function(“接口函数名称”,
inputParameters,outputParameters);
String encodedFunction = FunctionEncoder.encode(function);
org.web3j.protocol.core.methods.response.EthCall response = Web3JClient.getClient().ethCall(
Transaction.createEthCallTransaction(AdminAccountID, smartContract.ContractAddress, encodedFunction),
DefaultBlockParameterName.LATEST)
.sendAsync().get();
//获取返回结果
List result = FunctionReturnDecoder.decode(
response.getValue(), function.getOutputParameters());
if (result.size()==0)
return false;
boolean login_ok=Boolean.parseBoolean(result.get(0).getValue().toString());
return login_ok;
ps:function方式需要注意web3j数据类型和你需要的java类型区别哦
这里也建议大家全程挖矿跑程序,因为部署智能合约以及调用修改数据都需要挖矿的。
大致就写到这里,有什么需要补充的我会在之后补充。
3.具体案例
这里附上个人的实战项目:
https://gitee.com/AidenX/ethereum_blockchain
欢迎各位来学习交流,共同进步。