use of io.nuls.contract.entity.txdata.CreateContractData in project nuls by nuls-io.
the class ContractTxServiceImpl method contractPreCreateTx.
/**
* 预创建生成智能合约的交易
* 用于测试合约是否能正确创建
*
* @param sender 交易创建者
* @param gasLimit 最大gas消耗
* @param price 执行合约单价
* @param contractCode 合约代码
* @param args 参数列表
* @param password 账户密码
* @param remark 备注
* @return
*/
@Override
public Result contractPreCreateTx(String sender, Long gasLimit, Long price, byte[] contractCode, String[][] args, String password, String remark) {
try {
AssertUtil.canNotEmpty(sender, "the sender address can not be empty");
AssertUtil.canNotEmpty(contractCode, "the contractCode can not be empty");
Na value = Na.ZERO;
Result<Account> accountResult = accountService.getAccount(sender);
if (accountResult.isFailed()) {
return accountResult;
}
// 生成一个地址作为智能合约地址
Address contractAddress = AccountTool.createContractAddress();
byte[] contractAddressBytes = contractAddress.getAddressBytes();
byte[] senderBytes = AddressTool.getAddress(sender);
CreateContractTransaction tx = new CreateContractTransaction();
if (StringUtils.isNotBlank(remark)) {
try {
tx.setRemark(remark.getBytes(NulsConfig.DEFAULT_ENCODING));
} catch (UnsupportedEncodingException e) {
Log.error(e);
throw new RuntimeException(e);
}
}
tx.setTime(TimeService.currentTimeMillis());
// 计算CoinData
/*
* 智能合约计算手续费以消耗的Gas*Price为根据,然而创建交易时并不执行智能合约,
* 所以此时交易的CoinData是不固定的,比实际要多,
* 打包时执行智能合约,真实的手续费已算出,然而tx的手续费已扣除,
* 多扣除的费用会以CoinBase交易还给Sender
*/
CoinData coinData = new CoinData();
// 向智能合约账户转账
if (!Na.ZERO.equals(value)) {
Coin toCoin = new Coin(contractAddressBytes, value);
coinData.getTo().add(toCoin);
}
BlockHeader blockHeader = NulsContext.getInstance().getBestBlock().getHeader();
// 当前区块高度
long blockHeight = blockHeader.getHeight();
// 当前区块状态根
byte[] prevStateRoot = ContractUtil.getStateRoot(blockHeader);
AssertUtil.canNotEmpty(prevStateRoot, "All features of the smart contract are locked.");
// 执行VM验证合法性
ProgramCreate programCreate = new ProgramCreate();
programCreate.setContractAddress(contractAddressBytes);
programCreate.setSender(senderBytes);
programCreate.setValue(BigInteger.valueOf(value.getValue()));
programCreate.setPrice(price.longValue());
programCreate.setGasLimit(gasLimit.longValue());
programCreate.setNumber(blockHeight);
programCreate.setContractCode(contractCode);
if (args != null) {
programCreate.setArgs(args);
}
ProgramExecutor track = programExecutor.begin(prevStateRoot);
// 验证合约时跳过Gas验证
long realGasLimit = programCreate.getGasLimit();
programCreate.setGasLimit(MAX_GASLIMIT);
ProgramResult programResult = track.create(programCreate);
// 执行结果失败时,交易直接返回错误,不上链,不消耗Gas,
if (!programResult.isSuccess()) {
Log.error(programResult.getStackTrace());
Result result = Result.getFailed(ContractErrorCode.DATA_ERROR);
result.setMsg(ContractUtil.simplifyErrorMsg(programResult.getErrorMessage()));
result = checkVmResultAndReturn(programResult.getErrorMessage(), result);
return result;
} else {
// 其他合法性都通过后,再验证Gas
track = programExecutor.begin(prevStateRoot);
programCreate.setGasLimit(realGasLimit);
programResult = track.create(programCreate);
if (!programResult.isSuccess()) {
Log.error(programResult.getStackTrace());
Result result = Result.getFailed(ContractErrorCode.DATA_ERROR);
result.setMsg(ContractUtil.simplifyErrorMsg(programResult.getErrorMessage()));
return result;
}
}
long gasUsed = gasLimit.longValue();
Na imputedNa = Na.valueOf(LongUtils.mul(gasUsed, price));
// 总花费
Na totalNa = imputedNa.add(value);
// 组装txData
CreateContractData createContractData = new CreateContractData();
createContractData.setSender(senderBytes);
createContractData.setContractAddress(contractAddressBytes);
createContractData.setValue(value.getValue());
createContractData.setGasLimit(gasLimit);
createContractData.setPrice(price);
createContractData.setCodeLen(contractCode.length);
createContractData.setCode(contractCode);
if (args != null) {
createContractData.setArgsCount((byte) args.length);
if (args.length > 0) {
createContractData.setArgs(args);
}
}
tx.setTxData(createContractData);
CoinDataResult coinDataResult = accountLedgerService.getCoinData(senderBytes, totalNa, tx.size(), TransactionFeeCalculator.MIN_PRICE_PRE_1024_BYTES);
if (!coinDataResult.isEnough()) {
return Result.getFailed(TransactionErrorCode.INSUFFICIENT_BALANCE);
}
return Result.getSuccess();
} catch (NulsException e) {
Log.error(e);
return Result.getFailed(e.getErrorCode());
} catch (Exception e) {
Log.error(e);
Result result = Result.getFailed(ContractErrorCode.CONTRACT_TX_CREATE_ERROR);
result.setMsg(e.getMessage());
return result;
}
}
use of io.nuls.contract.entity.txdata.CreateContractData in project nuls by nuls-io.
the class CreateContractTxProcessor method onRollback.
@Override
public Result onRollback(CreateContractTransaction tx, Object secondaryData) {
CreateContractData txData = tx.getTxData();
byte[] contractAddress = txData.getContractAddress();
contractCollectionStorageService.deleteContractAddress(contractAddress);
contractAddressStorageService.deleteContractAddress(contractAddress);
contractService.deleteContractExecuteResult(tx.getHash());
return Result.getSuccess();
}
use of io.nuls.contract.entity.txdata.CreateContractData in project nuls by nuls-io.
the class CreateContractTxProcessor method onCommit.
@Override
public Result onCommit(CreateContractTransaction tx, Object secondaryData) {
ContractResult contractResult = tx.getContractResult();
contractService.saveContractExecuteResult(tx.getHash(), contractResult);
CreateContractData txData = tx.getTxData();
byte[] contractAddress = txData.getContractAddress();
byte[] sender = txData.getSender();
String senderStr = AddressTool.getStringAddressByBytes(sender);
String contractAddressStr = AddressTool.getStringAddressByBytes(contractAddress);
// 移除未确认的创建合约交易
contractTxService.removeLocalUnconfirmedCreateContractTransaction(senderStr, contractAddressStr, contractResult);
// 执行失败的合约直接返回
if (!contractResult.isSuccess()) {
return Result.getSuccess();
}
NulsDigestData hash = tx.getHash();
long blockHeight = tx.getBlockHeight();
long bestBlockHeight = NulsContext.getInstance().getBestHeight();
ContractAddressInfoPo info = new ContractAddressInfoPo();
info.setContractAddress(contractAddress);
info.setSender(sender);
try {
info.setCreateTxHash(hash.serialize());
} catch (IOException e) {
throw new NulsRuntimeException(e);
}
info.setCreateTime(tx.getTime());
info.setBlockHeight(blockHeight);
// byte[] stateRoot = contractResult.getStateRoot();
boolean isNrc20Contract = contractResult.isNrc20();
boolean acceptDirectTransfer = contractResult.isAcceptDirectTransfer();
info.setAcceptDirectTransfer(acceptDirectTransfer);
info.setNrc20(isNrc20Contract);
// 获取 token tracker
if (isNrc20Contract) {
BlockHeader blockHeader = tx.getBlockHeader();
byte[] newestStateRoot = blockHeader.getStateRoot();
// NRC20 token 标准方法获取名称数据
ProgramResult programResult = vmHelper.invokeViewMethod(newestStateRoot, bestBlockHeight, contractAddress, NRC20_METHOD_NAME, null, null);
if (programResult.isSuccess()) {
String tokenName = programResult.getResult();
info.setNrc20TokenName(tokenName);
}
programResult = vmHelper.invokeViewMethod(newestStateRoot, bestBlockHeight, contractAddress, NRC20_METHOD_SYMBOL, null, null);
if (programResult.isSuccess()) {
String symbol = programResult.getResult();
info.setNrc20TokenSymbol(symbol);
}
programResult = vmHelper.invokeViewMethod(newestStateRoot, bestBlockHeight, contractAddress, NRC20_METHOD_DECIMALS, null, null);
if (programResult.isSuccess()) {
String decimals = programResult.getResult();
if (StringUtils.isNotBlank(decimals)) {
try {
info.setDecimals(new BigInteger(decimals).longValue());
} catch (Exception e) {
Log.error("Get nrc20 decimals error.", e);
// skip it
}
}
}
programResult = vmHelper.invokeViewMethod(newestStateRoot, bestBlockHeight, contractAddress, NRC20_METHOD_TOTAL_SUPPLY, null, null);
if (programResult.isSuccess()) {
String totalSupply = programResult.getResult();
if (StringUtils.isNotBlank(totalSupply)) {
try {
info.setTotalSupply(new BigInteger(totalSupply));
} catch (Exception e) {
Log.error("Get nrc20 totalSupply error.", e);
// skip it
}
}
}
// 刷新创建者的token余额
vmHelper.refreshTokenBalance(newestStateRoot, info, senderStr, contractAddressStr);
// 处理合约事件
vmHelper.dealEvents(newestStateRoot, tx, contractResult, info);
}
Result result = contractAddressStorageService.saveContractAddress(contractAddress, info);
return result;
}
use of io.nuls.contract.entity.txdata.CreateContractData in project nuls by nuls-io.
the class VMHelper method validateNrc20Contract.
public Result validateNrc20Contract(ProgramExecutor track, CreateContractTransaction tx, ContractResult contractResult) {
if (contractResult == null) {
return Result.getFailed(ContractErrorCode.NULL_PARAMETER);
}
CreateContractData createContractData = tx.getTxData();
byte[] stateRoot = contractResult.getStateRoot();
byte[] contractAddress = contractResult.getContractAddress();
long bestBlockHeight = NulsContext.getInstance().getBestHeight();
List<ProgramMethod> methods = this.getAllMethods(createContractData.getCode());
boolean isNrc20 = this.checkNrc20Contract(methods);
boolean isAcceptDirectTransfer = this.checkAcceptDirectTransfer(methods);
contractResult.setNrc20(isNrc20);
contractResult.setAcceptDirectTransfer(isAcceptDirectTransfer);
if (isNrc20) {
// NRC20 tokenName 验证代币名称格式
ProgramResult programResult = this.invokeViewMethod(track, stateRoot, bestBlockHeight, contractAddress, NRC20_METHOD_NAME, null, null);
if (programResult.isSuccess()) {
String tokenName = programResult.getResult();
if (StringUtils.isNotBlank(tokenName)) {
if (!StringUtils.validTokenNameOrSymbol(tokenName)) {
return Result.getFailed(ContractErrorCode.CONTRACT_NAME_FORMAT_INCORRECT);
}
}
}
// NRC20 tokenSymbol 验证代币符号的格式
programResult = this.invokeViewMethod(track, stateRoot, bestBlockHeight, contractAddress, NRC20_METHOD_SYMBOL, null, null);
if (programResult.isSuccess()) {
String symbol = programResult.getResult();
if (StringUtils.isNotBlank(symbol)) {
if (!StringUtils.validTokenNameOrSymbol(symbol)) {
return Result.getFailed(ContractErrorCode.CONTRACT_NRC20_SYMBOL_FORMAT_INCORRECT);
}
}
}
programResult = this.invokeViewMethod(track, stateRoot, bestBlockHeight, contractAddress, NRC20_METHOD_DECIMALS, null, null);
BigInteger decimalsBig = BigInteger.ZERO;
if (programResult.isSuccess()) {
String decimals = programResult.getResult();
if (StringUtils.isNotBlank(decimals)) {
try {
decimalsBig = new BigInteger(decimals);
if (decimalsBig.compareTo(BigInteger.ZERO) < 0 || decimalsBig.compareTo(MAXIMUM_DECIMALS) > 0) {
return Result.getFailed(ContractErrorCode.CONTRACT_NRC20_MAXIMUM_DECIMALS);
}
} catch (Exception e) {
Log.error("Get nrc20 decimals error.", e);
// skip it
}
}
}
programResult = this.invokeViewMethod(track, stateRoot, bestBlockHeight, contractAddress, NRC20_METHOD_TOTAL_SUPPLY, null, null);
if (programResult.isSuccess()) {
String totalSupply = programResult.getResult();
if (StringUtils.isNotBlank(totalSupply)) {
try {
BigInteger totalSupplyBig = new BigInteger(totalSupply);
if (totalSupplyBig.compareTo(BigInteger.ZERO) <= 0 || totalSupplyBig.compareTo(MAXIMUM_TOTAL_SUPPLY.multiply(BigInteger.TEN.pow(decimalsBig.intValue()))) > 0) {
return Result.getFailed(ContractErrorCode.CONTRACT_NRC20_MAXIMUM_TOTAL_SUPPLY);
}
} catch (Exception e) {
Log.error("Get nrc20 totalSupply error.", e);
// skip it
}
}
}
}
return Result.getSuccess();
}
Aggregations