以太坊區塊鏈java開發:web3j
阿新 • • 發佈:2018-11-27
以太坊 java開發依賴
<dependency>
<groupId>org.web3j</groupId>
<artifactId>core</artifactId>
<version>3.5.0</version>
</dependency>
連線到以太坊同步節點
private String gethURL = "http://localhost:8545"; private void connectGeth() { // --rpcport value HTTP-RPC伺服器監聽埠(預設值:8545) this.web3j = Admin.build(new HttpService(gethURL)); if(null==web3j) { System.out.println("connectGeth error"); } }
建立賬戶,需要輸入密碼,返回賬戶地址 0x00000...
public String addAccount(String password) { // walletId:$|chainId:$|account:$ if (web3j == null) connectGeth(); String account = ""; try { String fileName = WalletUtils.generateNewWalletFile(password, new File(keystorePath)); Credentials credentials = WalletUtils.loadCredentials(password, keystorePath + "/" + fileName); account = credentials.getAddress(); } catch (InvalidAlgorithmParameterException e1) { e1.printStackTrace(); } catch (NoSuchAlgorithmException e1) { e1.printStackTrace(); } catch (NoSuchProviderException e1) { e1.printStackTrace(); } catch (CipherException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } addCredentials(account, password); return account; }
以太幣(礦幣)轉賬
private void transfer_ETH(String _from, String password, String _to, BigInteger value, Long logId) { Credentials credentials = loadCredentials(_from, password); try { //獲取nonce EthGetTransactionCount count=web3j.ethGetTransactionCount(_from, DefaultBlockParameterName.LATEST).send(); BigInteger nonce=count.getTransactionCount(); //建立交易 //BigInteger val=Convert.toWei(new BigDecimal(value), Unit.ETHER).toBigInteger(); RawTransaction rawTransaction=RawTransaction.createEtherTransaction(nonce, GAS_PRICE, GAS_LIMIT, _to, value); //驗證簽名 byte[] signMessage = TransactionEncoder.signMessage(rawTransaction, credentials); String hexValue=Numeric.toHexString(signMessage); //傳送交易 CompletableFuture<EthSendTransaction> sendAsync = web3j.ethSendRawTransaction(hexValue).sendAsync(); sendAsync.whenComplete((v,e)->{ //交易回撥 new Thread(() -> { callBackService.callbackEthSendTransaction(v, logId, e); }).start(); }); }catch(Exception e) { } }
載入已有賬戶的憑證
keystorePath是憑證檔案儲存的資料夾地址,例如:/home/geth/keystore
這裡讀取檔案之前,先去全域性map(預載入)裡檢查是否能拿到憑證
private Credentials loadCredentials(String address,String password){
Credentials credentials=credentialsMap.get(address);
if(CommonUtil.isNull(credentials)){
//往map里加
credentials=addCredentials(address,password);
}
return credentials;
}
private Credentials addCredentials(String address,String password){
Credentials credentials=null;
try {
File file = new File(keystorePath);
File[] files = file.listFiles();
if(files!=null && files.length>0){
for (File f : files) {
String a=address.trim().substring(2);
if (f.getName().contains(a)) {
//取到這個檔案,並生成credentials物件
credentials = WalletUtils.loadCredentials(password, f);
break;
}
}
}
if (!CommonUtil.isNull(credentials)){
credentialsMap.putIfAbsent(address, credentials);
}else {
throw new RuntimeException("讀取憑證錯誤,請檢查錢包檔案是否存在");
}
} catch (IOException | CipherException e) {
e.printStackTrace();
}
return credentials;
}
以太坊還包括合約釋出的代幣-虛擬幣
載入執行合約也是非常常見的
智慧合約由solidity語言開發,可以編譯為java檔案
可以使用VScode下載solidity外掛,將合約編譯 .sol ---> .bin .abi .json
使用codegen-3.5.0.jar 的 SolidityFunctionWrapperGenerator.class 的 mian方法
<!-- https://mvnrepository.com/artifact/org.web3j/codegen -->
<dependency>
<groupId>org.web3j</groupId>
<artifactId>codegen</artifactId>
<version>3.5.0</version>
</dependency>
並需要附帶引數
#來自https://docs.web3j.io/smart_contracts.html
$ web3j solidity generate [--javaTypes|--solidityTypes] /path/to/<smart-contract>.bin /path/to/<smart-contract>.abi -o /path/to/src/main/java -p com.your.organisation.name
private static final String USAGE = "solidity generate "
+ "[--javaTypes|--solidityTypes] "
+ "<input binary file>.bin <input abi file>.abi "
+ "-p|--package <base package name> "
+ "-o|--output <destination base directory>";
從輸出資料夾拿到編譯好的java合約程式碼
呼叫合約方法 載入合約需要合約地址
//GAS_PRICE可以動態獲取
private BigInteger GAS_PRICE = BigInteger.valueOf(22_000_000_000L);
//GAS_LIMIT引數固定
private final BigInteger GAS_LIMIT = BigInteger.valueOf(43_000);
/**
* USDT的轉賬方法
*/
private void transfer_USDT(String _from, String password, String _to, BigInteger value, Long logId) {
//拿到憑證
Credentials credentials = loadCredentials(_from, password);
//使用憑證載入合約 需要知道合約的地址 這裡涉及到GAS 是為交易手續費 從合約呼叫方賬戶上扣除以太幣
TetherToken contract = TetherToken.load(USDTAddress, getWeb3j(), credentials, GAS_PRICE, GAS_LIMIT);
//呼叫合約方法
RemoteCall<TransactionReceipt> transfer = contract.transfer(_to, value);
CompletableFuture<TransactionReceipt> sendAsync = transfer.sendAsync();
//非同步執行 並添加回調方法
sendAsync.whenComplete((v,e)->{
new Thread(() -> {
callBackService.callbackTransactionReceipt(v, logId, e);
}).start();
});
}
有一部分合約並不公開原始碼
在無法編譯得到java合約程式碼的情況下,可以使用 abi+合約二進位制程式碼+合約地址 呼叫
abi包括一個合約所有方法、引數、返回值
以下做一個簡單的示例,並不支援很多引數型別
import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Bool;
import org.web3j.abi.datatypes.Int;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.Uint;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.tx.Contract;
import com.alibaba.fastjson.JSON;
import okkpp.function.Function;
import okkpp.function.Param;
public class WidelyContract extends Contract {
private List<Function> functions;
@SuppressWarnings("deprecation")
protected WidelyContract(String abi, String contractBinary, String contractAddress, Web3j web3j,
Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
super(contractBinary, contractAddress, web3j, credentials, gasPrice, gasLimit);
this.functions = JSON.parseArray(abi, Function.class);
}
public static WidelyContract load(String abi, String contractBinary, String contractAddress, Web3j web3j,
Credentials credentials, BigInteger gasPrice, BigInteger gasLimit) {
return new WidelyContract(abi, contractBinary, contractAddress, web3j, credentials, gasPrice, gasLimit);
}
@SuppressWarnings("rawtypes")
public Type<?> excute(String functionName, String... params) throws IOException {
List<Type> inputs = null;
List<TypeReference<?>> outputs = null;
for (Function f : functions) {
if (functionName.equals(f.getName())) {
inputs = getInputs(f.getInputs(), params);
outputs = getOutputs(f.getOutputs());
}
}
return executeCallSingleValueReturn(new org.web3j.abi.datatypes.Function(functionName, inputs, outputs));
}
@SuppressWarnings("rawtypes")
private List<Type> getInputs(List<Param> inputs, String[] params) {
int size = inputs.size();
List<Type> result = new ArrayList<Type>();
for (int i = 0; i < size; i++) {
result.add(getParam(inputs.get(i).getType(), params[i]));
}
return result;
}
private List<TypeReference<?>> getOutputs(List<Param> outputs) {
int size = outputs.size();
List<TypeReference<?>> result = new ArrayList<>();
for (int i = 0; i < size; i++) {
result.add(getType(outputs.get(i).getType()));
}
return result;
}
private TypeReference<?> getType(String type) {
switch (type) {
case "address": {
return new TypeReference<Address>() {
};
}
case "string": {
return new TypeReference<Utf8String>() {
};
}
case "bool": {
return new TypeReference<Bool>() {
};
}
}
if (type.startsWith("uint")) {
return new TypeReference<Uint>() {
};
}
if (type.startsWith("int")) {
return new TypeReference<Int>() {
};
}
return null;
}
@SuppressWarnings("rawtypes")
private Type getParam(String type, String input) {
switch (type) {
case "address": {
return new Address(input);
}
case "string": {
return new Utf8String(input);
}
case "bool": {
return new Bool(input.equals("true"));
}
}
if (type.startsWith("uint")) {
return new Uint(new BigInteger(input));
}
if (type.startsWith("int")) {
return new Int(new BigInteger(input));
}
return null;
}
}
另附自定義Function類
import java.util.List;
public class Function {
private boolean constant;
private String name;
private boolean payable;
private String stateMutability;
private String type;
private List<Param> inputs;
private List<Param> outputs;
public boolean isConstant() {
return constant;
}
public void setConstant(boolean constant) {
this.constant = constant;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isPayable() {
return payable;
}
public void setPayable(boolean payable) {
this.payable = payable;
}
public String getStateMutability() {
return stateMutability;
}
public void setStateMutability(String stateMutability) {
this.stateMutability = stateMutability;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public List<Param> getInputs() {
return inputs;
}
public void setInputs(List<Param> inputs) {
this.inputs = inputs;
}
public List<Param> getOutputs() {
return outputs;
}
public void setOutputs(List<Param> outputs) {
this.outputs = outputs;
}
@Override
public String toString() {
return "Function [constant=" + constant + ", name=" + name + ", payable=" + payable + ", stateMutability="
+ stateMutability + ", type=" + type + ", inputs=" + inputs + ", outputs=" + outputs + "]";
}
}
與Param類
public class Param {
private String name;
private String type;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
@Override
public String toString() {
return "Param [name=" + name + ", type=" + type + "]";
}
}