use of io.nuls.account.ledger.model.CoinDataResult 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());
}
}
use of io.nuls.account.ledger.model.CoinDataResult in project nuls by nuls-io.
the class AccountLedgerResource method createTransactionSimple.
@POST
@Path("/transaction/simple")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "创建转账交易(不用计算手续费)", notes = "返回交易Hash,交易对象序列化数组Hex编码,本次交易的输入,输出")
@ApiResponses(value = { @ApiResponse(code = 200, message = "success") })
public RpcClientResult createTransactionSimple(@ApiParam(name = "form", value = "转账参数(传入该账户拥有的UTXO)", required = true) TransferSimpleForm form) {
try {
if (form == null) {
return Result.getFailed(AccountErrorCode.ADDRESS_ERROR).toRpcClientResult();
}
if (form.getUtxos() == null || form.getUtxos().isEmpty()) {
return RpcClientResult.getFailed("inputs error");
}
if (!AddressTool.validAddress(form.getAddress())) {
return Result.getFailed(AccountErrorCode.ADDRESS_ERROR).toRpcClientResult();
}
if (!AddressTool.validAddress(form.getToAddress())) {
return Result.getFailed(AccountErrorCode.ADDRESS_ERROR).toRpcClientResult();
}
if (form.getAmount() <= 0) {
return Result.getFailed(AccountLedgerErrorCode.PARAMETER_ERROR).toRpcClientResult();
}
byte[] remarkBytes = new byte[0];
if (form.getRemark() != null && form.getRemark().length() > 0) {
try {
remarkBytes = form.getRemark().getBytes(NulsConfig.DEFAULT_ENCODING);
} catch (UnsupportedEncodingException e) {
return Result.getFailed(AccountLedgerErrorCode.PARAMETER_ERROR).toRpcClientResult();
}
}
byte[] fromBytes = AddressTool.getAddress(form.getAddress());
byte[] toBytes = AddressTool.getAddress(form.getToAddress());
Na values = Na.valueOf(form.getAmount());
TransferTransaction tx = new TransferTransaction();
tx.setRemark(remarkBytes);
tx.setTime(TimeService.currentTimeMillis());
CoinData coinData = new CoinData();
// 如果为多签地址则以脚本方式存储
Coin toCoin;
if (toBytes[2] == NulsContext.P2SH_ADDRESS_TYPE) {
Script scriptPubkey = SignatureUtil.createOutputScript(toBytes);
toCoin = new Coin(scriptPubkey.getProgram(), values);
} else {
toCoin = new Coin(toBytes, values);
}
coinData.getTo().add(toCoin);
List<Coin> coinList = ConvertCoinTool.convertCoinList(form.getUtxos());
CoinDataResult coinDataResult = getCoinData(fromBytes, values, tx.size() + coinData.size(), TransactionFeeCalculator.MIN_PRICE_PRE_1024_BYTES, coinList);
if (!coinDataResult.isEnough()) {
return Result.getFailed(TransactionErrorCode.INSUFFICIENT_BALANCE).toRpcClientResult();
}
coinData.setFrom(coinDataResult.getCoinList());
// 找零的UTXO
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();
}
TransactionCreatedReturnInfo returnInfo = LedgerRpcUtil.makeReturnInfo(tx);
Map<String, TransactionCreatedReturnInfo> map = new HashMap<>();
map.put("value", returnInfo);
return Result.getSuccess().setData(map).toRpcClientResult();
} catch (Exception e) {
Log.error(e);
return Result.getFailed(AccountLedgerErrorCode.SYS_UNKOWN_EXCEPTION).toRpcClientResult();
}
}
use of io.nuls.account.ledger.model.CoinDataResult in project nuls by nuls-io.
the class CoinDataTool method getCoinData.
public static CoinDataResult getCoinData(byte[] address, Na amount, int size, Na price, List<Coin> coinList) {
if (null == price) {
throw new NulsRuntimeException(KernelErrorCode.PARAMETER_ERROR);
}
CoinDataResult coinDataResult = new CoinDataResult();
coinDataResult.setEnough(false);
if (coinList.isEmpty()) {
return coinDataResult;
}
List<Coin> coins = new ArrayList<>();
Na values = Na.ZERO;
// 累加到足够支付转出额与手续费
for (int i = 0; i < coinList.size(); i++) {
Coin coin = coinList.get(i);
coins.add(coin);
size += coin.size();
if (i == 127) {
size += 1;
}
// 每次累加一条未花费余额时,需要重新计算手续费
Na fee = TransactionFeeCalculator.getFee(size, price);
values = values.add(coin.getNa());
/**
* 判断是否是脚本验证UTXO
*/
int signType = coinDataResult.getSignType();
if (signType != 3) {
if ((signType & 0x01) == 0 && coin.getTempOwner().length == 23) {
coinDataResult.setSignType((byte) (signType | 0x01));
size += P2PHKSignature.SERIALIZE_LENGTH;
} else if ((signType & 0x02) == 0 && coin.getTempOwner().length != 23) {
coinDataResult.setSignType((byte) (signType | 0x02));
size += P2PHKSignature.SERIALIZE_LENGTH;
}
}
// 需要判断是否找零,如果有找零,则需要重新计算手续费
if (values.isGreaterThan(amount.add(fee))) {
Na change = values.subtract(amount.add(fee));
Coin changeCoin = new Coin();
if (address[2] == NulsContext.P2SH_ADDRESS_TYPE) {
changeCoin.setOwner(SignatureUtil.createOutputScript(address).getProgram());
} else {
changeCoin.setOwner(address);
}
changeCoin.setNa(change);
fee = TransactionFeeCalculator.getFee(size + changeCoin.size(), price);
if (values.isLessThan(amount.add(fee))) {
continue;
}
changeCoin.setNa(values.subtract(amount.add(fee)));
if (!changeCoin.getNa().equals(Na.ZERO)) {
coinDataResult.setChange(changeCoin);
}
}
coinDataResult.setFee(fee);
if (values.isGreaterOrEquals(amount.add(fee))) {
coinDataResult.setEnough(true);
coinDataResult.setCoinList(coins);
break;
}
}
return coinDataResult;
}
use of io.nuls.account.ledger.model.CoinDataResult 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.account.ledger.model.CoinDataResult in project nuls by nuls-io.
the class ContractTxServiceImpl method contractCreateTx.
/**
* 创建生成智能合约的交易
* 如果是创建合约的交易,交易仅仅用于创建合约,合约内部不执行复杂逻辑
*
* @param sender 交易创建者
* @param gasLimit 最大gas消耗
* @param price 执行合约单价
* @param contractCode 合约代码
* @param args 参数列表
* @param password 账户密码
* @param remark 备注
* @return
*/
@Override
public Result contractCreateTx(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;
}
if (!accountResult.getData().isOk()) {
return Result.getFailed(AccountErrorCode.IMPORTING_ACCOUNT);
}
if (!ContractUtil.checkPrice(price.longValue())) {
return Result.getFailed(ContractErrorCode.CONTRACT_MINIMUM_PRICE);
}
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);
}
}
// 生成一个地址作为智能合约地址
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();
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() + 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);
// 保存未确认交易到本地账本
Result saveResult = accountLedgerService.verifyAndSaveUnconfirmedTransaction(tx);
if (saveResult.isFailed()) {
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);
return sendResult;
}
Map<String, String> resultMap = MapUtil.createHashMap(2);
String txHash = tx.getHash().getDigestHex();
String contractAddressStr = AddressTool.getStringAddressByBytes(contractAddressBytes);
resultMap.put("txHash", txHash);
resultMap.put("contractAddress", contractAddressStr);
// 保留未确认的创建合约交易到内存中
this.saveLocalUnconfirmedCreateContractTransaction(sender, resultMap, tx.getTime());
return Result.getSuccess().setData(resultMap);
} catch (IOException e) {
Log.error(e);
Result result = Result.getFailed(ContractErrorCode.CONTRACT_TX_CREATE_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_TX_CREATE_ERROR);
result.setMsg(e.getMessage());
return result;
}
}
Aggregations