NFTService.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. package com.izouma.nineth.service;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONArray;
  4. import com.alipay.mychain.sdk.api.utils.Utils;
  5. import com.antfinancial.mychain.baas.tool.restclient.RestClient;
  6. import com.antfinancial.mychain.baas.tool.restclient.RestClientProperties;
  7. import com.antfinancial.mychain.baas.tool.restclient.model.CallRestBizParam;
  8. import com.antfinancial.mychain.baas.tool.restclient.model.Method;
  9. import com.antfinancial.mychain.baas.tool.restclient.model.ReceiptDecoration;
  10. import com.antfinancial.mychain.baas.tool.restclient.response.BaseResp;
  11. import com.izouma.nineth.config.Constants;
  12. import com.izouma.nineth.config.GeneralProperties;
  13. import com.izouma.nineth.dto.NFT;
  14. import com.izouma.nineth.dto.NFTAccount;
  15. import com.izouma.nineth.event.AccountCreatedEvent;
  16. import com.izouma.nineth.exception.BusinessException;
  17. import com.izouma.nineth.utils.SnowflakeIdWorker;
  18. import lombok.AllArgsConstructor;
  19. import lombok.extern.slf4j.Slf4j;
  20. import org.apache.commons.codec.binary.Hex;
  21. import org.springframework.context.ApplicationContext;
  22. import org.springframework.core.env.Environment;
  23. import org.springframework.retry.annotation.Backoff;
  24. import org.springframework.retry.annotation.Retryable;
  25. import org.springframework.scheduling.annotation.Async;
  26. import org.springframework.stereotype.Service;
  27. import java.math.BigInteger;
  28. import java.util.ArrayList;
  29. import java.util.Arrays;
  30. import java.util.List;
  31. @Service
  32. @Slf4j
  33. @AllArgsConstructor
  34. public class NFTService {
  35. private final RestClient restClient;
  36. private final RestClientProperties restClientProperties;
  37. private final GeneralProperties generalProperties;
  38. private final SnowflakeIdWorker snowflakeIdWorker;
  39. private final ApplicationContext context;
  40. private final Environment env;
  41. @Retryable(maxAttempts = 10, backoff = @Backoff(delay = 5000), value = BusinessException.class)
  42. public NFTAccount createAccount(String username) {
  43. CallRestBizParam callRestBizParam = CallRestBizParam.builder()
  44. .orderId(String.valueOf(snowflakeIdWorker.nextId()))
  45. .bizid(Constants.bizId)
  46. .account(restClientProperties.getAccount())
  47. .mykmsKeyId(restClientProperties.getKmsId())
  48. .newAccountId(username)
  49. .newAccountKmsId(Constants.kmsKey)
  50. .method(Method.TENANTCREATEACCUNT)
  51. .gas(100000L).build();
  52. try {
  53. BaseResp baseResp = restClient.chainCallForBiz(callRestBizParam);
  54. NFTAccount account = new NFTAccount(username, Constants.kmsKey, baseResp.getData());
  55. if (baseResp.isSuccess() || "400123".equals(baseResp.getCode())) {
  56. log.info("创建账户成功 {}", account);
  57. return account;
  58. } else {
  59. throw new RuntimeException(baseResp.getCode());
  60. }
  61. } catch (Exception e) {
  62. e.printStackTrace();
  63. log.error("创建账户失败", e);
  64. throw new BusinessException("创建账户失败");
  65. }
  66. }
  67. @Retryable(maxAttempts = 10, backoff = @Backoff(delay = 5000), value = BusinessException.class)
  68. public synchronized NFT createToken(String toAccount, String tokenId) throws Exception {
  69. log.info("开始执行EVM合约mint, toAccount: {}, tokenId: {}", toAccount, tokenId);
  70. if (Arrays.asList(env.getActiveProfiles()).contains("notifytest")) {
  71. NFT nft = new NFT("1", tokenId, new BigInteger("1"), new BigInteger("1"));
  72. log.info("测试服生成假token {}", tokenId);
  73. return nft;
  74. }
  75. JSONArray jsonArray = new JSONArray();
  76. jsonArray.add(Utils.getIdentityByName(toAccount));
  77. jsonArray.add(new BigInteger(tokenId, 16));
  78. CallRestBizParam callRestBizParam = CallRestBizParam.builder()
  79. .orderId(String.valueOf(snowflakeIdWorker.nextId()))
  80. .bizid(restClientProperties.getBizid())
  81. .account(restClientProperties.getAccount())
  82. .contractName(generalProperties.getContractName())
  83. .methodSignature("mint(identity,uint256)")
  84. .inputParamListStr(jsonArray.toJSONString())
  85. .outTypes("[]")//合约返回值类型
  86. .mykmsKeyId(restClientProperties.getKmsId())
  87. .method(Method.CALLCONTRACTBIZASYNC)
  88. .tenantid(restClientProperties.getTenantid())
  89. .gas(500000L)
  90. .build();
  91. BaseResp resp = restClient.bizChainCallWithReceipt(callRestBizParam);
  92. if (!resp.isSuccess()) {
  93. log.info("EVM合约执行失败: code: {}, data: {}, toAccount: {}, tokenId: {}", resp.getCode(), resp.getData(), toAccount, tokenId);
  94. }
  95. if ("200".equals(resp.getCode())) {
  96. log.info("EVM合约执行成功");
  97. // 合约调用交易回执内容
  98. ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
  99. BigInteger gasUsed = txReceipt.getGasUsed();
  100. long result = txReceipt.getResult();
  101. log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
  102. NFT nft = new NFT(txReceipt.getHash(), tokenId, txReceipt.getBlockNumber(), txReceipt.getGasUsed());
  103. log.info("NFT生成成功 {}", nft);
  104. return nft;
  105. } else if ("211".equals(resp.getCode())) {
  106. log.error("EVM合约执行限流");
  107. } else if ("10201".equals(resp.getCode())) {
  108. // 异步交易未成功需要根据状态码判断交易状态
  109. ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
  110. String out = new String(matchAndReplaceNonEnglishChar(txReceipt.getOutput()));
  111. // 异步交易未成功需要根据状态码判断交易状态
  112. log.error("EVM合约执行未成功: code: {}, out: {}, tokenId: {}", resp.getCode(), out, tokenId);
  113. if (out.endsWith("ERC721: token already minted")) {
  114. return new NFT(txReceipt.getHash(), tokenId, txReceipt.getBlockNumber(), txReceipt.getGasUsed());
  115. }
  116. }
  117. throw new BusinessException("执行EVM合约mint失败");
  118. }
  119. public static byte[] matchAndReplaceNonEnglishChar(byte[] data) {
  120. List<Byte> list = new ArrayList<>();
  121. for (byte c : data) {
  122. if (c >= 32 && c <= 127) {
  123. list.add(c);
  124. }
  125. }
  126. byte[] bytes = new byte[list.size()];
  127. for (int i = 0; i < list.size(); i++) {
  128. bytes[i] = list.get(i);
  129. }
  130. return bytes;
  131. }
  132. public String ownerOf(String tokenId) throws Exception {
  133. CallRestBizParam callRestBizParam = CallRestBizParam.builder()
  134. .orderId(String.valueOf(snowflakeIdWorker.nextId()))
  135. .bizid(restClientProperties.getBizid())
  136. .account(restClientProperties.getAccount())
  137. .contractName(generalProperties.getContractName())
  138. .methodSignature("ownerOf(uint256)")
  139. .inputParamListStr("[" + new BigInteger(tokenId, 16) + "]")
  140. .outTypes("[identity]")//合约返回值类型
  141. .mykmsKeyId(restClientProperties.getKmsId())
  142. .method(Method.CALLCONTRACTBIZASYNC)
  143. .tenantid(restClientProperties.getTenantid())
  144. .gas(500000L)
  145. .build();
  146. BaseResp resp = restClient.bizChainCallWithReceipt(callRestBizParam);
  147. if (!resp.isSuccess()) {
  148. log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
  149. }
  150. if ("200".equals(resp.getCode())) {
  151. log.info("EVM合约执行成功");
  152. // 合约调用交易回执内容
  153. ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
  154. BigInteger gasUsed = txReceipt.getGasUsed();
  155. long result = txReceipt.getResult();
  156. log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
  157. String identity = Hex.encodeHexString(txReceipt.getOutput());
  158. log.info("owner is: {}", identity);
  159. return identity;
  160. } else {
  161. // 异步交易未成功需要根据状态码判断交易状态
  162. log.error("EVM合约执行未成功: " + resp.getCode());
  163. throw new BusinessException("EVM合约执行未成功: " + resp.getCode());
  164. }
  165. }
  166. public NFT transferToken(String tokenId, String fromAccount, String toAccount) throws Exception {
  167. JSONArray jsonArray = new JSONArray();
  168. jsonArray.add(Utils.getIdentityByName("raex_official"));
  169. jsonArray.add(Utils.getIdentityByName("9th_HCWWflAZ_"));
  170. jsonArray.add("95057808871064671354760012409081314299");
  171. CallRestBizParam callRestBizParam = CallRestBizParam.builder()
  172. .orderId(String.valueOf(snowflakeIdWorker.nextId()))
  173. .bizid(restClientProperties.getBizid())
  174. .account(restClientProperties.getAccount())
  175. .contractName("raex12")
  176. .methodSignature("ownerTransfer(identity,identity,uint256)")
  177. .inputParamListStr(jsonArray.toJSONString())
  178. .outTypes("[]")//合约返回值类型
  179. .mykmsKeyId(restClientProperties.getKmsId())
  180. .method(Method.CALLCONTRACTBIZASYNC)
  181. .tenantid(restClientProperties.getTenantid())
  182. .gas(500000L)
  183. .build();
  184. BaseResp resp = restClient.bizChainCallWithReceipt(callRestBizParam);
  185. if (!resp.isSuccess()) {
  186. log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
  187. }
  188. if ("200".equals(resp.getCode())) {
  189. log.info("EVM合约执行成功");
  190. // 合约调用交易回执内容
  191. ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
  192. BigInteger gasUsed = txReceipt.getGasUsed();
  193. long result = txReceipt.getResult();
  194. log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
  195. return new NFT(txReceipt.getHash(), tokenId, txReceipt.getBlockNumber(), txReceipt.getGasUsed());
  196. } else {
  197. // 异步交易未成功需要根据状态码判断交易状态
  198. log.error("EVM合约执行未成功: " + resp.getCode());
  199. }
  200. throw new BusinessException("转移token失败");
  201. }
  202. public NFT setApprovalForAll(String account) throws Exception {
  203. JSONArray jsonArray = new JSONArray();
  204. jsonArray.add(Utils.getIdentityByName(account));
  205. jsonArray.add(true);
  206. CallRestBizParam callRestBizParam = CallRestBizParam.builder()
  207. .orderId(String.valueOf(snowflakeIdWorker.nextId()))
  208. .bizid(restClientProperties.getBizid())
  209. .account(restClientProperties.getAccount())
  210. .contractName(generalProperties.getContractName())
  211. .methodSignature("setApprovalForAll(identity,bool)")
  212. .inputParamListStr(jsonArray.toJSONString())
  213. .outTypes("[]")//合约返回值类型
  214. .mykmsKeyId(restClientProperties.getKmsId())
  215. .method(Method.CALLCONTRACTBIZASYNC)
  216. .tenantid(restClientProperties.getTenantid())
  217. .gas(500000L)
  218. .build();
  219. BaseResp resp = restClient.bizChainCallWithReceipt(callRestBizParam);
  220. if (!resp.isSuccess()) {
  221. log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
  222. }
  223. if ("200".equals(resp.getCode())) {
  224. log.info("EVM合约执行成功");
  225. // 合约调用交易回执内容
  226. ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
  227. BigInteger gasUsed = txReceipt.getGasUsed();
  228. long result = txReceipt.getResult();
  229. log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
  230. } else {
  231. // 异步交易未成功需要根据状态码判断交易状态
  232. log.error("EVM合约执行未成功: " + resp.getCode());
  233. }
  234. throw new BusinessException("创建nft失败");
  235. }
  236. public void deployContract(String name, String code) throws Exception {
  237. String orderId = "order_" + System.currentTimeMillis();
  238. CallRestBizParam callRestBizParam = CallRestBizParam.builder()
  239. .orderId(orderId)
  240. .bizid(restClientProperties.getBizid())
  241. .account(restClientProperties.getAccount())
  242. .contractName(name)
  243. .contractCode(code)
  244. .mykmsKeyId(restClientProperties.getKmsId())
  245. .method(Method.DEPLOYCONTRACTFORBIZ)
  246. .tenantid(restClientProperties.getTenantid())
  247. .gas(1000000L).build();
  248. BaseResp resp = restClient.chainCallForBiz(callRestBizParam);
  249. if (!resp.isSuccess()) {
  250. log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
  251. }
  252. log.info("EVM合约执行成功");
  253. // 合约调用交易回执内容
  254. ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
  255. BigInteger gasUsed = txReceipt.getGasUsed();
  256. long result = txReceipt.getResult();
  257. log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
  258. }
  259. }