對于fil的充提,公司原來用的是www.ztpay.org這個網站提供的服務,這個平臺后面卷款跑路了,導致公司損失了十幾萬,沒辦法,后面想去找一些比較可靠的第三方平臺,但是由于每個月的服務費都要幾萬塊,所以公司就要求能不能自己部署節點,然后自己提供充提服務,但是部署節點有個問題是,由于服務器的性能不是很好,磁盤容量不夠,導致需要經常清資料,這樣的運維成本比較大,后面發現可以直接使用infura提供的服務,Ethereum API | IPFS API & Gateway | ETH Nodes as a Service | Infura
但是infura有個缺點就是,它不提供完整的區塊鏈資訊,只是提供三天內產生的區塊資訊,這個要特別注意一下,不過這個對于大多數的應用來說,都不是什么問題,
一、在infura上注冊一個應用
1、注冊并登陸infura平臺,進入到以下控制臺

2、點擊左邊的選單欄"FILCOIN",進入filcoin的設定頁面

3、點擊右上角的"CREATE NEW PROJECT"按鈕,在彈出的彈框中輸入專案的名稱


4、如下圖所示,我們就在infura創建了一個filCoin的專案,記住“PROJECT ID” 和"PROJECT SECRET",這兩個值在后面的操作中,我們要用到,

二、關鍵代碼
1、fil節點地址配置類
public class FilApiConfig {
//指定fil節點的地址
public final static String FIL_RPC_URL = "https://filecoin.infura.io";
public final static String FIL_METHOD_CHAINGETTIPSETBYHEIGHT = "Filecoin.ChainGetTipSetByHeight";
public final static String FIL_METHOD_CHAINGETBLOCKMESSAGES = "Filecoin.ChainGetBlockMessages";
public final static String FIL_METHOD_CHAINHEAD = "Filecoin.ChainHead";
}
2、fil的http請求工具類(主要配置權限認證)
import com.alibaba.fastjson.JSONObject;
import com.alpha.fil.mining.pool.common.util.filChain.config.FilApiConfig;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.Credentials;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class FilHttpUtils {
private static RestTemplate restTemplate;
private static ObjectMapper objectMapper = new ObjectMapper();
private static HttpHeaders headers = new HttpHeaders();
private static JSONObject jsonObject = new JSONObject();
static {
SimpleClientHttpRequestFactory clientHttpRequestFactory = new SimpleClientHttpRequestFactory();
clientHttpRequestFactory.setConnectTimeout(20 * 1000);
clientHttpRequestFactory.setReadTimeout(20 * 1000);
restTemplate = new RestTemplate(clientHttpRequestFactory);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
headers.add("Content-Type", "application/json");
//Infura提供的訪問點需要http basic身份認證
String credential = Credentials.basic("專案ID", "秘鑰");
//如果是自己部署的fil節點,則在節點配置上拿到請求秘鑰,配置如下
//String credential = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJBbGxvdyI6WyJyZWFkIiwid3JpdGUiLCJzaWduIiwiYWRtaW4iXX0.eqQKY2EE9dgQ3f6PgEM7B-IEPLMYWJpkOZkUf-BemhE";
headers.add("Authorization", credential);
jsonObject.put("jsonrpc", "2.0");
jsonObject.put("id", 1);
}
public static Optional<String> post(List<Object> object, String method) {
jsonObject.put("method", method);
jsonObject.put("params", object);
HttpEntity<String> httpEntity = new HttpEntity<>(jsonObject.toString(), headers);
ResponseEntity<String> mapResponseEntity = restTemplate.postForEntity(FilApiConfig.FIL_RPC_URL, httpEntity, String.class);
return Optional.ofNullable(mapResponseEntity).filter(x -> x.getStatusCode() == HttpStatus.OK).map(x -> x.getBody());
}
public static Optional<String> get(String url) {
try {
ResponseEntity<String> mapResponseEntity = restTemplate.getForEntity(url, String.class);
return Optional.ofNullable(mapResponseEntity).filter(x -> x.getStatusCode() == HttpStatus.OK).map(x -> x.getBody());
} catch (RestClientException e) {
e.printStackTrace();
}
return Optional.empty();
}
}
3、fil的操作類(包含充提操作,測驗例子都在main函式中)
package com.canye.fil.demo.filChain;
import cn.hutool.core.codec.Base32;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.HexUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.canye.fil.demo.filChain.blake.Blake2b;
import com.canye.fil.demo.filChain.config.FilecoinCnt;
import com.canye.fil.demo.filChain.crypto.ECKey;
import com.canye.fil.demo.filChain.handler.TransactionHandler;
import com.canye.fil.demo.filChain.vo.*;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* fil的工具類
* @author plb
* @date 2021-06-04 14:50
*/
public class FilCoinUtil {
/**
* 獲取余額
* @param address 地址
* @return
*/
public static BigDecimal getBalance(String address){
try{
String method = "Filecoin.WalletBalance";
List<Object> object = new ArrayList<>();
object.add(address);
Optional optional = FilHttpUtils.post(object,method);
JSONObject dataJson = JSONObject.parseObject(String.valueOf(optional.get()));
System.out.println("FilCoinUtil.getBalance " + address + " | res:" + dataJson);
BigDecimal amount = dataJson.getBigDecimal("result");
amount = amount.divide(BigDecimal.TEN.pow(18));
return amount;
}catch (Exception e){
System.out.println("FilCoinUtil.getBalance: " + address + e.getMessage());
throw new RuntimeException("獲取余額失敗",e);
}
}
/**
* 檢測地址是否有效
* @param address 地址
* @return
*/
public static boolean checkAddress(String address){
try{
String method = "Filecoin.WalletValidateAddress";
List<Object> object = new ArrayList<>();
object.add(address);
Optional optional = FilHttpUtils.post(object,method);
JSONObject dataJson = JSONObject.parseObject(String.valueOf(optional.get()));
System.out.println("FilCoinUtil.checkAddress " + address + "|res:" + dataJson);
if(dataJson.containsKey("result") && dataJson.getString("result").equals(address)) {
return true;
}
return false;
}catch (Exception e){
System.out.println("FilCoinUtil.checkAddress: " + address+ e.getMessage());
throw new RuntimeException("檢驗地址失敗",e);
}
}
/**
* 生成地址
* @return
*/
public static WalletResult createWallet() {
try {
ECKey ecKey = new ECKey();
byte[] privKeyBytes = ecKey.getPrivKeyBytes();
byte[] pubKey = ecKey.getPubKey();
if (privKeyBytes == null || privKeyBytes.length < 1) {
throw new RuntimeException("create wallet error");
}
String filAddress = byteToAddress(pubKey);
String privatekey = HexUtil.encodeHexStr(privKeyBytes);
return WalletResult.builder().address(filAddress).privatekey(privatekey).build();
}catch (Exception e){
System.out.println("createWallet error"+ e.getMessage());
throw new RuntimeException("生成地址失敗!");
}
}
/**
* 位元組轉地址
* @param pub
* @return
*/
private static String byteToAddress(byte[] pub) {
Blake2b.Digest digest = Blake2b.Digest.newInstance(20);
String hash = HexUtil.encodeHexStr(digest.digest(pub));
//4.計算校驗和
String pubKeyHash = "01" + HexUtil.encodeHexStr(digest.digest(pub));
Blake2b.Digest blake2b3 = Blake2b.Digest.newInstance(4);
String checksum = HexUtil.encodeHexStr(blake2b3.digest(HexUtil.decodeHex(pubKeyHash)));
//5.生成地址
return "f1" + Base32.encode(HexUtil.decodeHex(hash + checksum)).toLowerCase();
}
/**
* 根據區塊鏈高度獲取區塊鏈資訊
*/
public static ChainResult getChainTipSetByHeight(BigInteger heigth){
try{
String method = "Filecoin.ChainGetTipSetByHeight";
List<Object> object = new ArrayList<>();
object.add(heigth);
object.add(null);
Optional optional = FilHttpUtils.post(object,method);
JSONObject dataJson = JSONObject.parseObject(String.valueOf(optional.get()));
System.out.println("FilCoinUtil.getChainTipSetByHeight " + heigth + " | res:" + dataJson);
ChainResult chainResult = chainJsonToChainResult(dataJson);
return chainResult;
}catch (Exception e){
System.out.println("FilCoinUtil.getChainTipSetByHeight: " + heigth+ e.getMessage());
throw new RuntimeException("getChainTipSetByHeight error",e);
}
}
/**
* 區塊鏈json資料轉物件
* @param jsonObject
* @return
*/
private static ChainResult chainJsonToChainResult(JSONObject jsonObject){
JSONObject result = jsonObject.getJSONObject("result");
BigInteger height = result.getBigInteger("Height");
ArrayList<String> cidList = new ArrayList<>();
ArrayList<String> parentCidList = new ArrayList<>();
JSONArray cidJsonArr = result.getJSONArray("Cids");
JSONArray blocksArr = result.getJSONArray("Blocks");
if (cidJsonArr != null){
for (Object o : cidJsonArr) {
cn.hutool.json.JSONObject cidKy = new cn.hutool.json.JSONObject(o);
String cid = cidKy.getStr("/");
if (!StringUtils.isEmpty(cid)){
cidList.add(cid);
}
}
}
if (blocksArr != null && blocksArr.size() > 0){
JSONArray cidArr = JSONObject.parseObject(String.valueOf(blocksArr.get(0))).getJSONArray("Parents");
if (cidArr != null){
for (Object o : cidArr) {
String cid = new cn.hutool.json.JSONObject(o).getStr("/");
if (!StringUtils.isEmpty(cid)){
parentCidList.add(cid);
}
}
}
}
return ChainResult.builder().height(height).blockCidList(cidList).parentBlockCidList(parentCidList).build();
}
/**
* 根據訊息cid獲取訊息詳情
* @param cid 訊息cid
*/
public static MessagesResult getMessageByCid(String cid){
try{
String method = "Filecoin.ChainGetMessage";
JSONObject jsonObject = new JSONObject();
jsonObject.put("/", cid);
List<Object> object = new ArrayList<>();
object.add(jsonObject);
Optional optional = FilHttpUtils.post(object,method);
JSONObject dataJson = JSONObject.parseObject(String.valueOf(optional.get()));
System.out.println(optional.get());
System.out.println("FilCoinUtil.getTradeMessageByCid " + cid + " | res:" + dataJson);
System.out.println(dataJson);
MessagesResult messagesResult = messagesJsonToMessagesResult(dataJson.getJSONObject("result"));
return messagesResult;
}catch (Exception e){
System.out.println("FilCoinUtil.getTradeMessageByCid: " + cid + e.getMessage());
throw new RuntimeException("獲取訊息詳情失敗",e);
}
}
/**
* 訊息json轉為訊息物件
* @param jsonObject
* @return
*/
private static MessagesResult messagesJsonToMessagesResult(JSONObject jsonObject){
return MessagesResult.builder().from(jsonObject.getString("From"))
.to(jsonObject.getString("To"))
.version(jsonObject.getInteger("Version"))
.nonce(jsonObject.getInteger("Nonce"))
.value(Convert.fromAtto(jsonObject.getBigInteger("Value").toString(), Convert.Unit.FIL))
.gasLimit(jsonObject.getInteger("GasLimit"))
.gasFeeCap(jsonObject.getBigInteger("GasFeeCap"))
.gasPremium(jsonObject.getBigInteger("GasPremium"))
.method(jsonObject.getInteger("Method"))
.params(jsonObject.getString("Params"))
.cid(jsonObject.getJSONObject("CID")
.getString("/"))
.build();
}
/**
* 根據區塊cid獲取區塊內所有訊息
* @param blockCid 區塊id
* @return
*/
public static List<MessagesResult> getMessagesByBlockCid(String blockCid){
try{
String method = "Filecoin.ChainGetBlockMessages";
JSONObject _cid = new JSONObject();
_cid.put("/", blockCid);
List<Object> object = new ArrayList<>();
object.add(_cid);
Optional optional = FilHttpUtils.post(object,method);
JSONObject dataJson = JSONObject.parseObject(String.valueOf(optional.get()));
System.out.println("FilCoinUtil.getMessagesByBlockCid " + blockCid + " | res:" + dataJson);
System.out.println(dataJson.toJSONString());
ArrayList<MessagesResult> messagesResults = new ArrayList<>();
JSONObject resultJson = dataJson.getJSONObject("result");
if(resultJson == null){
resultJson = new JSONObject();
}
JSONArray blsMessagesArr = resultJson.getJSONArray("BlsMessages");
JSONArray secpkMessagesArr = resultJson.getJSONArray("SecpkMessages");
if (blsMessagesArr != null){
for (Object o : blsMessagesArr) {
messagesResults.add(messagesJsonToMessagesResult(JSONObject.parseObject(String.valueOf(o))));
}
}
if (secpkMessagesArr != null){
for (Object o : secpkMessagesArr) {
JSONObject secpkMessagesObj = JSONObject.parseObject(String.valueOf(o));
JSONObject message = secpkMessagesObj.getJSONObject("Message");
message.put("CID", secpkMessagesObj.get("CID"));
messagesResults.add(messagesJsonToMessagesResult(message));
}
}
return messagesResults;
}catch (Exception e){
System.out.println("FilCoinUtil.getMessagesByBlockCid: " + blockCid + e.getMessage());
throw new RuntimeException("獲取區塊內所有訊息",e);
}
}
/**
* 獲取指定高度的所有訊息
* @param height
* @return
*/
public static ChainMessagesResult getAllMessagesByHeight(BigInteger height) {
ChainMessagesResult res = null;
ChainResult chainTipSet = getChainTipSetByHeight(height);
if (chainTipSet != null && chainTipSet.getBlockCidList() != null){
res = ChainMessagesResult.builder().blockCidList(chainTipSet.getBlockCidList()).build();
ArrayList<MessagesResult> messageList = new ArrayList<>();
for (String blockCid : chainTipSet.getBlockCidList()) {
List<MessagesResult> messagesList = getMessagesByBlockCid(blockCid);
messageList.addAll(messagesList);
}
//訊息去重
List list = messageList.stream().distinct().collect(Collectors.toList());
res.setMessageList(list);
}
return res;
}
/**
* 獲取nonce值
* @param address 地址
* @return
*/
public static int getNonce(String address) {
try{
String method = FilecoinCnt.GET_NONCE;
List<Object> object = new ArrayList<>();
object.add(address);
Optional optional = FilHttpUtils.post(object,method);
JSONObject dataJson = JSONObject.parseObject(String.valueOf(optional.get()));
System.out.println("FilCoinUtil.getNonce " + address + " | res:" + dataJson);
return dataJson.getIntValue("result");
}catch (Exception e){
System.out.println("FilCoinUtil.getNonce: " + address + e.getMessage());
throw new RuntimeException("獲取Nonce失敗",e);
}
}
/**
* 獲取gas資訊
* @param gas
* @return
*/
public static GasResult getGas(GetGas gas) {
if (gas == null || StringUtils.isBlank(gas.getFrom())
|| StringUtils.isBlank(gas.getTo())
|| gas.getValue() == null) {
throw new RuntimeException("paramter cannot be empty");
}
if (gas.getValue().compareTo(BigInteger.ZERO) < 1) {
throw new RuntimeException("the transfer amount must be greater than 0 !" + JSONObject.toJSONString(gas));
}
List<Object> list = new ArrayList<>();
JSONObject json = new JSONObject();
json.put("From", gas.getFrom());
json.put("To", gas.getTo());
json.put("Value", gas.getValue().toString());
list.add(json);
list.add(null);
list.add(null);
Optional optional = FilHttpUtils.post(list,FilecoinCnt.GET_GAS);
GasResult gasResult = null;
try {
JSONObject result = JSONObject.parseObject(String.valueOf(optional.get()));
JSONObject jsonObject = result.getJSONObject("result");
gasResult = GasResult.builder().gasFeeCap(jsonObject.getString("GasFeeCap"))
.gasLimit(jsonObject.getBigInteger("GasLimit"))
.gasPremium(jsonObject.getString("GasPremium")).build();
return gasResult;
} catch (Exception e) {
System.out.println("FilCoinUtil.getGas " + e.getMessage());
throw new RuntimeException("get gas error " ,e);
}
}
public static SendResult send(Transaction transaction, String privatekey) throws RuntimeException {
if (transaction == null || StringUtils.isBlank(transaction.getFrom())
|| StringUtils.isBlank(transaction.getTo())
|| StringUtils.isBlank(transaction.getGasFeeCap())
|| StringUtils.isBlank(transaction.getGasPremium())
|| StringUtils.isBlank(transaction.getValue())
|| transaction.getGasLimit() == null
|| transaction.getMethod() == null
|| transaction.getNonce() == null
|| StringUtils.isBlank(privatekey)) {
throw new RuntimeException("parameter cnanot be empty");
}
BigInteger account = new BigInteger(transaction.getValue());
if (account.compareTo(BigInteger.ZERO) < 1) {
throw new RuntimeException("the transfer amount must be greater than 0");
}
byte[] cidHash = null;
try {
cidHash = TransactionHandler.transactionSerialize(transaction);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("transaction entity serialization failed");
}
//簽名
ECKey ecKey = ECKey.fromPrivate(HexUtil.decodeHex(privatekey));
String sing = Base64.encode(ecKey.sign(cidHash).toByteArray());
List<Object> list = new ArrayList<>();
JSONObject signatureJson = new JSONObject();
JSONObject messageJson = JSONObject.parseObject(JSONObject.toJSONString(transaction));
JSONObject json = new JSONObject();
messageJson.put("version", 0);
signatureJson.put("data", sing);
signatureJson.put("type", 1);
json.put("message", messageJson);
json.put("signature", signatureJson);
list.add(json);
SendResult build = null;
Optional optional = null;
try {
optional = FilHttpUtils.post(list,FilecoinCnt.BOARD_TRANSACTION);
JSONObject executeJson = JSONObject.parseObject(String.valueOf(optional.get()));
String result = executeJson.getJSONObject("result").getString("/");
build = SendResult.builder().cid(result)
.nonce(transaction.getNonce()).build();
return build;
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("send error " + optional , e);
}
}
public static SendResult easySend(EasySend send) {
if (send == null || StringUtils.isBlank(send.getFrom())
|| StringUtils.isBlank(send.getTo())
|| StringUtils.isBlank(send.getPrivatekey())
|| send.getValue() == null) {
throw new RuntimeException("parameter cannot be empty");
}
BigDecimal amount = Convert.toAtto(send.getValue(), Convert.Unit.FIL);
BigInteger bigInteger = amount.toBigInteger();
//獲取gas
GasResult gas = getGas(GetGas.builder().from(send.getFrom())
.to(send.getTo())
.value(bigInteger).build());
//獲取nonce
int nonce = getNonce(send.getFrom());
//拼裝交易引數
Transaction transaction = Transaction.builder().from(send.getFrom())
.to(send.getTo())
.gasFeeCap(gas.getGasFeeCap())
.gasLimit(gas.getGasLimit().longValue() * 2)
.gasPremium(gas.getGasPremium())
.method(0L)
.nonce((long) nonce)
.params("")
.value(bigInteger.toString()).build();
return send(transaction, send.getPrivatekey());
}
/**
* 獲取訊息收據
* @param messageCid 訊息id
* @return
*/
public static StateGetReceiptResult stateGetReceipt(String messageCid) {
List<Object> list = new ArrayList<>();
HashMap<String, String> cidParam = new HashMap<>(8);
cidParam.put("/", messageCid);
list.add(cidParam);
list.add(null);
StateGetReceiptResult res = null;
JSONObject jsonObject = null;
try {
Optional optional = FilHttpUtils.post(list,FilecoinCnt.STATE_GET_RECEIPT);
jsonObject = JSONObject.parseObject(String.valueOf(optional.get()));
JSONObject result = jsonObject.getJSONObject("result");
res = StateGetReceiptResult.builder().exitCode(result.getInteger("ExitCode"))
.messageReturn(result.getString("Return"))
.gasUsed(result.getBigInteger("GasUsed"))
.build();
return res;
} catch (Exception e) {
System.out.println("stateGetReceipt error messgeid:" + messageCid + ",jsonObject=" + JSON.toJSONString(jsonObject) + e.getMessage());
throw new RuntimeException("stateGetReceipt error " + messageCid + ",jsonObject=" + JSON.toJSONString(jsonObject) + e.getMessage());
}
}
public static void main(String[] args){
//獲取地址余額
//System.out.println(getBalance("f1l3aboc6csauxuqrb2mftpvsjahuli4gxro6tllq"));
//校驗地址
// System.out.println(checkAddress("f1l3aboc6csauxuqrb2mftpvsjahuli4gxro6tll2"));
//測驗生成地址
//System.out.println(createWallet());
//獲取某個高度的所有訊息(充值到賬)
// ChainMessagesResult chainMessagesResult = getAllMessagesByHeight(BigInteger.valueOf(1142577L));
// for(MessagesResult messagesResult:chainMessagesResult.getMessageList()){
//
// //判斷訊息是否是交易資訊,等于0
// if(messagesResult.getMethod().intValue() == 0) {
// //校驗交易是否正常
// StateGetReceiptResult stateGetReceiptResult = FilCoinUtil.stateGetReceipt(messagesResult.getCid());
// if(stateGetReceiptResult.getExitCode().intValue() == 0 ) {
// //到賬地址
// String toAddress = messagesResult.getTo();
// //交易hash
// String hash = messagesResult.getCid();
// //todo 業務處理,充值到賬
// }
// }
// }
//測驗提幣上鏈
// //私鑰
// String privatekey = "fil秘鑰";
// EasySend easySend = new EasySend();
// //來源地址
// easySend.setFrom("f1sle4slx2xqap7p2ehatextarj5nlgeilclgs723");
// //目標地址
// easySend.setTo("f1sle4slx2xqap7p2ehatextarj5nlgeilclgs7qi");
// easySend.setPrivatekey(privatekey);
// //轉賬金額
// BigDecimal amount = new BigDecimal("0.0001");
// easySend.setValue(amount);
// System.out.println(easySend(easySend));
}
}
如在使用的程序中,有問題,麻煩發資訊到郵箱408337459@qq.com
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/303364.html
標籤:區塊鏈
上一篇:專案管理(PMP)認證介紹
