package easyJava.controller;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import easyJava.Configs;
import easyJava.dao.master.BaseDao;
import easyJava.entity.BaseModel;
import easyJava.entity.ResponseEntity;
import easyJava.entity.TransactionMy;
import easyJava.utils.HttpsUtils;
import easyJava.utils.MapBeanUtil;
import io.reactivex.functions.Consumer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.java_websocket.exceptions.WebsocketNotConnectedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.crypto.*;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.admin.Admin;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.response.*;
import org.web3j.protocol.websocket.WebSocketService;
import org.web3j.tx.Transfer;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.net.ConnectException;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.*;
@RestController
public class Web3jController {
private static final Logger logger = LogManager.getLogger(Web3jController.class);
@Autowired
BaseDao baseDao;
public static final String TRANSACTION_RECEIPT_TABLE_NAME = "accounts_coin_transaction_receipts";
public static final String TRANSACTION_TABLE_NAME = "accounts_coin_transactions";
public static final String ACCOUNT_TABLE_NAME = "accounts_coin_balance";
public static final String pwd = "";
WebSocketService ws = new WebSocketService(Configs.getEthHost(), false);
public static final BigInteger GAS_LIMIT = BigInteger.valueOf(21000);
public static final BigInteger ETH_WEI = BigInteger.valueOf(1000000000000000000L);
private void initWsToEthNode() {
try {
ws.close();
} catch (Exception e) {
}
try {
ws = new WebSocketService(Configs.getEthHost(), false);
//订阅交易
// subscribeTransactions(ws);
} catch (Exception e) {
e.printStackTrace();
}
}
public Web3jController() {
try {
ws.connect();
subscribeTransactions(ws);
} catch (ConnectException e) {
e.printStackTrace();
}
}
private void subscribeTransactions(WebSocketService ws) {
Web3j web3 = Web3j.build(ws);
//暂时屏蔽
// web3.transactionFlowable().subscribe(new TransactionConsumer("finish") {
// });
// web3.pendingTransactionFlowable().subscribe(new TransactionConsumer("pending") {
// });
}
public class TransactionConsumer implements Consumer<Transaction> {
private String pending = "";
public TransactionConsumer(String pending) {
this.pending = pending;
}
private Set<String> addressSet = new HashSet<>();
private long lastUpdate = new Date().getTime();
@Override
public void accept(Transaction transaction) {
try {
dealWithPendingTransaction(transaction);
String tStr = JSON.toJSONString(transaction);
TransactionMy t = JSON.parseObject(tStr, TransactionMy.class);
t.setTime(new Date().getTime() + "");
t.setPending(pending);
if (hasEthAccountInOurDB(transaction.getFrom())) {
insertTransaction(t, TRANSACTION_TABLE_NAME);
return;
}
if (hasEthAccountInOurDB(transaction.getTo())) {
insertTransaction(t, TRANSACTION_TABLE_NAME);
return;
}
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean hasEthAccountInOurDB(String address) {
if (addressSet.contains(address)) {
return true;
} else {
if (new Date().getTime() - lastUpdate > 10 * 1000) {
updateAddressSet();
return hasEthAccountInOurDB(address);
}
return false;
}
}
private synchronized void updateAddressSet() {
if (new Date().getTime() - lastUpdate < 10 * 1000) {
return;
}
Map<String, Object> queryMap = new HashMap<>();
queryMap.put("coin_name", "ETH");
//queryMap.put("recharge_address", address);
queryMap.put("tableName", ACCOUNT_TABLE_NAME);
var result = baseDao.selectBaseList(queryMap, new BaseModel().setPageNo(1).setPageSize(100000).setOrderAsc("desc"));
result.forEach(map -> {
if (map.get("recharge_address") != null) {
String address = map.get("recharge_address").toString();
addressSet.add(address);
}
});
lastUpdate = new Date().getTime();
}
}
private static boolean isValidHexQuantity(String value) {
if (value == null) {
return false;
} else if (value.length() < 3) {
return false;
} else {
return value.startsWith("0x");
}
}
private static final String DEFAULT_V = "0x111";
private void dealWithPendingTransaction(Transaction t) {
if (t.getNonceRaw() == null || t.getNonceRaw().length() == 0) {
t.setNonce(DEFAULT_V);
}
if (t.getBlockNumberRaw() == null || t.getBlockNumberRaw().length() == 0) {
t.setBlockNumber(DEFAULT_V);
}
if (t.getTransactionIndexRaw() == null || t.getTransactionIndexRaw().length() == 0) {
t.setTransactionIndex(DEFAULT_V);
}
if (t.getGasPriceRaw() == null || t.getGasPriceRaw().length() == 0) {
t.setGasPrice(DEFAULT_V);
}
if (t.getGasRaw() == null || t.getGasRaw().length() == 0) {
t.setGas(DEFAULT_V);
}
if (t.getValueRaw() == null || t.getValueRaw().length() == 0) {
t.setValue(DEFAULT_V);
}
}
private void insertTransaction(TransactionMy transaction, String tableName) {
Map map = JSON.parseObject(JSON.toJSON(transaction).toString(), Map.class);
map.put("tableName", tableName);
System.out.println(JSON.toJSON(map));
baseDao.insertUpdateBase(map);
}
@GetMapping("/v1/web3j/ethHost")
public ResponseEntity ethHost() {
return new ResponseEntity(Configs.getEthHost());
}
@PostMapping("/v1/web3j/createWallet")
public ResponseEntity createWallet(@RequestParam("uuid") String uuid) throws CipherException, InvalidAlgorithmParameterException,
NoSuchAlgorithmException, NoSuchProviderException, IOException {
Map map = createAccount(uuid);
return new ResponseEntity(map);
}
/*************创建一个钱包文件**************/
private static Map createAccount(String uuid) throws NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, CipherException, IOException {
Map map = new HashMap();
String walletFileName = "";//文件名
String walletFilePath = getWalletFilePath(uuid);
File dir = new File(walletFilePath);
dir.setWritable(true, false);
dir.mkdirs();
//钱包文件保持路径,请替换位自己的某文件夹路径
if (dir.list().length == 0) {
walletFileName = WalletUtils.generateNewWalletFile(pwd, dir, false);
}
Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
//钱包地址 :
String address = credentials.getAddress();
// 公钥16进制字符串表示:
String publicKey = credentials.getEcKeyPair().getPublicKey().toString(16);
//私钥16进制字符串表示:
String privateKey = credentials.getEcKeyPair().getPrivateKey().toString(16);
map.put("uuid", uuid);
map.put("address", address);
map.put("publickey", publicKey);
map.put("privatekey", privateKey);
map.put("walletfilename", walletFileName);
return map;
}
private static String getWalletFilePath(String uuid) {
String walletFilePath = "./eth_wallets/uid_" + uuid + "/";
return walletFilePath;
}
private static String getWalletFilePathName(String uuid) {
String walletFilePath = "./eth_wallets/uid_" + uuid + "/";
File dir = new File(walletFilePath);
dir.setWritable(true, false);
String[] files = dir.list();
if (files != null && files.length != 0) {
return walletFilePath + files[0];
}
return null;
}
/*************创建一个钱包文件**************/
private static Map createAccountRemote(String uuid) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
String urlString = "https://signal.lxrtalk.com/easyJava/v1/web3j/createWallet?uuid=" + uuid;
String response = HttpsUtils.Post(urlString, "text/json", null);
ResponseEntity responseEntity = objectMapper.readValue(response, ResponseEntity.class);
Map<String, Object> dataMap = responseEntity.getData();
Map<String, Object> walletMap = (Map<String, Object>) dataMap.get("data");
return walletMap;
}
/**
* 转账金额是到账金额,手续费从转出账户余额扣除
*
* @param uuid
* @param toAddress
* @param balance
* @return
* @throws Exception
*/
@PostMapping("/v1/web3j/transferWithoutFee")
public ResponseEntity transferWithoutFee(@RequestParam("uuid") String uuid, @RequestParam("toAddress") String toAddress
, @RequestParam("balance") double balance) throws Exception {
Admin web3 = Admin.build(ws); // defaults to http://localhost:8545/
Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
TransactionReceipt transactionReceipt = Transfer.sendFunds(
web3, credentials, toAddress,
BigDecimal.valueOf(balance), Convert.Unit.ETHER)
.send();
transactionReceipt.setLogsBloom("");
Map map = MapBeanUtil.object2Map(transactionReceipt);
map.remove("logs");
map.remove("logsBloom");
map.put("tableName", TRANSACTION_RECEIPT_TABLE_NAME);
baseDao.insertBase(map);
return new ResponseEntity(transactionReceipt);
}
/**
* 转账金额包含手续费,因此到账会少一部分
*
* @param uuid
* @param toAddress
* @param balance
* @return
* @throws Exception
*/
@PostMapping("/v1/web3j/transfer")
public ResponseEntity transfer(@RequestParam("uuid") String uuid, @RequestParam("toAddress") String toAddress
, @RequestParam("balance") double balance) throws Exception {
Admin web3 = Admin.build(ws); // defaults to http://localhost:8545/
EthGasPrice ethGasPrice = web3.ethGasPrice().send();
BigDecimal b = new BigDecimal(balance).multiply(new BigDecimal(ETH_WEI));
BigDecimal balanceWithoutFee = b.subtract(new BigDecimal(ethGasPrice.getGasPrice().multiply(GAS_LIMIT)))
.divide(new BigDecimal(ETH_WEI))
.setScale(15, RoundingMode.DOWN);
Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
TransactionReceipt transactionReceipt = Transfer.sendFunds(
web3, credentials, toAddress,
balanceWithoutFee, Convert.Unit.ETHER)
.send();
transactionReceipt.setLogsBloom("");
Map map = MapBeanUtil.object2Map(transactionReceipt);
map.remove("logs");
map.remove("logsBloom");
map.put("tableName", TRANSACTION_RECEIPT_TABLE_NAME);
baseDao.insertBase(map);
return new ResponseEntity(transactionReceipt);
}
@GetMapping("/v1/web3j/transfer/history")
public ResponseEntity history(@RequestParam("uuid") String uuid, @RequestParam("pageNo") int pageNo, @RequestParam("pageSize") int pageSize) throws IOException, CipherException {
BaseModel baseModel = new BaseModel();
baseModel.setPageNo(pageNo);
baseModel.setPageSize(pageSize);
Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
Map map = new HashMap();
map.put("to", credentials.getAddress());
map.put("from", credentials.getAddress());
map.put("tableName", TRANSACTION_TABLE_NAME);
baseModel.setOrderAsc("desc");
int count = baseDao.selectBaseCountOr(map);
List<Map> list = baseDao.selectBaseListOr(map, baseModel);
return new ResponseEntity(list, count, baseModel.getPageNo(), baseModel.getPageSize());
}
@GetMapping("/v1/web3j/balance")
public ResponseEntity ethGetBalance(@RequestParam("address") String address) throws Exception {
Web3j web3 = Web3j.build(ws); // defaults to http://localhost:8545/
try {
EthGetBalance ret = web3.ethGetBalance(address, DefaultBlockParameter.valueOf("latest")).send();
BigDecimal balance = new BigDecimal(ret.getBalance()).setScale(6)
.divide((new BigDecimal(10.000000).pow(18)).setScale(6), RoundingMode.HALF_DOWN).setScale(6);
logger.info("ethGetBalance,address:" + address + ",balance:" + ret.getBalance());
return new ResponseEntity(balance.toPlainString());
} catch (WebsocketNotConnectedException e) {
initWsToEthNode();
logger.error("ethGetBalance", e);
}
return new ResponseEntity(400, "查询失败。");
}
@GetMapping("/v1/web3j/gasPrice")
public ResponseEntity gasPrice() throws Exception {
Web3j web3 = Web3j.build(ws);
EthGasPrice ret = web3.ethGasPrice().send();
return new ResponseEntity(ret);
}
@GetMapping("/v1/web3j/getTX")
public ResponseEntity getTX(@RequestParam("transactionHash") String transactionHash) throws Exception {
Web3j web3 = Web3j.build(ws);
EthTransaction ret = web3.ethGetTransactionByHash(transactionHash).send();
return new ResponseEntity(ret);
}
@GetMapping("/v1/web3j/estimate")
public ResponseEntity estimate(@RequestParam("uuid") String uuid, @RequestParam("toAddress") String toAddress
, @RequestParam("balance") double balance) {
Web3j web3 = Web3j.build(ws);
try {
BigDecimal v = BigDecimal.valueOf(balance).multiply(new BigDecimal("10000000000000000"));
EthGasPrice ethGasPrice = web3.ethGasPrice().send();
Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
EthEstimateGas gas = web3.ethEstimateGas(new org.web3j.protocol.core.methods.request.Transaction(credentials.getAddress(), BigInteger.valueOf(1),
ethGasPrice.getGasPrice(), GAS_LIMIT, toAddress, v.toBigInteger(), "")).send();
return new ResponseEntity(gas.getResult());
} catch (Exception e) {
logger.error("estimate:", e);
}
return new ResponseEntity();
}
@PostMapping("/v1/web3j/transferETH")
public ResponseEntity transferETH(@RequestParam("uuid") String uuid, @RequestParam("toAddress") String toAddress
, @RequestParam("balance") double balance) throws Exception {
Admin web3 = Admin.build(ws);
Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
TransactionReceipt transactionReceipt = Transfer.sendFunds(
web3, credentials, toAddress,
BigDecimal.valueOf(balance).multiply(new BigDecimal(10).pow(18)), Convert.Unit.WEI)
.send();
transactionReceipt.setLogsBloom("");
return new ResponseEntity(transactionReceipt);
}
@PostMapping("/v1/web3j/transferTrash")
public ResponseEntity transferTrash(@RequestParam("uuid") String uuid, @RequestParam("toAddress") String toAddress
, @RequestParam("balance") double balance) throws Exception {
String transactionHash = trashferTrashStatic(ws, uuid, toAddress, balance);
System.out.println(transactionHash);
//todo 日志入库
// Map map = MapBeanUtil.object2Map(transactionReceipt);
// map.remove("logs");
// map.remove("logsBloom");
// map.put("tableName", TRANSACTION_RECEIPT_TABLE_NAME);
// baseDao.insertBase(map);
return new ResponseEntity(transactionHash);
}
public static String trashferTrashStatic(WebSocketService ws, String uuid, String toAddress, double balance) {
logger.info("-------------trashferTrashStatic start--------" + toAddress + "------" + balance);
String transactionHash = "start";
try {
Admin web3j = Admin.build(ws);
EthGasPrice ethGasPrice = web3j.ethGasPrice().send();
logger.info("-------------trashferTrashStatic ethGasPrice--------" + ethGasPrice.getGasPrice() + "------");
Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
String fromAddress = credentials.getAddress();
EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(
fromAddress, DefaultBlockParameterName.LATEST).sendAsync().get();
BigInteger nonce = ethGetTransactionCount.getTransactionCount();
BigDecimal balanceDecimal = new BigDecimal(balance).multiply(new BigDecimal(10).pow(18));
Function function = new Function(
"transfer",
Arrays.asList(new Address(toAddress), new Uint256(balanceDecimal.toBigInteger())),
Arrays.asList(new TypeReference<Type>() {
}));
String encodedFunction = FunctionEncoder.encode(function);
RawTransaction rawTransaction = RawTransaction.createTransaction(nonce,
//代币转账gas通常多一些
ethGasPrice.getGasPrice(),
GAS_LIMIT.multiply(new BigInteger("3")),
Configs.getTrashContractAddr(), encodedFunction);
byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
String hexValue = Numeric.toHexString(signedMessage);
EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();
transactionHash = ethSendTransaction.getTransactionHash();
logger.info("-------------trashferTrashStatic end transactionHash:" + transactionHash + "--------");
if (transactionHash == null) {
logger.error("-------------trashferTrashStatic error:" + ethSendTransaction.getError().getMessage() + "--------");
}
} catch (Exception e) {
e.printStackTrace();
logger.error("trashferTrashStatic", e);
}
return transactionHash;
}
public static String mintTrashNFTStatic(WebSocketService ws, String uuid, String toAddress) {
logger.info("-------------mintTrashNFTStatic start--------" + toAddress + "------");
String transactionHash = "start";
try {
Admin web3j = Admin.build(ws);
EthGasPrice ethGasPrice = web3j.ethGasPrice().send();
logger.info("-------------mintTrashNFTStatic ethGasPrice--------" + ethGasPrice.getGasPrice() + "------");
Credentials credentials = WalletUtils.loadCredentials(pwd, getWalletFilePathName(uuid));
String fromAddress = credentials.getAddress();
EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(
fromAddress, DefaultBlockParameterName.LATEST).sendAsync().get();
BigInteger nonce = ethGetTransactionCount.getTransactionCount();
Function function = new Function(
"mint",
Arrays.asList(new Address(toAddress)),
Arrays.asList(new TypeReference<Type>() {
}));
String encodedFunction = FunctionEncoder.encode(function);
RawTransaction rawTransaction = RawTransaction.createTransaction(nonce,
//代币转账gas通常多一些
ethGasPrice.getGasPrice(),
GAS_LIMIT.multiply(new BigInteger("3")),
Configs.getTrashNftContractAddr(), encodedFunction);
byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
String hexValue = Numeric.toHexString(signedMessage);
EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).send();
transactionHash = ethSendTransaction.getTransactionHash();
logger.info("-------------mintTrashNFTStatic end transactionHash:" + transactionHash + "--------");
if (transactionHash == null) {
logger.error("-------------mintTrashNFTStatic error:" + ethSendTransaction.getError().getMessage() + "--------");
}
} catch (Exception e) {
e.printStackTrace();
logger.error("mintTrashNFTStatic", e);
}
return transactionHash;
}
@PostMapping("/v1/web3j/systemAcc")
public ResponseEntity systemAcc() throws Exception {
var bigInteger=Numeric.toBigInt(Configs.getSystemPrivate());
ECKeyPair ecKeyPair = ECKeyPair.create(bigInteger);
//手动创建以下目录后,调用
String walletFilePath = "./eth_wallets/uid_system/";
String filename = WalletUtils.generateWalletFile(pwd, ecKeyPair, new File(walletFilePath), false);
return new ResponseEntity(filename);
}
@PostMapping("/v1/web3j/systemNftAcc")
public ResponseEntity systemNftAcc() throws Exception {
var bigInteger=Numeric.toBigInt(Configs.getSystemNftPrivate());
ECKeyPair ecKeyPair = ECKeyPair.create(bigInteger);
//手动创建以下目录后,调用
String walletFilePath = "./eth_wallets/uid_system_nft/";
String filename = WalletUtils.generateWalletFile(pwd, ecKeyPair, new File(walletFilePath), false);
return new ResponseEntity(filename);
}
public static void main(String[] args) throws Exception {
var bigInteger=Numeric.toBigInt(Configs.getSystemNftPrivate());
ECKeyPair ecKeyPair = ECKeyPair.create(bigInteger);
System.out.println(Configs.getSystemNftPrivate());
//手动创建以下目录后,调用
String walletFilePath = "./";
System.out.println(ecKeyPair.getPrivateKey());
String filename = WalletUtils.generateWalletFile(pwd, ecKeyPair, new File(walletFilePath), false);
}
}