All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.nuls.v2.util.ContractUtil Maven / Gradle / Ivy

/**
 * MIT License
 * 

* Copyright (c) 2017-2019 nuls.io *

* Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: *

* The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. *

* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package io.nuls.v2.util; import io.nuls.base.RPCUtil; import io.nuls.base.basic.AddressTool; import io.nuls.base.basic.NulsByteBuffer; import io.nuls.base.basic.TransactionFeeCalculator; import io.nuls.base.data.*; import io.nuls.core.basic.NulsData; import io.nuls.core.basic.Result; import io.nuls.core.basic.VarInt; import io.nuls.core.constant.CommonCodeConstanst; import io.nuls.core.exception.NulsException; import io.nuls.core.log.Log; import io.nuls.core.model.LongUtils; import io.nuls.core.model.StringUtils; import io.nuls.v2.constant.Constant; import io.nuls.v2.error.ContractErrorCode; import io.nuls.v2.model.dto.ProgramMultyAssetValue; import io.nuls.v2.tx.CallContractTransaction; import io.nuls.v2.tx.CreateContractTransaction; import io.nuls.v2.tx.DeleteContractTransaction; import io.nuls.v2.txdata.CallContractData; import io.nuls.v2.txdata.ContractData; import io.nuls.v2.txdata.CreateContractData; import io.nuls.v2.txdata.DeleteContractData; import java.io.IOException; import java.lang.reflect.Array; import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Base64; import java.util.List; import static io.nuls.core.constant.TxType.*; import static io.nuls.core.model.StringUtils.isBlank; import static io.nuls.v2.constant.Constant.*; /** * @author: PierreLuo * @date: 2018/8/25 */ public class ContractUtil { public static String[][] twoDimensionalArray(Object[] args, String[] types) { if (args == null) { return null; } else { int length = args.length; String[][] two = new String[length][]; Object arg; for (int i = 0; i < length; i++) { arg = args[i]; if (arg == null) { two[i] = new String[0]; continue; } if (arg instanceof String) { String argStr = (String) arg; // 非String类型参数,若传参是空字符串,则赋值为空一维数组,避免数字类型转化异常 -> 空字符串转化为数字 if (types != null && isBlank(argStr) && !STRING.equalsIgnoreCase(types[i])) { two[i] = new String[0]; } else { two[i] = new String[]{argStr}; } } else if (arg.getClass().isArray()) { int len = Array.getLength(arg); String[] result = new String[len]; for (int k = 0; k < len; k++) { result[k] = valueOf(Array.get(arg, k)); } two[i] = result; } else if (arg instanceof ArrayList) { ArrayList resultArg = (ArrayList) arg; int size = resultArg.size(); String[] result = new String[size]; for (int k = 0; k < size; k++) { result[k] = valueOf(resultArg.get(k)); } two[i] = result; } else { two[i] = new String[]{valueOf(arg)}; } } return two; } } public static byte[] extractContractAddressFromTxData(Transaction tx) { if (tx == null) { return null; } int txType = tx.getType(); if (txType == CREATE_CONTRACT || txType == CALL_CONTRACT || txType == DELETE_CONTRACT) { return extractContractAddressFromTxData(tx.getTxData()); } return null; } private static byte[] extractContractAddressFromTxData(byte[] txData) { if (txData == null) { return null; } int length = txData.length; if (length < Address.ADDRESS_LENGTH * 2) { return null; } byte[] contractAddress = new byte[Address.ADDRESS_LENGTH]; System.arraycopy(txData, Address.ADDRESS_LENGTH, contractAddress, 0, Address.ADDRESS_LENGTH); return contractAddress; } public static String[][] twoDimensionalArray(Object[] args) { return twoDimensionalArray(args, null); } public static String valueOf(Object obj) { return (obj == null) ? null : obj.toString(); } public static boolean isContractTransaction(Transaction tx) { if (tx == null) { return false; } int txType = tx.getType(); if (txType == CREATE_CONTRACT || txType == CALL_CONTRACT || txType == DELETE_CONTRACT || txType == CONTRACT_TRANSFER || txType == CONTRACT_RETURN_GAS) { return true; } return false; } public static boolean isGasCostContractTransaction(Transaction tx) { if (tx == null) { return false; } int txType = tx.getType(); if (txType == CREATE_CONTRACT || txType == CALL_CONTRACT) { return true; } return false; } public static boolean isLockContract(long lastestHeight, long blockHeight) throws NulsException { if (blockHeight > 0) { long confirmCount = lastestHeight - blockHeight; if (confirmCount < 7) { return true; } } return false; } public static String bigInteger2String(BigInteger bigInteger) { if (bigInteger == null) { return "0"; } return bigInteger.toString(); } public static String simplifyErrorMsg(String errorMsg) { String resultMsg = "contract error - "; if (isBlank(errorMsg)) { return resultMsg; } if (errorMsg.contains("Exception:")) { String[] msgs = errorMsg.split("Exception:", 2); return resultMsg + msgs[1].trim(); } return resultMsg + errorMsg; } public static Result checkVmResultAndReturn(String errorMessage, Result defaultResult) { if (isBlank(errorMessage)) { return defaultResult; } if (isNotEnoughGasError(errorMessage)) { return Result.getFailed(ContractErrorCode.CONTRACT_GAS_LIMIT); } return defaultResult; } private static boolean isNotEnoughGasError(String errorMessage) { if (errorMessage == null) { return false; } if (errorMessage.contains(NOT_ENOUGH_GAS)) { return true; } return false; } public static boolean isTerminatedContract(int status) { return Constant.STOP == status; } public static boolean isTransferMethod(String method) { return (NRC20_METHOD_TRANSFER.equals(method) || NRC20_METHOD_TRANSFER_FROM.equals(method)); } public static String argToString(String[][] args) { if (args == null) { return ""; } String result = ""; for (String[] a : args) { result += Arrays.toString(a) + "| "; } return result; } public static boolean isLegalContractAddress(int chainId, byte[] addressBytes) { if (addressBytes == null) { return false; } return AddressTool.validContractAddress(addressBytes, chainId); } public static Result getSuccess() { return Result.getSuccess(CommonCodeConstanst.SUCCESS); } public static Result getFailed() { return Result.getFailed(CommonCodeConstanst.FAILED); } public static String asString(byte[] bytes) { return Base64.getEncoder().encodeToString(bytes); } public static byte[] asBytes(String string) { return Base64.getDecoder().decode(string); } public static BigInteger minus(BigInteger a, BigInteger b) { BigInteger result = a.subtract(b); if (result.compareTo(BigInteger.ZERO) < 0) { throw new RuntimeException("Negative number detected."); } return result; } public static int extractTxTypeFromTx(String txString) throws NulsException { String txTypeHexString = txString.substring(0, 4); NulsByteBuffer byteBuffer = new NulsByteBuffer(RPCUtil.decode(txTypeHexString)); return byteBuffer.readUint16(); } public static String toString(String[][] a) { if (a == null) return "null"; int iMax = a.length - 1; if (iMax == -1) return "[]"; StringBuilder b = new StringBuilder(); b.append('['); for (int i = 0; ; i++) { b.append(Arrays.toString(a[i])); if (i == iMax) { b.append(']'); break; } b.append(", "); } return b.toString(); } public static CreateContractTransaction newCreateTx(int chainId, int assetsId, BigInteger senderBalance, String nonce, CreateContractData createContractData, String remark) { try { CreateContractTransaction tx = new CreateContractTransaction(); if (StringUtils.isNotBlank(remark)) { tx.setRemark(remark.getBytes(StandardCharsets.UTF_8)); } tx.setTime(System.currentTimeMillis() / 1000); // 计算CoinData CoinData coinData = makeCoinData(chainId, assetsId, senderBalance, nonce, createContractData, tx.size(), calcSize(createContractData)); tx.setTxDataObj(createContractData); tx.setCoinDataObj(coinData); tx.serializeData(); return tx; } catch (IOException e) { Log.error(e); throw new RuntimeException(e.getMessage()); } } public static CallContractTransaction newCallTx(int chainId, int assetId, BigInteger senderBalance, String nonce, CallContractData callContractData, String remark, List multyAssetValues) { return newCallTx(chainId, assetId, senderBalance, nonce, callContractData, 0, remark, multyAssetValues); } public static CallContractTransaction newCallTx(int chainId, int assetId, BigInteger senderBalance, String nonce, CallContractData callContractData, long time, String remark, List multyAssetValues) { try { CallContractTransaction tx = new CallContractTransaction(); if (StringUtils.isNotBlank(remark)) { tx.setRemark(remark.getBytes(StandardCharsets.UTF_8)); } if (time == 0) { tx.setTime(System.currentTimeMillis() / 1000); } else { tx.setTime(time); } byte[] sender = callContractData.getSender(); BigInteger value = callContractData.getValue(); byte[] contractAddress = callContractData.getContractAddress(); List froms = new ArrayList<>(); List tos = new ArrayList<>(); if (value.compareTo(BigInteger.ZERO) > 0) { CoinFrom coinFrom = new CoinFrom(sender, chainId, assetId, value, RPCUtil.decode(nonce), (byte) 0); froms.add(coinFrom); CoinTo coinTo = new CoinTo(contractAddress, chainId, assetId, value); tos.add(coinTo); } int _assetChainId, _assetId; if (multyAssetValues != null) { for (ProgramMultyAssetValue multyAssetValue : multyAssetValues) { BigInteger _value = multyAssetValue.getValue(); _assetChainId = multyAssetValue.getAssetChainId(); _assetId = multyAssetValue.getAssetId(); CoinFrom coinFrom = new CoinFrom(sender, _assetChainId, _assetId, _value, RPCUtil.decode(multyAssetValue.getNonce()), (byte) 0); froms.add(coinFrom); CoinTo coinTo = new CoinTo(contractAddress, _assetChainId, _assetId, _value); tos.add(coinTo); } } // 计算CoinData CoinData coinData = new CoinData(); coinData.setFrom(froms); coinData.setTo(tos); long gasUsed = callContractData.getGasLimit(); BigInteger imputedValue = BigInteger.valueOf(LongUtils.mul(gasUsed, callContractData.getPrice())); byte[] feeAccountBytes = sender; BigInteger feeValue = imputedValue; CoinFrom feeAccountFrom = null; for (CoinFrom from : froms) { _assetChainId = from.getAssetsChainId(); _assetId = from.getAssetsId(); if (Arrays.equals(from.getAddress(), feeAccountBytes) && _assetChainId == chainId && _assetId == assetId) { from.setAmount(from.getAmount().add(feeValue)); feeAccountFrom = from; break; } } if (feeAccountFrom == null) { feeAccountFrom = new CoinFrom(feeAccountBytes, chainId, assetId, feeValue, RPCUtil.decode(nonce), (byte) 0); coinData.addFrom(feeAccountFrom); } tx.setCoinData(coinData.serialize()); tx.setTxData(callContractData.serialize()); BigInteger txSizeFee = TransactionFeeCalculator.getNormalUnsignedTxFee(tx.getSize() + 130); feeAccountFrom.setAmount(feeAccountFrom.getAmount().add(txSizeFee)); tx.setCoinData(coinData.serialize()); return tx; } catch (IOException e) { Log.error(e); throw new RuntimeException(e.getMessage()); } } public static DeleteContractTransaction newDeleteTx(int chainId, int assetsId, BigInteger senderBalance, String nonce, DeleteContractData deleteContractData, String remark) { try { DeleteContractTransaction tx = new DeleteContractTransaction(); if (StringUtils.isNotBlank(remark)) { tx.setRemark(remark.getBytes(StandardCharsets.UTF_8)); } tx.setTime(System.currentTimeMillis() / 1000); // 计算CoinData CoinData coinData = makeCoinData(chainId, assetsId, senderBalance, nonce, deleteContractData, tx.size(), calcSize(deleteContractData)); tx.setTxDataObj(deleteContractData); tx.setCoinDataObj(coinData); tx.serializeData(); return tx; } catch (IOException e) { Log.error(e); throw new RuntimeException(e.getMessage()); } } private static CoinData makeCoinData(int chainId, int assetsId, BigInteger senderBalance, String nonce, ContractData contractData, int txSize, int txDataSize) { CoinData coinData = new CoinData(); long gasUsed = contractData.getGasLimit(); BigInteger imputedValue = BigInteger.valueOf(LongUtils.mul(gasUsed, contractData.getPrice())); // 总花费 BigInteger value = contractData.getValue(); BigInteger totalValue = imputedValue.add(value); CoinFrom coinFrom = new CoinFrom(contractData.getSender(), chainId, assetsId, totalValue, RPCUtil.decode(nonce), (byte) 0); coinData.addFrom(coinFrom); if (value.compareTo(BigInteger.ZERO) > 0) { CoinTo coinTo = new CoinTo(contractData.getContractAddress(), chainId, assetsId, value); coinData.addTo(coinTo); } BigInteger fee = TransactionFeeCalculator.getNormalUnsignedTxFee(txSize + txDataSize + calcSize(coinData)); totalValue = totalValue.add(fee); if (senderBalance.compareTo(totalValue) < 0) { // Insufficient balance throw new RuntimeException("Insufficient balance"); } coinFrom.setAmount(totalValue); return coinData; } private static int calcSize(NulsData nulsData) { if (nulsData == null) { return 0; } int size = nulsData.size(); // 计算tx.size()时,当coinData和txData为空时,计算了1个长度,若此时nulsData不为空,则要扣减这1个长度 return VarInt.sizeOf(size) + size - 1; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy