NFTService.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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. @Async
  42. public void createAccount(Long userId) {
  43. NFTAccount account = null;
  44. for (int i = 0; i < 10; i++) {
  45. try {
  46. Thread.sleep(5000);
  47. account = createAccount("account_" + userId);
  48. break;
  49. } catch (Exception e) {
  50. }
  51. }
  52. if (account != null) {
  53. context.publishEvent(new AccountCreatedEvent(this, userId, account));
  54. }
  55. }
  56. @Retryable(maxAttempts = 10, backoff = @Backoff(delay = 5000), value = BusinessException.class)
  57. public NFTAccount createAccount(String username) {
  58. CallRestBizParam callRestBizParam = CallRestBizParam.builder()
  59. .orderId(String.valueOf(snowflakeIdWorker.nextId()))
  60. .bizid(Constants.bizId)
  61. .account(restClientProperties.getAccount())
  62. .mykmsKeyId(restClientProperties.getKmsId())
  63. .newAccountId(username)
  64. .newAccountKmsId(Constants.kmsKey)
  65. .method(Method.TENANTCREATEACCUNT)
  66. .gas(100000L).build();
  67. try {
  68. BaseResp baseResp = restClient.chainCallForBiz(callRestBizParam);
  69. NFTAccount account = new NFTAccount(username, Constants.kmsKey, baseResp.getData());
  70. if (baseResp.isSuccess() || "400123".equals(baseResp.getCode())) {
  71. log.info("创建账户成功 {}", account);
  72. return account;
  73. } else {
  74. throw new RuntimeException(baseResp.getCode());
  75. }
  76. } catch (Exception e) {
  77. e.printStackTrace();
  78. log.error("创建账户失败", e);
  79. throw new BusinessException("创建账户失败");
  80. }
  81. }
  82. @Retryable(maxAttempts = 10, backoff = @Backoff(delay = 5000), value = BusinessException.class)
  83. public synchronized NFT createToken(String toAccount, String tokenId) throws Exception {
  84. log.info("开始执行EVM合约mint, toAccount: {}, tokenId: {}", toAccount, tokenId);
  85. if (Arrays.asList(env.getActiveProfiles()).contains("notifytest")) {
  86. NFT nft = new NFT("1", tokenId, new BigInteger("1"), new BigInteger("1"));
  87. log.info("测试服生成假token {}", tokenId);
  88. return nft;
  89. }
  90. JSONArray jsonArray = new JSONArray();
  91. jsonArray.add(Utils.getIdentityByName(toAccount));
  92. jsonArray.add(new BigInteger(tokenId, 16));
  93. CallRestBizParam callRestBizParam = CallRestBizParam.builder()
  94. .orderId(String.valueOf(snowflakeIdWorker.nextId()))
  95. .bizid(restClientProperties.getBizid())
  96. .account(restClientProperties.getAccount())
  97. .contractName(generalProperties.getContractName())
  98. .methodSignature("mint(identity,uint256)")
  99. .inputParamListStr(jsonArray.toJSONString())
  100. .outTypes("[]")//合约返回值类型
  101. .mykmsKeyId(restClientProperties.getKmsId())
  102. .method(Method.CALLCONTRACTBIZASYNC)
  103. .tenantid(restClientProperties.getTenantid())
  104. .gas(500000L)
  105. .build();
  106. BaseResp resp = restClient.bizChainCallWithReceipt(callRestBizParam);
  107. if (!resp.isSuccess()) {
  108. log.info("EVM合约执行失败: code: {}, data: {}, toAccount: {}, tokenId: {}", resp.getCode(), resp.getData(), toAccount, tokenId);
  109. }
  110. if ("200".equals(resp.getCode())) {
  111. log.info("EVM合约执行成功");
  112. // 合约调用交易回执内容
  113. ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
  114. BigInteger gasUsed = txReceipt.getGasUsed();
  115. long result = txReceipt.getResult();
  116. log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
  117. NFT nft = new NFT(txReceipt.getHash(), tokenId, txReceipt.getBlockNumber(), txReceipt.getGasUsed());
  118. log.info("NFT生成成功 {}", nft);
  119. return nft;
  120. } else if ("211".equals(resp.getCode())) {
  121. log.error("EVM合约执行限流");
  122. } else if ("10201".equals(resp.getCode())) {
  123. // 异步交易未成功需要根据状态码判断交易状态
  124. ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
  125. String out = new String(matchAndReplaceNonEnglishChar(txReceipt.getOutput()));
  126. // 异步交易未成功需要根据状态码判断交易状态
  127. log.error("EVM合约执行未成功: code: {}, out: {}, tokenId: {}", resp.getCode(), out, tokenId);
  128. if (out.endsWith("ERC721: token already minted")) {
  129. return new NFT(txReceipt.getHash(), tokenId, txReceipt.getBlockNumber(), txReceipt.getGasUsed());
  130. }
  131. }
  132. throw new BusinessException("执行EVM合约mint失败");
  133. }
  134. public static byte[] matchAndReplaceNonEnglishChar(byte[] data) {
  135. List<Byte> list = new ArrayList<>();
  136. for (byte c : data) {
  137. if (c >= 32 && c <= 127) {
  138. list.add(c);
  139. }
  140. }
  141. byte[] bytes = new byte[list.size()];
  142. for (int i = 0; i < list.size(); i++) {
  143. bytes[i] = list.get(i);
  144. }
  145. return bytes;
  146. }
  147. public String ownerOf(String tokenId) throws Exception {
  148. CallRestBizParam callRestBizParam = CallRestBizParam.builder()
  149. .orderId(String.valueOf(snowflakeIdWorker.nextId()))
  150. .bizid(restClientProperties.getBizid())
  151. .account(restClientProperties.getAccount())
  152. .contractName(generalProperties.getContractName())
  153. .methodSignature("ownerOf(uint256)")
  154. .inputParamListStr("[" + new BigInteger(tokenId, 16) + "]")
  155. .outTypes("[identity]")//合约返回值类型
  156. .mykmsKeyId(restClientProperties.getKmsId())
  157. .method(Method.CALLCONTRACTBIZASYNC)
  158. .tenantid(restClientProperties.getTenantid())
  159. .gas(500000L)
  160. .build();
  161. BaseResp resp = restClient.bizChainCallWithReceipt(callRestBizParam);
  162. if (!resp.isSuccess()) {
  163. log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
  164. }
  165. if ("200".equals(resp.getCode())) {
  166. log.info("EVM合约执行成功");
  167. // 合约调用交易回执内容
  168. ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
  169. BigInteger gasUsed = txReceipt.getGasUsed();
  170. long result = txReceipt.getResult();
  171. log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
  172. String identity = Hex.encodeHexString(txReceipt.getOutput());
  173. log.info("owner is: {}", identity);
  174. return identity;
  175. } else {
  176. // 异步交易未成功需要根据状态码判断交易状态
  177. log.error("EVM合约执行未成功: " + resp.getCode());
  178. throw new BusinessException("EVM合约执行未成功: " + resp.getCode());
  179. }
  180. }
  181. public NFT transferToken(String tokenId, String fromAccount, String toAccount) throws Exception {
  182. JSONArray jsonArray = new JSONArray();
  183. jsonArray.add(Utils.getIdentityByName("raex_official"));
  184. jsonArray.add(Utils.getIdentityByName("9th_HCWWflAZ_"));
  185. jsonArray.add("95057808871064671354760012409081314299");
  186. CallRestBizParam callRestBizParam = CallRestBizParam.builder()
  187. .orderId(String.valueOf(snowflakeIdWorker.nextId()))
  188. .bizid(restClientProperties.getBizid())
  189. .account(restClientProperties.getAccount())
  190. .contractName("raex12")
  191. .methodSignature("ownerTransfer(identity,identity,uint256)")
  192. .inputParamListStr(jsonArray.toJSONString())
  193. .outTypes("[]")//合约返回值类型
  194. .mykmsKeyId(restClientProperties.getKmsId())
  195. .method(Method.CALLCONTRACTBIZASYNC)
  196. .tenantid(restClientProperties.getTenantid())
  197. .gas(500000L)
  198. .build();
  199. BaseResp resp = restClient.bizChainCallWithReceipt(callRestBizParam);
  200. if (!resp.isSuccess()) {
  201. log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
  202. }
  203. if ("200".equals(resp.getCode())) {
  204. log.info("EVM合约执行成功");
  205. // 合约调用交易回执内容
  206. ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
  207. BigInteger gasUsed = txReceipt.getGasUsed();
  208. long result = txReceipt.getResult();
  209. log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
  210. return new NFT(txReceipt.getHash(), tokenId, txReceipt.getBlockNumber(), txReceipt.getGasUsed());
  211. } else {
  212. // 异步交易未成功需要根据状态码判断交易状态
  213. log.error("EVM合约执行未成功: " + resp.getCode());
  214. }
  215. throw new BusinessException("转移token失败");
  216. }
  217. public NFT setApprovalForAll(String account) throws Exception {
  218. JSONArray jsonArray = new JSONArray();
  219. jsonArray.add(Utils.getIdentityByName(account));
  220. jsonArray.add(true);
  221. CallRestBizParam callRestBizParam = CallRestBizParam.builder()
  222. .orderId(String.valueOf(snowflakeIdWorker.nextId()))
  223. .bizid(restClientProperties.getBizid())
  224. .account(restClientProperties.getAccount())
  225. .contractName(generalProperties.getContractName())
  226. .methodSignature("setApprovalForAll(identity,bool)")
  227. .inputParamListStr(jsonArray.toJSONString())
  228. .outTypes("[]")//合约返回值类型
  229. .mykmsKeyId(restClientProperties.getKmsId())
  230. .method(Method.CALLCONTRACTBIZASYNC)
  231. .tenantid(restClientProperties.getTenantid())
  232. .gas(500000L)
  233. .build();
  234. BaseResp resp = restClient.bizChainCallWithReceipt(callRestBizParam);
  235. if (!resp.isSuccess()) {
  236. log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
  237. }
  238. if ("200".equals(resp.getCode())) {
  239. log.info("EVM合约执行成功");
  240. // 合约调用交易回执内容
  241. ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
  242. BigInteger gasUsed = txReceipt.getGasUsed();
  243. long result = txReceipt.getResult();
  244. log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
  245. } else {
  246. // 异步交易未成功需要根据状态码判断交易状态
  247. log.error("EVM合约执行未成功: " + resp.getCode());
  248. }
  249. throw new BusinessException("创建nft失败");
  250. }
  251. public void deployContract(String name, String code) throws Exception {
  252. String orderId = "order_" + System.currentTimeMillis();
  253. CallRestBizParam callRestBizParam = CallRestBizParam.builder()
  254. .orderId(orderId)
  255. .bizid(restClientProperties.getBizid())
  256. .account(restClientProperties.getAccount())
  257. .contractName(name)
  258. .contractCode(code)
  259. .mykmsKeyId(restClientProperties.getKmsId())
  260. .method(Method.DEPLOYCONTRACTFORBIZ)
  261. .tenantid(restClientProperties.getTenantid())
  262. .gas(1000000L).build();
  263. BaseResp resp = restClient.chainCallForBiz(callRestBizParam);
  264. if (!resp.isSuccess()) {
  265. log.info("EVM合约执行失败: " + resp.getCode() + ", " + resp.getData());
  266. }
  267. log.info("EVM合约执行成功");
  268. // 合约调用交易回执内容
  269. ReceiptDecoration txReceipt = JSON.parseObject(resp.getData(), ReceiptDecoration.class);
  270. BigInteger gasUsed = txReceipt.getGasUsed();
  271. long result = txReceipt.getResult();
  272. log.info("EVM合约交易内容: 哈希 " + txReceipt.getHash() + ", 消耗燃料 " + gasUsed + ", 结果 " + result);
  273. }
  274. }