文章目录
- 一、搭建私有链
- 1. 创世json文件: genesis.json
- 2. 创建区块链节点: dataChain
- 2.1 错误与常见问题
- 3. 启动服务端: dataChainServer
- 4. 启动客户端: dataChainClient
- 5. 客户端设置挖矿账户
- 5.1 常见问题
- 二、部署智能合约
- 1. 合约代码
- 2. 编译合约
- 3. 部署合约
- 3.1 常见问题
- 三、Java 通过web3j 调用智能合约
- 1. 将web3j编译生成的java合约文件拷贝到工程中
- 2. web3j 工具类连接区块链
- 3. 调用智能合约里的方法
一、搭建私有链
1. 创世json文件: genesis.json
{
"config":{
"chainId":15,
"homesteadBlock":0,
"eip155Block":0,
"eip158Block":0
},
"difficulty":"4",
"gasLimit":"2100000",
"alloc":{ }
}
2. 创建区块链节点: dataChain
geth init genesis.json --datadir dataChain
2.1 错误与常见问题
<1>错误1:pcscd服务错误
如果初始化节点过程中报以下错误:
INFO [11-09|17:23:10.229] Smartcard socket not found, disabling err="stat /run/pcscd/pcscd.comm: no such file or directory"
则后面会挖不出来矿,需要安装pscd服务
sudo apt-get -y install libusb-dev
sudo apt-get -y install pcscd
<2>错误2:创世文件block
如果初始化节点过程中报以下错误:
Fatal: Failed to write genesis block: unsupported fork ordering: eip150Block not enabled, but eip155Block enabled at 0
则修改创世文件block号150和155:
"eip150Block": 0,
"eip155Block": 0
3. 启动服务端: dataChainServer
# geth --networkid 123 --datadir dataChain --rpc --rpcaddr 127.0.0.1 --rpcport 8545 --port 3000 --allow-insecure-unlock
备注:
- rpcaddr 可以写本机地址,也可以写127.0.0.1
- 如果这里的rpcaddr 和 rpcport 修改为其他值了,则需要修改程序里web3j连接的url
- 加上 --allow-insecure-unlock 防止解锁账户过程中出错;
4. 启动客户端: dataChainClient
# cd dataChain
# geth attach ipc:geth.ipc
5. 客户端设置挖矿账户
# geth attach ipc:geth.ipc
> personal.listAccounts
[]
> personal.newAccount("123456")
"0x0f6740d209db6ed6b12c70daf667340cd06c5611"
> personal.listAccounts
["0x0f6740d209db6ed6b12c70daf667340cd06c5611"]
> eth.getBalance(personal.listAccounts[0])
0
> miner.setEtherbase(personal.listAccounts[0])
true
> miner.start()
null
> miner.stop()
null
> eth.getBalance(personal.listAccounts[0])
35000000000000000000
说明:
- 创建账户过程中newAccount()方法传递的参数即是该账户的密码(如:“123456”)
5.1 常见问题
<1> 首次挖矿需要等待较长时间
- 一台计算机创建完节点首次挖矿时需要等待较长时间,Generating DAG in progress需要从0%到100%
二、部署智能合约
1. 合约代码
pragma solidity ^0.4.4;
contract CheckContract {
function check(string attributeA, string attributeB) pure public returns(bool){
if(bytes(attributeA).length != bytes(attributeB).length){
return false;
}else{
return keccak256(bytes(attributeA)) == keccak256(bytes(attributeA));
}
}
}
2. 编译合约
# 编译合约,生成bin文件
solc --bin CheckContract.sol -o ./
# 编译合约,生成abi文件
solc --abi CheckContract.sol -o ./
// web3j 根据bin文件和abi文件生成Java合约类
web3j solidity generate --javaTypes --binFile=CheckContract.bin --abiFile=CheckContract.abi -o ./ --package='myContract'
3. 部署合约
#! dataChainClient
# 合约bin赋值,拷贝.bin文件里的值
# 记得在.bin文件的值之前加 0x
>var publicBinCode="0x608060405234801561001057600080fd5b50610227806100206000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063725dbc4014610046575b600080fd5b34801561005257600080fd5b506100f3600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061010d565b604051808215151515815260200191505060405180910390f35b60008151835114151561012357600090506101f5565b816040518082805190602001908083835b6020831015156101595780518252602082019150602081019050602083039250610134565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902060001916836040518082805190602001908083835b6020831015156101c0578051825260208201915060208101905060208303925061019b565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020600019161490505b929150505600a165627a7a7230582096b45b9571ed5dacf868e454b6330fa960d7099f595bd758e940a49bd7ec68790029"
# 合约 abi赋值
>var publicAbiCode=[{"constant":true,"inputs":[{"name":"attributeA","type":"string"},{"name":"attributeB","type":"string"}],"name":"check","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"pure","type":"function"}]
# 解锁挖矿账户
> personal.unlockAccount(personal.listAccounts[0])
Passphrase: // 输入创建账户时,括号里的参数,即账户的密码,"123456"
Unlock account 0x449f09d90a4726db78b86972528fc99ea00777d8
# 预估gas
> eth.estimateGas({data:publicBinCode})
199867
> var publicAbiContract=eth.contract(publicAbiCode)
undefined
> var publicContract=publicAbiContract.new({from:personal.listAccounts[0],data:publicBinCode,gas:200000})
undefined
# 开始挖矿
> miner.start()
> miner.stop()
# 查看合约
> publicContract
{
abi: [{
constant: true,
inputs: [{...}, {...}],
name: "check",
outputs: [{...}],
payable: false,
stateMutability: "pure",
type: "function"
}],
address: "0x291f3347f9cb6d58ca9c24b61f87bf2348b15d2f",
transactionHash: "0xf9cbfcf8027a085106855eb31b1e10368b63bca177c2b9fffe9131d179bf061b",
allEvents: function(),
check: function()
}
> publicContract.address
"0x291f3347f9cb6d58ca9c24b61f87bf2348b15d2f"
3.1 常见问题
<1>错误:无法解锁挖矿账户
如果解锁账户过程中报以下错误:
account unlock with HTTP access is forbidden
因为新版本geth,出于安全考虑,默认禁止了HTTP通道解锁账户,解决办法是在启动服务端时加上参数
--allow-insecure-unlock
# 完整的启动命令
geth --networkid 123 --datadir dataChain --rpc --rpcaddr 127.0.0.1 --rpcport 8545 --port 3000 --allow-insecure-unlock
注意事项:
- bin赋值时需要在数字前加’0x’,然后将一串数字放入引号内(单双引号均可)
- abi赋值时不需要加内容,也不需要加引号!
三、Java 通过web3j 调用智能合约
1. 将web3j编译生成的java合约文件拷贝到工程中
package access.contract;
import java.math.BigInteger;
import java.util.Arrays;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Bool;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.RemoteCall;
import org.web3j.tx.Contract;
import org.web3j.tx.TransactionManager;
import org.web3j.tx.gas.ContractGasProvider;
public class CheckContract extends Contract {
private static final String BINARY = "608060405234801561001057600080fd5b50610227806100206000396000f300608060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063725dbc4014610046575b600080fd5b34801561005257600080fd5b506100f3600480360381019080803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509192919290803590602001908201803590602001908080601f016020809104026020016040519081016040528093929190818152602001838380828437820191505050505050919291929050505061010d565b604051808215151515815260200191505060405180910390f35b60008151835114151561012357600090506101f5565b826040518082805190602001908083835b6020831015156101595780518252602082019150602081019050602083039250610134565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902060001916836040518082805190602001908083835b6020831015156101c0578051825260208201915060208101905060208303925061019b565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040518091039020600019161490505b929150505600a165627a7a72305820780ddfaae258de09943d9399c67ea187c86272e789ea36798919fc34edf3656b0029";
public static final String FUNC_CHECK = "check";
@Deprecated
protected CheckContract(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
super(BINARY, contractAddress, web3j, credentials, gasPrice, gasLimit);
}
protected CheckContract(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
super(BINARY, contractAddress, web3j, credentials, contractGasProvider);
}
@Deprecated
protected CheckContract(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
super(BINARY, contractAddress, web3j, transactionManager, gasPrice, gasLimit);
}
protected CheckContract(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
super(BINARY, contractAddress, web3j, transactionManager, contractGasProvider);
}
public RemoteCall<Boolean> check(String attributeA, String attributeB) {
final Function function = new Function(FUNC_CHECK,
Arrays.<Type>asList(new org.web3j.abi.datatypes.Utf8String(attributeA),
new org.web3j.abi.datatypes.Utf8String(attributeB)),
Arrays.<TypeReference<?>>asList(new TypeReference<Bool>() {}));
return executeRemoteCallSingleValueReturn(function, Boolean.class);
}
@Deprecated
public static CheckContract load(String contractAddress, Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
return new CheckContract(contractAddress, web3j, credentials, gasPrice, gasLimit);
}
@Deprecated
public static CheckContract load(String contractAddress, Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
return new CheckContract(contractAddress, web3j, transactionManager, gasPrice, gasLimit);
}
public static CheckContract load(String contractAddress, Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
return new CheckContract(contractAddress, web3j, credentials, contractGasProvider);
}
public static CheckContract load(String contractAddress, Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
return new CheckContract(contractAddress, web3j, transactionManager, contractGasProvider);
}
public static RemoteCall<CheckContract> deploy(Web3j web3j, Credentials credentials, ContractGasProvider contractGasProvider) {
return deployRemoteCall(CheckContract.class, web3j, credentials, contractGasProvider, BINARY, "");
}
@Deprecated
public static RemoteCall<CheckContract> deploy(Web3j web3j, Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
return deployRemoteCall(CheckContract.class, web3j, credentials, gasPrice, gasLimit, BINARY, "");
}
public static RemoteCall<CheckContract> deploy(Web3j web3j, TransactionManager transactionManager, ContractGasProvider contractGasProvider) {
return deployRemoteCall(CheckContract.class, web3j, transactionManager, contractGasProvider, BINARY, "");
}
@Deprecated
public static RemoteCall<CheckContract> deploy(Web3j web3j, TransactionManager transactionManager, BigInteger gasPrice, BigInteger gasLimit) {
return deployRemoteCall(CheckContract.class, web3j, transactionManager, gasPrice, gasLimit, BINARY, "");
}
}
2. web3j 工具类连接区块链
public class Web3JClient {
// 启动区块链服务端时的 rpcaddr 和 rpcport 参数,如果没有使用了其他参数,则需要在这里修改
private static String url = "http://127.0.0.1:8545/";
private Web3JClient() {
}
private volatile static Web3j web3j;
public static Web3j getClient() {
if (web3j == null) {
synchronized (Web3JClient.class) {
if (web3j == null) {
web3j = Web3j.build(new HttpService(url));
}
}
}
return web3j;
}
}
3. 调用智能合约里的方法
public class ContractService {
private static Web3j web3j = Web3JClient.getClient();
public static Boolean publicCheck(String contractAddress, String subAttribute, String objAttribute) {
try {
// 获取第一个账户
String minerBaseAccount = web3j.ethAccounts().send().getAccounts().get(0);
Credentials credentials = Credentials.create(minerBaseAccount);
// 合约对象
CheckContract checkContract = CheckContract.load(contractAddress, web3j, credentials, Contract.GAS_PRICE, Contract.GAS_PRICE);
return checkContract.check(subAttribute, objAttribute).send();
} catch (Exception e) {
System.out.println("exception");
return null;
}
}
}