use of io.nuls.contract.entity.txdata.CallContractData in project nuls by nuls-io.
the class CallContractTxValidator method validate.
@Override
public ValidateResult validate(CallContractTransaction tx) throws NulsException {
CallContractData txData = tx.getTxData();
Na transferNa = Na.valueOf(txData.getValue());
byte[] contractAddress = txData.getContractAddress();
byte[] sender = txData.getSender();
Set<String> addressSet = SignatureUtil.getAddressFromTX(tx);
if (!ContractLedgerUtil.isExistContractAddress(contractAddress)) {
Log.error("contract call error: The contract does not exist.");
return ValidateResult.getFailedResult(this.getClass().getSimpleName(), ContractErrorCode.CONTRACT_ADDRESS_NOT_EXIST);
}
if (!addressSet.contains(AddressTool.getStringAddressByBytes(sender))) {
Log.error("contract call error: The contract caller is not the transaction creator.");
return ValidateResult.getFailedResult(this.getClass().getSimpleName(), TransactionErrorCode.TX_DATA_VALIDATION_ERROR);
}
Na contractReceivedNa = Na.ZERO;
for (Coin coin : tx.getCoinData().getTo()) {
byte[] owner = coin.getOwner();
if (owner.length > 23) {
owner = coin.getAddress();
}
// Keep the change maybe a very small coin
if (addressSet.contains(AddressTool.getStringAddressByBytes(owner))) {
// When the receiver sign this tx,Allow it transfer small coin
continue;
}
if (coin.getLockTime() != 0) {
Log.error("contract call error: The amount of the transfer cannot be locked(UTXO status error).");
return ValidateResult.getFailedResult(this.getClass().getSimpleName(), TransactionErrorCode.UTXO_STATUS_CHANGE);
}
if (!ArraysTool.arrayEquals(owner, contractAddress)) {
Log.error("contract call error: The receiver is not the contract address.");
return ValidateResult.getFailedResult(this.getClass().getSimpleName(), TransactionErrorCode.TX_DATA_VALIDATION_ERROR);
} else {
contractReceivedNa = contractReceivedNa.add(coin.getNa());
}
if (coin.getNa().isLessThan(ProtocolConstant.MININUM_TRANSFER_AMOUNT)) {
Log.error("contract call error: The amount of the transfer is too small.");
return ValidateResult.getFailedResult(this.getClass().getSimpleName(), TransactionErrorCode.TOO_SMALL_AMOUNT);
}
}
if (contractReceivedNa.isLessThan(transferNa)) {
Log.error("contract call error: Insufficient amount to transfer to the contract address.");
return ValidateResult.getFailedResult(this.getClass().getSimpleName(), TransactionErrorCode.INVALID_AMOUNT);
}
Na realFee = tx.getCoinData().getFee();
Na fee = TransactionFeeCalculator.getTransferFee(tx.size()).add(Na.valueOf(LongUtils.mul(txData.getGasLimit(), txData.getPrice())));
if (realFee.isGreaterOrEquals(fee)) {
return ValidateResult.getSuccessResult();
} else {
Log.error("contract call error: The contract transaction fee is not right.");
return ValidateResult.getFailedResult(this.getClass().getName(), TransactionErrorCode.FEE_NOT_RIGHT);
}
}
use of io.nuls.contract.entity.txdata.CallContractData in project nuls by nuls-io.
the class CallContractTxProcessor method onRollback.
@Override
public Result onRollback(CallContractTransaction tx, Object secondaryData) {
try {
// 回滚代币转账交易
byte[] txHashBytes = null;
try {
txHashBytes = tx.getHash().serialize();
} catch (IOException e) {
Log.error(e);
}
ContractResult contractResult = tx.getContractResult();
if (contractResult == null) {
contractResult = contractService.getContractExecuteResult(tx.getHash());
}
CallContractData txData = tx.getTxData();
byte[] senderContractAddressBytes = txData.getContractAddress();
Result<ContractAddressInfoPo> senderContractAddressInfoResult = contractAddressStorageService.getContractAddressInfo(senderContractAddressBytes);
ContractAddressInfoPo po = senderContractAddressInfoResult.getData();
if (po != null) {
if (contractResult != null) {
// 处理合约执行失败 - 没有transferEvent的情况, 直接从数据库中删除
if (!contractResult.isSuccess()) {
if (ContractUtil.isTransferMethod(txData.getMethodName())) {
contractTokenTransferStorageService.deleteTokenTransferInfo(ArraysTool.concatenate(txData.getSender(), txHashBytes, new VarInt(0).encode()));
}
}
List<String> events = contractResult.getEvents();
int size = events.size();
// 目前只处理Transfer事件
String event;
ContractAddressInfoPo contractAddressInfo;
if (events != null && size > 0) {
for (int i = 0; i < size; i++) {
event = events.get(i);
// 按照NRC20标准,TransferEvent事件中第一个参数是转出地址-from,第二个参数是转入地址-to, 第三个参数是金额
ContractTokenTransferInfoPo tokenTransferInfoPo = ContractUtil.convertJsonToTokenTransferInfoPo(event);
if (tokenTransferInfoPo == null) {
continue;
}
String contractAddress = tokenTransferInfoPo.getContractAddress();
if (StringUtils.isBlank(contractAddress)) {
continue;
}
if (!AddressTool.validAddress(contractAddress)) {
continue;
}
byte[] contractAddressBytes = AddressTool.getAddress(contractAddress);
if (ArraysTool.arrayEquals(senderContractAddressBytes, contractAddressBytes)) {
contractAddressInfo = po;
} else {
Result<ContractAddressInfoPo> contractAddressInfoResult = contractAddressStorageService.getContractAddressInfo(contractAddressBytes);
contractAddressInfo = contractAddressInfoResult.getData();
}
if (contractAddressInfo == null) {
continue;
}
// 事件不是NRC20合约的事件
if (!contractAddressInfo.isNrc20()) {
continue;
}
// 回滚token余额
this.rollbackContractToken(tokenTransferInfoPo);
contractTokenTransferStorageService.deleteTokenTransferInfo(ArraysTool.concatenate(tokenTransferInfoPo.getFrom(), txHashBytes, new VarInt(i).encode()));
contractTokenTransferStorageService.deleteTokenTransferInfo(ArraysTool.concatenate(tokenTransferInfoPo.getTo(), txHashBytes, new VarInt(i).encode()));
}
}
}
}
// 删除子交易从全网账本
// 删除子交易从合约账本
// 删除子交易从本地账本
List<ContractTransferTransaction> contractTransferTxList = this.extractContractTransferTxs(contractResult);
if (contractTransferTxList != null && !contractTransferTxList.isEmpty()) {
// 用于回滚本地账本
List<Transaction> txList = new ArrayList<>();
for (ContractTransferTransaction transferTx : contractTransferTxList) {
try {
txList.add(transferTx);
Result result = ledgerService.rollbackTx(transferTx);
if (result.isFailed()) {
Log.error("rollback contract transfer tx from ledger error. msg: {}", result.getMsg());
return result;
}
result = contractService.rollbackContractTransferTx(transferTx);
if (result.isFailed()) {
Log.error("rollback contract transfer tx from contract ledger error. msg: {}", result.getMsg());
return Result.getFailed();
}
} catch (Exception e) {
Log.error("rollback contract transfer tx error. msg: {}", e.getMessage());
return Result.getFailed();
}
}
Result result = accountLedgerService.rollbackTransactions(txList);
if (result.isFailed()) {
Log.error("rollback contract transfer tx from account ledger error. msg: {}", result.getMsg());
return Result.getFailed();
}
}
// 删除合约调用交易的UTXO
contractUtxoService.deleteUtxoOfTransaction(tx);
// 删除合约执行结果
contractService.deleteContractExecuteResult(tx.getHash());
} catch (Exception e) {
Log.error("rollback call contract tx error.", e);
return Result.getFailed();
}
return Result.getSuccess();
}
use of io.nuls.contract.entity.txdata.CallContractData in project nuls by nuls-io.
the class ContractTxServiceImpl method contractCallTx.
/**
* 创建调用智能合约的交易
*
* @param sender 交易创建者
* @param value 交易附带的货币量
* @param gasLimit 最大gas消耗
* @param price 执行合约单价
* @param contractAddress 合约地址
* @param methodName 方法名
* @param methodDesc 方法签名,如果方法名不重复,可以不传
* @param args 参数列表
* @param password 账户密码
* @param remark 备注
* @return
*/
@Override
public Result contractCallTx(String sender, Na value, Long gasLimit, Long price, String contractAddress, String methodName, String methodDesc, String[][] args, String password, String remark) {
try {
AssertUtil.canNotEmpty(sender, "the sender address can not be empty");
AssertUtil.canNotEmpty(contractAddress, "the contractAddress can not be empty");
AssertUtil.canNotEmpty(methodName, "the methodName can not be empty");
if (value == null) {
value = Na.ZERO;
}
if (!ContractUtil.checkPrice(price.longValue())) {
return Result.getFailed(ContractErrorCode.CONTRACT_MINIMUM_PRICE);
}
Result<Account> accountResult = accountService.getAccount(sender);
if (accountResult.isFailed()) {
return accountResult;
}
Account account = accountResult.getData();
// 验证账户密码
if (account.isEncrypted() && account.isLocked()) {
AssertUtil.canNotEmpty(password, "the password can not be empty");
if (!account.validatePassword(password)) {
return Result.getFailed(AccountErrorCode.PASSWORD_IS_WRONG);
}
}
if (!account.isOk()) {
return Result.getFailed(AccountErrorCode.IMPORTING_ACCOUNT);
}
byte[] senderBytes = AddressTool.getAddress(sender);
byte[] contractAddressBytes = AddressTool.getAddress(contractAddress);
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执行数据
ProgramCall programCall = new ProgramCall();
programCall.setContractAddress(contractAddressBytes);
programCall.setSender(senderBytes);
programCall.setNumber(blockHeight);
programCall.setMethodName(methodName);
programCall.setMethodDesc(methodDesc);
programCall.setArgs(args);
// 如果方法是不上链的合约调用,同步执行合约代码,不改变状态根,并返回值
ProgramMethod method;
if ((method = vmHelper.getMethodInfoByContractAddress(methodName, methodDesc, contractAddressBytes)).isView()) {
ProgramResult programResult = vmHelper.invokeCustomGasViewMethod(contractAddressBytes, methodName, methodDesc, ContractUtil.twoDimensionalArray(args, method.argsType2Array()));
// programCall.setValue(BigInteger.ZERO);
// programCall.setGasLimit(ContractConstant.CONTRACT_CONSTANT_GASLIMIT);
// programCall.setPrice(ContractConstant.CONTRACT_CONSTANT_PRICE);
// ProgramExecutor track = programExecutor.begin(prevStateRoot);
// ProgramResult programResult = track.call(programCall);
Result result;
if (!programResult.isSuccess()) {
Log.error(programResult.getStackTrace());
result = Result.getFailed(ContractErrorCode.DATA_ERROR);
result.setMsg(ContractUtil.simplifyErrorMsg(programResult.getErrorMessage()));
result = checkVmResultAndReturn(programResult.getErrorMessage(), result);
} else {
result = Result.getSuccess();
result.setData(programResult.getResult());
}
return result;
}
// 创建链上交易,包含智能合约
programCall.setValue(BigInteger.valueOf(value.getValue()));
programCall.setPrice(price.longValue());
programCall.setGasLimit(gasLimit.longValue());
CallContractTransaction tx = new CallContractTransaction();
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);
}
// 执行VM验证合法性
ProgramExecutor track = programExecutor.begin(prevStateRoot);
// 验证合约时跳过Gas验证
long realGasLimit = programCall.getGasLimit();
programCall.setGasLimit(MAX_GASLIMIT);
ProgramResult programResult = track.call(programCall);
// 执行结果失败时,交易直接返回错误,不上链,不消耗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);
programCall.setGasLimit(realGasLimit);
programResult = track.call(programCall);
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
CallContractData callContractData = new CallContractData();
callContractData.setContractAddress(contractAddressBytes);
callContractData.setSender(senderBytes);
callContractData.setValue(value.getValue());
callContractData.setPrice(price.longValue());
callContractData.setGasLimit(gasLimit.longValue());
callContractData.setMethodName(methodName);
callContractData.setMethodDesc(methodDesc);
if (args != null) {
callContractData.setArgsCount((byte) args.length);
callContractData.setArgs(args);
}
tx.setTxData(callContractData);
CoinDataResult coinDataResult = accountLedgerService.getCoinData(senderBytes, totalNa, tx.size() + coinData.size(), TransactionFeeCalculator.MIN_PRICE_PRE_1024_BYTES);
if (!coinDataResult.isEnough()) {
return Result.getFailed(TransactionErrorCode.INSUFFICIENT_BALANCE);
}
coinData.setFrom(coinDataResult.getCoinList());
// 找零的UTXO
if (coinDataResult.getChange() != null) {
coinData.getTo().add(coinDataResult.getChange());
}
tx.setCoinData(coinData);
tx.setHash(NulsDigestData.calcDigestData(tx.serializeForHash()));
// 生成签名
List<ECKey> signEckeys = new ArrayList<>();
List<ECKey> scriptEckeys = new ArrayList<>();
ECKey eckey = account.getEcKey(password);
// 如果最后一位为1则表示该交易包含普通签名
if ((coinDataResult.getSignType() & 0x01) == 0x01) {
signEckeys.add(eckey);
}
// 如果倒数第二位位为1则表示该交易包含脚本签名
if ((coinDataResult.getSignType() & 0x02) == 0x02) {
scriptEckeys.add(eckey);
}
SignatureUtil.createTransactionSignture(tx, scriptEckeys, signEckeys);
// 保存未确认Token转账
Result<byte[]> unConfirmedTokenTransferResult = this.saveUnConfirmedTokenTransfer(tx, sender, contractAddress, methodName, args);
if (unConfirmedTokenTransferResult.isFailed()) {
return unConfirmedTokenTransferResult;
}
byte[] infoKey = unConfirmedTokenTransferResult.getData();
// 保存未确认交易到本地账本
Result saveResult = accountLedgerService.verifyAndSaveUnconfirmedTransaction(tx);
if (saveResult.isFailed()) {
if (infoKey != null) {
contractTokenTransferStorageService.deleteTokenTransferInfo(infoKey);
}
if (KernelErrorCode.DATA_SIZE_ERROR.getCode().equals(saveResult.getErrorCode().getCode())) {
// 重新算一次交易(不超出最大交易数据大小下)的最大金额
Result rs = accountLedgerService.getMaxAmountOfOnce(senderBytes, tx, TransactionFeeCalculator.MIN_PRICE_PRE_1024_BYTES);
if (rs.isSuccess()) {
Na maxAmount = (Na) rs.getData();
rs = Result.getFailed(KernelErrorCode.DATA_SIZE_ERROR_EXTEND);
rs.setMsg(rs.getMsg() + maxAmount.toDouble());
}
return rs;
}
return saveResult;
}
// 广播
Result sendResult = transactionService.broadcastTx(tx);
if (sendResult.isFailed()) {
// 失败则回滚
accountLedgerService.deleteTransaction(tx);
if (infoKey != null) {
contractTokenTransferStorageService.deleteTokenTransferInfo(infoKey);
}
return sendResult;
}
return Result.getSuccess().setData(tx.getHash().getDigestHex());
} catch (IOException e) {
Log.error(e);
Result result = Result.getFailed(ContractErrorCode.CONTRACT_EXECUTE_ERROR);
result.setMsg(e.getMessage());
return result;
} catch (NulsException e) {
Log.error(e);
return Result.getFailed(e.getErrorCode());
} catch (Exception e) {
Log.error(e);
Result result = Result.getFailed(ContractErrorCode.CONTRACT_EXECUTE_ERROR);
result.setMsg(e.getMessage());
return result;
}
}
use of io.nuls.contract.entity.txdata.CallContractData in project nuls by nuls-io.
the class ContractSdkResource method callContractTx.
@POST
@Path("/sdk/call")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "call smart contract transaction")
@ApiResponses(value = { @ApiResponse(code = 200, message = "success") })
public RpcClientResult callContractTx(@ApiParam(name = "callForm", value = "call smart contract transaction", required = true) CallContractTx callContractTx) {
if (!AddressTool.validAddress(callContractTx.getSender())) {
return Result.getFailed(AccountErrorCode.ADDRESS_ERROR).toRpcClientResult();
}
String contractAddress = callContractTx.getContractAddress();
if (!AddressTool.validAddress(contractAddress)) {
return Result.getFailed(AccountErrorCode.ADDRESS_ERROR).toRpcClientResult();
}
if (callContractTx.getGasLimit() < 0 || callContractTx.getPrice() < 0) {
return Result.getFailed(ContractErrorCode.PARAMETER_ERROR).toRpcClientResult();
}
if (StringUtils.isBlank(callContractTx.getMethodName())) {
return Result.getFailed(ContractErrorCode.PARAMETER_ERROR).toRpcClientResult();
}
if (callContractTx.getUtxos().isEmpty()) {
// utxo not null and size not zero.
return Result.getFailed(AccountErrorCode.DATA_PARSE_ERROR).toRpcClientResult();
}
Long gasLimit = callContractTx.getGasLimit();
Long price = callContractTx.getPrice();
Na value = Na.valueOf(callContractTx.getValue());
byte[] senderBytes = AddressTool.getAddress(callContractTx.getSender());
byte[] contractAddressBytes = AddressTool.getAddress(callContractTx.getContractAddress());
if (value == null) {
value = Na.ZERO;
}
CallContractTransaction tx = new CallContractTransaction();
String remark = callContractTx.getRemark();
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());
long gasUsed = gasLimit.longValue();
Na imputedNa = Na.valueOf(LongUtils.mul(gasUsed, price));
Na totalNa = imputedNa.add(value);
CallContractData callContractData = new CallContractData();
callContractData.setContractAddress(contractAddressBytes);
callContractData.setSender(senderBytes);
callContractData.setValue(value.getValue());
callContractData.setPrice(price.longValue());
callContractData.setGasLimit(gasLimit.longValue());
callContractData.setMethodName(callContractTx.getMethodName());
callContractData.setMethodDesc(callContractTx.getMethodDesc());
/**
* build args
*/
Object[] args = callContractTx.getArgs();
if (args != null) {
callContractData.setArgsCount((byte) args.length);
callContractData.setArgs(ContractUtil.twoDimensionalArray(args));
}
tx.setTxData(callContractData);
/**
* build coin data
*/
CoinData coinData = new CoinData();
// transfer to contract address.
if (value.isGreaterThan(Na.ZERO)) {
Coin toCoin = new Coin(contractAddressBytes, value);
coinData.getTo().add(toCoin);
}
List<Coin> coinList = ConvertCoinTool.convertCoinList(callContractTx.getUtxos());
CoinDataResult coinDataResult = CoinDataTool.getCoinData(senderBytes, totalNa, tx.size() + coinData.size(), TransactionFeeCalculator.MIN_PRICE_PRE_1024_BYTES, coinList);
if (!coinDataResult.isEnough()) {
return RpcClientResult.getFailed(TransactionErrorCode.INSUFFICIENT_BALANCE);
}
coinData.setFrom(coinDataResult.getCoinList());
if (coinDataResult.getChange() != null) {
coinData.getTo().add(coinDataResult.getChange());
}
tx.setCoinData(coinData);
// 重置为0,重新计算交易对象的size
tx.setSize(0);
if (tx.getSize() > TxMaxSizeValidator.MAX_TX_SIZE) {
return Result.getFailed(TransactionErrorCode.DATA_SIZE_ERROR).toRpcClientResult();
}
return this.buildReturnInfo(tx, contractAddress);
}
use of io.nuls.contract.entity.txdata.CallContractData in project nuls by nuls-io.
the class CallContractTxProcessor method onCommit.
@Override
public Result onCommit(CallContractTransaction tx, Object secondaryData) {
try {
ContractResult contractResult = tx.getContractResult();
// 保存调用合约交易的UTXO
Result utxoResult = contractUtxoService.saveUtxoForContractAddress(tx);
if (utxoResult.isFailed()) {
Log.error("save confirmed contract utxo error, reason is {}.", utxoResult.getMsg());
return utxoResult;
}
long blockHeight = tx.getBlockHeight();
/**
* 保存子交易到全网账本、合约账本、本地账本
*/
Collection<ContractTransferTransaction> contractTransferTxs = tx.getContractTransferTxs();
if (contractTransferTxs != null && contractTransferTxs.size() > 0) {
for (ContractTransferTransaction transferTx : contractTransferTxs) {
try {
transferTx.setBlockHeight(blockHeight);
Result result = ledgerService.saveTx(transferTx);
if (result.isFailed()) {
Log.error("save contract transfer tx to ledger error. msg: {}", result.getMsg());
return result;
}
result = contractService.saveContractTransferTx(transferTx);
if (result.isFailed()) {
Log.error("save contract transfer tx to contract ledger error. msg: {}", result.getMsg());
return result;
}
result = accountLedgerService.saveConfirmedTransaction(transferTx);
if (result.isFailed()) {
Log.error("save contract transfer tx to account ledger error. msg: {}", result.getMsg());
return result;
}
} catch (Exception e) {
e.printStackTrace();
Log.error("save contract transfer tx error. msg: {}", e.getMessage());
return Result.getFailed();
}
}
}
// 保存代币交易
CallContractData callContractData = tx.getTxData();
byte[] contractAddress = callContractData.getContractAddress();
Result<ContractAddressInfoPo> contractAddressInfoPoResult = contractAddressStorageService.getContractAddressInfo(contractAddress);
if (contractAddressInfoPoResult.isFailed()) {
return contractAddressInfoPoResult;
}
ContractAddressInfoPo contractAddressInfoPo = contractAddressInfoPoResult.getData();
if (contractAddressInfoPo == null) {
return Result.getFailed(ContractErrorCode.CONTRACT_ADDRESS_NOT_EXIST);
}
contractResult.setNrc20(contractAddressInfoPo.isNrc20());
BlockHeader blockHeader = tx.getBlockHeader();
byte[] newestStateRoot = blockHeader.getStateRoot();
// 获取合约当前状态
ProgramStatus status = vmHelper.getContractStatus(newestStateRoot, contractAddress);
boolean isTerminatedContract = ContractUtil.isTerminatedContract(status.ordinal());
// 处理合约执行失败 - 没有transferEvent的情况, 直接从数据库中获取, 若是本地创建的交易,获取到修改为失败交易
if (isTerminatedContract || !contractResult.isSuccess()) {
if (contractAddressInfoPo != null && contractAddressInfoPo.isNrc20() && ContractUtil.isTransferMethod(callContractData.getMethodName())) {
byte[] txHashBytes = tx.getHash().serialize();
byte[] infoKey = ArraysTool.concatenate(callContractData.getSender(), txHashBytes, new VarInt(0).encode());
Result<ContractTokenTransferInfoPo> infoResult = contractTokenTransferStorageService.getTokenTransferInfo(infoKey);
ContractTokenTransferInfoPo po = infoResult.getData();
if (po != null) {
po.setStatus((byte) 2);
contractTokenTransferStorageService.saveTokenTransferInfo(infoKey, po);
// 刷新token余额
if (isTerminatedContract) {
// 终止的合约,回滚token余额
this.rollbackContractToken(po);
contractResult.setError(true);
contractResult.setErrorMessage("this contract has been terminated");
} else {
if (po.getFrom() != null) {
vmHelper.refreshTokenBalance(newestStateRoot, contractAddressInfoPo, AddressTool.getStringAddressByBytes(po.getFrom()), po.getContractAddress());
}
if (po.getTo() != null) {
vmHelper.refreshTokenBalance(newestStateRoot, contractAddressInfoPo, AddressTool.getStringAddressByBytes(po.getTo()), po.getContractAddress());
}
}
}
}
}
if (!isTerminatedContract) {
// 处理合约事件
vmHelper.dealEvents(newestStateRoot, tx, contractResult, contractAddressInfoPo);
}
// 保存合约执行结果
contractService.saveContractExecuteResult(tx.getHash(), contractResult);
} catch (Exception e) {
Log.error("save call contract tx error.", e);
return Result.getFailed();
}
return Result.getSuccess();
}
Aggregations