Search in sources :

Example 1 with ECKey

use of io.nuls.core.tools.crypto.ECKey in project nuls by nuls-io.

the class AccountLedgerServiceImpl method sendToAddress.

@Override
public Result sendToAddress(byte[] from, byte[] to, Na values, String password, byte[] remark, Na price) {
    try {
        Result<Account> accountResult = accountService.getAccount(from);
        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);
            }
        }
        // 检查to是否为合约地址,如果是合约地址,则返回错误
        if (contractService.isContractAddress(to)) {
            return Result.getFailed(ContractErrorCode.NON_CONTRACTUAL_TRANSACTION_NO_TRANSFER);
        }
        if (price == null) {
            price = TransactionFeeCalculator.MIN_PRICE_PRE_1024_BYTES;
        }
        TransactionDataResult txResult = createTransferTx(from, values, price, to, remark);
        if (!txResult.isEnough()) {
            return Result.getFailed(AccountLedgerErrorCode.INSUFFICIENT_BALANCE);
        }
        Transaction tx = txResult.getTransaction();
        tx.setHash(NulsDigestData.calcDigestData(tx.serializeForHash()));
        // 生成签名
        List<ECKey> signEckeys = new ArrayList<>();
        List<ECKey> scriptEckeys = new ArrayList<>();
        ECKey eckey = account.getEcKey(password);
        // 如果最后一位为1则表示该交易包含普通签名
        if ((txResult.getSignType() & 0x01) == 0x01) {
            signEckeys.add(eckey);
        }
        // 如果倒数第二位位为1则表示该交易包含脚本签名
        if ((txResult.getSignType() & 0x02) == 0x02) {
            scriptEckeys.add(eckey);
        }
        SignatureUtil.createTransactionSignture(tx, scriptEckeys, signEckeys);
        // 保存未确认交易到本地账户
        Result saveResult = verifyAndSaveUnconfirmedTransaction(tx);
        if (saveResult.isFailed()) {
            if (KernelErrorCode.DATA_SIZE_ERROR.getCode().equals(saveResult.getErrorCode().getCode())) {
                // 重新算一次交易(不超出最大交易数据大小下)的最大金额
                Result rs = getMaxAmountOfOnce(from, tx, price);
                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;
        }
        // transactionService.newTx(tx);
        Result sendResult = transactionService.broadcastTx(tx);
        if (sendResult.isFailed()) {
            this.deleteTransaction(tx);
            return sendResult;
        }
        return Result.getSuccess().setData(tx.getHash().getDigestHex());
    } catch (IOException e) {
        Log.error(e);
        return Result.getFailed(KernelErrorCode.IO_ERROR);
    } catch (NulsException e) {
        Log.error(e);
        return Result.getFailed(e.getErrorCode());
    }
}
Also used : Account(io.nuls.account.model.Account) MultiSigAccount(io.nuls.account.model.MultiSigAccount) TransferTransaction(io.nuls.protocol.model.tx.TransferTransaction) DataTransaction(io.nuls.protocol.model.tx.DataTransaction) NulsException(io.nuls.kernel.exception.NulsException) ECKey(io.nuls.core.tools.crypto.ECKey) TransactionDataResult(io.nuls.account.ledger.model.TransactionDataResult) IOException(java.io.IOException) TransactionDataResult(io.nuls.account.ledger.model.TransactionDataResult) ValidateResult(io.nuls.kernel.validate.ValidateResult) CoinDataResult(io.nuls.account.ledger.model.CoinDataResult)

Example 2 with ECKey

use of io.nuls.core.tools.crypto.ECKey in project nuls by nuls-io.

the class AccountLedgerServiceImpl method txMultiProcess.

@Override
public Result txMultiProcess(Transaction tx, TransactionSignature transactionSignature, Account account, String password) {
    try {
        List<P2PHKSignature> p2PHKSignatures = new ArrayList<>();
        if (transactionSignature.getP2PHKSignatures() != null && transactionSignature.getP2PHKSignatures().size() > 0) {
            p2PHKSignatures = transactionSignature.getP2PHKSignatures();
        }
        List<Script> scripts = transactionSignature.getScripts();
        // 使用签名账户对交易进行签名
        P2PHKSignature p2PHKSignature = new P2PHKSignature();
        ECKey eckey = account.getEcKey(password);
        p2PHKSignature.setPublicKey(eckey.getPubKey());
        // 用当前交易的hash和账户的私钥账户
        p2PHKSignature.setSignData(accountService.signDigest(tx.getHash().getDigestBytes(), eckey));
        p2PHKSignatures.add(p2PHKSignature);
        // 当已签名数等于M则自动广播该交易
        if (p2PHKSignatures.size() == SignatureUtil.getM(scripts.get(0))) {
            // 将交易中的签名数据P2PHKSignatures按规则排序
            p2PHKSignatures.sort(P2PHKSignature.PUBKEY_COMPARATOR);
            // 将排序后的P2PHKSignatures的签名数据取出和赎回脚本结合生成解锁脚本
            List<byte[]> signatures = new ArrayList<>();
            for (P2PHKSignature p2PHKSignatureTemp : p2PHKSignatures) {
                signatures.add(p2PHKSignatureTemp.getSignData().getSignBytes());
            }
            transactionSignature.setP2PHKSignatures(null);
            Script scriptSign = ScriptBuilder.createNulsP2SHMultiSigInputScript(signatures, scripts.get(0));
            transactionSignature.getScripts().clear();
            transactionSignature.getScripts().add(scriptSign);
            tx.setTransactionSignature(transactionSignature.serialize());
            // 保存未确认交易到本地账户
            Result saveResult = verifyAndSaveUnconfirmedTransaction(tx);
            if (saveResult.isFailed()) {
                return saveResult;
            }
            transactionService.newTx(tx);
            Result sendResult = transactionService.broadcastTx(tx);
            if (sendResult.isFailed()) {
                this.deleteTransaction(tx);
                return sendResult;
            }
            return Result.getSuccess().setData(tx.getHash().getDigestHex());
        } else // 如果签名数还没达到,则返回交易
        {
            transactionSignature.setP2PHKSignatures(p2PHKSignatures);
            tx.setTransactionSignature(transactionSignature.serialize());
            return Result.getSuccess().setData(Hex.encode(tx.serialize()));
        }
    } catch (IOException e) {
        Log.error(e);
        return Result.getFailed(KernelErrorCode.IO_ERROR);
    } catch (NulsException e) {
        Log.error(e);
        return Result.getFailed(e.getErrorCode());
    }
}
Also used : NulsException(io.nuls.kernel.exception.NulsException) ECKey(io.nuls.core.tools.crypto.ECKey) IOException(java.io.IOException) TransactionDataResult(io.nuls.account.ledger.model.TransactionDataResult) ValidateResult(io.nuls.kernel.validate.ValidateResult) CoinDataResult(io.nuls.account.ledger.model.CoinDataResult)

Example 3 with ECKey

use of io.nuls.core.tools.crypto.ECKey in project nuls by nuls-io.

the class AccountLedgerServiceImpl method multipleAddressTransfer.

@Override
public Result multipleAddressTransfer(Set<String> addressSet, List<MultipleAddressTransferModel> fromList, List<MultipleAddressTransferModel> toList, Na amount, String remark, Na price) {
    try {
        for (MultipleAddressTransferModel from : fromList) {
            Result<Account> accountResult = accountService.getAccount(from.getAddress());
            if (accountResult.isFailed()) {
                return accountResult;
            }
            Account account = accountResult.getData();
            if (account.isEncrypted() && account.isLocked()) {
                AssertUtil.canNotEmpty(from.getPassword(), "the password can not be empty");
                if (!account.validatePassword(from.getPassword())) {
                    Result result = Result.getFailed(AccountErrorCode.PASSWORD_IS_WRONG);
                    result.setMsg(result.getErrorCode().getMsg() + ",address :" + AddressTool.getStringAddressByBytes(from.getAddress()));
                    return result;
                }
            }
        }
        for (MultipleAddressTransferModel to : toList) {
            // 检查to是否为合约地址,如果是合约地址,则返回错误
            if (contractService.isContractAddress(to.getAddress())) {
                return Result.getFailed(ContractErrorCode.NON_CONTRACTUAL_TRANSACTION_NO_TRANSFER);
            }
        }
        TransferTransaction tx = new TransferTransaction();
        if (StringUtils.isNotBlank(remark)) {
            try {
                tx.setRemark(remark.getBytes(NulsConfig.DEFAULT_ENCODING));
            } catch (UnsupportedEncodingException e) {
                Log.error(e);
            }
        }
        tx.setTime(TimeService.currentTimeMillis());
        CoinData coinData = new CoinData();
        for (MultipleAddressTransferModel to : toList) {
            // 如果为多签地址
            Coin toCoin = null;
            if (to.getAddress()[2] == 3) {
                Script scriptPubkey = SignatureUtil.createOutputScript(to.getAddress());
                toCoin = new Coin(scriptPubkey.getProgram(), Na.valueOf(to.getAmount()));
            } else {
                toCoin = new Coin(to.getAddress(), Na.valueOf(to.getAmount()));
            }
            coinData.getTo().add(toCoin);
        }
        if (price == null) {
            price = TransactionFeeCalculator.MIN_PRICE_PRE_1024_BYTES;
        }
        CoinDataResult coinDataResult = getCoinDataMultipleAdresses(fromList, amount, tx.size() + coinData.size() + addressSet.size() * P2PHKSignature.SERIALIZE_LENGTH, price);
        // 从多个地址中获取币 from
        List<Coin> fromCoinList = new ArrayList<>();
        List<Coin> changeCoinList = new ArrayList<>();
        if (!coinDataResult.isEnough()) {
            // 验证utxo是否足够
            return Result.getFailed(AccountLedgerErrorCode.INSUFFICIENT_BALANCE);
        }
        // 把每个地址获取的币放到list里面
        fromCoinList.addAll(coinDataResult.getCoinList());
        if (coinDataResult.getChange() != null) {
            changeCoinList.add(coinDataResult.getChange());
        }
        // 每个地址from获取的utxo list
        coinData.setFrom(fromCoinList);
        // 找零钱
        coinData.getTo().addAll(changeCoinList);
        tx.setCoinData(coinData);
        tx.setHash(NulsDigestData.calcDigestData(tx.serializeForHash()));
        // 生成签名
        List<ECKey> signEckeys = new ArrayList<>();
        List<ECKey> scriptEckeys = new ArrayList<>();
        for (int index = 0; index < fromList.size(); index++) {
            Result<Account> accountResult = accountService.getAccount(fromList.get(index).getAddress());
            Account account = accountResult.getData();
            // 用于生成ECKey
            ECKey ecKey = account.getEcKey(fromList.get(index).getPassword());
            // 如果最后一位为1则表示该交易包含普通签名
            if ((coinDataResult.getSignType() & 0x01) == 0x01) {
                signEckeys.add(ecKey);
            }
            // 如果倒数第二位位为1则表示该交易包含脚本签名
            if ((coinDataResult.getSignType() & 0x02) == 0x02) {
                scriptEckeys.add(ecKey);
            }
        }
        SignatureUtil.createTransactionSignture(tx, scriptEckeys, signEckeys);
        // 保存未确认交易到本地账户
        Result saveResult = verifyAndSaveUnconfirmedTransaction(tx);
        if (saveResult.isFailed()) {
            for (MultipleAddressTransferModel from : fromList) {
                if (KernelErrorCode.DATA_SIZE_ERROR.getCode().equals(saveResult.getErrorCode().getCode())) {
                    // 重新算一次交易(不超出最大交易数据大小下)的最大金额
                    Na maxAmount = getMaxAmountOfOnce(from.getAddress(), tx, price).getData();
                    Result 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()) {
            this.deleteTransaction(tx);
            return sendResult;
        }
        return Result.getSuccess().setData(tx.getHash().getDigestHex());
    } catch (IOException e) {
        Log.error(e);
        return Result.getFailed(KernelErrorCode.IO_ERROR);
    } catch (NulsException e) {
        Log.error(e);
        return Result.getFailed(e.getErrorCode());
    }
}
Also used : Account(io.nuls.account.model.Account) MultiSigAccount(io.nuls.account.model.MultiSigAccount) MultipleAddressTransferModel(io.nuls.account.ledger.model.MultipleAddressTransferModel) UnsupportedEncodingException(java.io.UnsupportedEncodingException) ECKey(io.nuls.core.tools.crypto.ECKey) IOException(java.io.IOException) TransactionDataResult(io.nuls.account.ledger.model.TransactionDataResult) ValidateResult(io.nuls.kernel.validate.ValidateResult) CoinDataResult(io.nuls.account.ledger.model.CoinDataResult) NulsException(io.nuls.kernel.exception.NulsException) TransferTransaction(io.nuls.protocol.model.tx.TransferTransaction) CoinDataResult(io.nuls.account.ledger.model.CoinDataResult)

Example 4 with ECKey

use of io.nuls.core.tools.crypto.ECKey in project nuls by nuls-io.

the class AccountLedgerResource method signMultipleTx.

@POST
@Path("/signMultipleTx")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "签名多地址转账交易", notes = "result.data: resultJson 返回签名后的txHex")
@ApiResponses(value = { @ApiResponse(code = 200, message = "success") })
public RpcClientResult signMultipleTx(@ApiParam(name = "form", value = "签名多地址转账交易", required = true) MulitpleTxForm form) {
    String txHex = form.getTxHex();
    List<String> priKeys = form.getPriKeys();
    String password = form.getPassword();
    if (StringUtils.isBlank(txHex) || priKeys.isEmpty()) {
        return Result.getFailed(AccountErrorCode.PARAMETER_ERROR).toRpcClientResult();
    }
    List<String> signKeys = new ArrayList<>(priKeys.size());
    for (int i = 0; i < priKeys.size(); i++) {
        String encryptPrivKey = priKeys.get(i);
        if (StringUtils.isBlank(password)) {
            signKeys.add(encryptPrivKey);
            continue;
        }
        byte[] privateKeyBytes = null;
        try {
            privateKeyBytes = AESEncrypt.decrypt(Hex.decode(encryptPrivKey), password);
        } catch (Exception e) {
            return Result.getFailed(AccountErrorCode.DECRYPT_ACCOUNT_ERROR).toRpcClientResult();
        }
        signKeys.add(Hex.encode(privateKeyBytes));
    }
    // conversion private key string to ECKey
    List<ECKey> keys = signKeys.stream().map(p -> ECKey.fromPrivate(new BigInteger(Hex.decode(p)))).collect(Collectors.toList());
    try {
        byte[] data = Hex.decode(txHex);
        Transaction tx = TransactionManager.getInstance(new NulsByteBuffer(data));
        List<P2PHKSignature> p2PHKSignatures = SignatureUtil.createSignaturesByEckey(tx, keys);
        TransactionSignature transactionSignature = new TransactionSignature();
        transactionSignature.setP2PHKSignatures(p2PHKSignatures);
        tx.setTransactionSignature(transactionSignature.serialize());
        txHex = Hex.encode(tx.serialize());
        Map<String, String> map = new HashMap<>();
        map.put("value", txHex);
        return Result.getSuccess().setData(map).toRpcClientResult();
    } catch (Exception e) {
        Log.error(e);
        return Result.getFailed(AccountErrorCode.DATA_PARSE_ERROR).toRpcClientResult();
    }
}
Also used : io.nuls.accout.ledger.rpc.form(io.nuls.accout.ledger.rpc.form) Output(io.protostuff.Output) ECKey(io.nuls.core.tools.crypto.ECKey) StringUtils(io.nuls.core.tools.str.StringUtils) CryptoException(io.nuls.core.tools.crypto.Exception.CryptoException) MediaType(javax.ws.rs.core.MediaType) UtxoDtoComparator(io.nuls.accout.ledger.rpc.util.UtxoDtoComparator) AccountTool(io.nuls.account.util.AccountTool) NulsContext(io.nuls.kernel.context.NulsContext) Page(io.nuls.core.tools.page.Page) io.swagger.annotations(io.swagger.annotations) BigInteger(java.math.BigInteger) SignatureUtil(io.nuls.kernel.script.SignatureUtil) TransferTransaction(io.nuls.protocol.model.tx.TransferTransaction) AccountLedgerErrorCode(io.nuls.account.ledger.constant.AccountLedgerErrorCode) TransactionSignature(io.nuls.kernel.script.TransactionSignature) CoinDataTool(io.nuls.account.ledger.util.CoinDataTool) TxStatusEnum(io.nuls.kernel.constant.TxStatusEnum) Collectors(java.util.stream.Collectors) ProtocolConstant(io.nuls.protocol.constant.ProtocolConstant) Arrays(org.spongycastle.util.Arrays) AccountService(io.nuls.account.service.AccountService) MultipleAddressTransferModel(io.nuls.account.ledger.model.MultipleAddressTransferModel) NulsException(io.nuls.kernel.exception.NulsException) javax.ws.rs(javax.ws.rs) AESEncrypt(io.nuls.core.tools.crypto.AESEncrypt) Autowired(io.nuls.kernel.lite.annotation.Autowired) ContractTokenTransferInfoPo(io.nuls.contract.dto.ContractTokenTransferInfoPo) TxMaxSizeValidator(io.nuls.protocol.model.validator.TxMaxSizeValidator) UnsupportedEncodingException(java.io.UnsupportedEncodingException) LedgerService(io.nuls.ledger.service.LedgerService) java.util(java.util) TimeService(io.nuls.kernel.func.TimeService) Script(io.nuls.kernel.script.Script) AccountLedgerService(io.nuls.account.ledger.service.AccountLedgerService) io.nuls.kernel.utils(io.nuls.kernel.utils) P2PHKSignature(io.nuls.kernel.script.P2PHKSignature) LedgerRpcUtil(io.nuls.accout.ledger.rpc.util.LedgerRpcUtil) Component(io.nuls.kernel.lite.annotation.Component) io.nuls.kernel.model(io.nuls.kernel.model) ValidateResult(io.nuls.kernel.validate.ValidateResult) AccountLegerUtils(io.nuls.account.ledger.base.util.AccountLegerUtils) AccountErrorCode(io.nuls.account.constant.AccountErrorCode) NulsConstant(io.nuls.kernel.constant.NulsConstant) CoinDataResult(io.nuls.account.ledger.model.CoinDataResult) MapUtil(io.nuls.core.tools.map.MapUtil) KernelErrorCode(io.nuls.kernel.constant.KernelErrorCode) TransactionErrorCode(io.nuls.kernel.constant.TransactionErrorCode) io.nuls.accout.ledger.rpc.dto(io.nuls.accout.ledger.rpc.dto) CoinComparator(io.nuls.account.ledger.base.util.CoinComparator) NulsRuntimeException(io.nuls.kernel.exception.NulsRuntimeException) TransactionInfo(io.nuls.account.ledger.model.TransactionInfo) NulsConfig(io.nuls.kernel.cfg.NulsConfig) IOException(java.io.IOException) Log(io.nuls.core.tools.log.Log) ConvertCoinTool(io.nuls.accout.ledger.rpc.util.ConvertCoinTool) ContractService(io.nuls.contract.service.ContractService) Hex(io.nuls.core.tools.crypto.Hex) LedgerErrorCode(io.nuls.ledger.constant.LedgerErrorCode) LocalUtxoService(io.nuls.account.ledger.base.service.LocalUtxoService) Balance(io.nuls.account.model.Balance) ECKey(io.nuls.core.tools.crypto.ECKey) TransactionSignature(io.nuls.kernel.script.TransactionSignature) CryptoException(io.nuls.core.tools.crypto.Exception.CryptoException) NulsException(io.nuls.kernel.exception.NulsException) UnsupportedEncodingException(java.io.UnsupportedEncodingException) NulsRuntimeException(io.nuls.kernel.exception.NulsRuntimeException) IOException(java.io.IOException) TransferTransaction(io.nuls.protocol.model.tx.TransferTransaction) P2PHKSignature(io.nuls.kernel.script.P2PHKSignature) BigInteger(java.math.BigInteger)

Example 5 with ECKey

use of io.nuls.core.tools.crypto.ECKey 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;
    }
}
Also used : Account(io.nuls.account.model.Account) ECKey(io.nuls.core.tools.crypto.ECKey) ContractResult(io.nuls.contract.dto.ContractResult) CoinDataResult(io.nuls.account.ledger.model.CoinDataResult) NulsException(io.nuls.kernel.exception.NulsException) CallContractTransaction(io.nuls.contract.entity.tx.CallContractTransaction) CoinDataResult(io.nuls.account.ledger.model.CoinDataResult) UnsupportedEncodingException(java.io.UnsupportedEncodingException) IOException(java.io.IOException) CallContractData(io.nuls.contract.entity.txdata.CallContractData) NulsException(io.nuls.kernel.exception.NulsException) UnsupportedEncodingException(java.io.UnsupportedEncodingException) IOException(java.io.IOException)

Aggregations

ECKey (io.nuls.core.tools.crypto.ECKey)43 NulsException (io.nuls.kernel.exception.NulsException)26 IOException (java.io.IOException)20 CoinDataResult (io.nuls.account.ledger.model.CoinDataResult)15 BigInteger (java.math.BigInteger)14 Account (io.nuls.account.model.Account)12 ArrayList (java.util.ArrayList)11 TransferTransaction (io.nuls.protocol.model.tx.TransferTransaction)10 MultiSigAccount (io.nuls.account.model.MultiSigAccount)8 ValidateResult (io.nuls.kernel.validate.ValidateResult)8 UnsupportedEncodingException (java.io.UnsupportedEncodingException)8 TransactionDataResult (io.nuls.account.ledger.model.TransactionDataResult)6 CryptoException (io.nuls.core.tools.crypto.Exception.CryptoException)6 NulsRuntimeException (io.nuls.kernel.exception.NulsRuntimeException)4 MultipleAddressTransferModel (io.nuls.account.ledger.model.MultipleAddressTransferModel)3 Alias (io.nuls.account.model.Alias)3 AliasTransaction (io.nuls.account.tx.AliasTransaction)3 Agent (io.nuls.consensus.poc.protocol.entity.Agent)3 ContractResult (io.nuls.contract.dto.ContractResult)3 Script (io.nuls.kernel.script.Script)3