use of io.nuls.kernel.exception.NulsException in project nuls by nuls-io.
the class AccountLedgerServiceImpl method transferP2SH.
/**
* A transfers NULS to B 多签交易
*
* @param fromAddr address of A
* @param signAddr address of B
* @param values NULS amount
* @param password password of A
* @param remark remarks of transaction
* @param price Unit price of fee
* @param pubkeys 公钥列表
* @param m 至少需要签名验证通过
* @return Result
*/
@Override
public Result transferP2SH(byte[] fromAddr, byte[] signAddr, List<MultipleAddressTransferModel> outputs, Na values, String password, String remark, Na price, List<String> pubkeys, int m, String txdata) {
try {
Result<Account> accountResult = accountService.getAccount(signAddr);
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);
}
TransferTransaction tx = new TransferTransaction();
TransactionSignature transactionSignature = new TransactionSignature();
List<P2PHKSignature> p2PHKSignatures = new ArrayList<>();
List<Script> scripts = new ArrayList<>();
// 如果txdata为空则表示当前请求为多签发起者调用,需要创建交易
if (txdata == null || txdata.trim().length() == 0) {
if (StringUtils.isNotBlank(remark)) {
try {
tx.setRemark(remark.getBytes(NulsConfig.DEFAULT_ENCODING));
} catch (UnsupportedEncodingException e) {
Log.error(e);
}
}
Script redeemScript = ScriptBuilder.createNulsRedeemScript(m, pubkeys);
tx.setTime(TimeService.currentTimeMillis());
CoinData coinData = new CoinData();
for (MultipleAddressTransferModel to : outputs) {
// 如果为多签地址
Coin toCoin = null;
if (to.getAddress()[2] == NulsContext.P2SH_ADDRESS_TYPE) {
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;
}
// 交易签名的长度为m*单个签名长度+赎回脚本长度
int scriptSignLenth = redeemScript.getProgram().length + m * 72;
CoinDataResult coinDataResult = getMutilCoinData(fromAddr, values, tx.size() + coinData.size() + scriptSignLenth, price);
if (!coinDataResult.isEnough()) {
return Result.getFailed(AccountLedgerErrorCode.INSUFFICIENT_BALANCE);
}
coinData.setFrom(coinDataResult.getCoinList());
if (coinDataResult.getChange() != null) {
coinData.getTo().add(coinDataResult.getChange());
}
tx.setCoinData(coinData);
tx.setHash(NulsDigestData.calcDigestData(tx.serializeForHash()));
// 将赎回脚本先存储在签名脚本中
scripts.add(redeemScript);
transactionSignature.setScripts(scripts);
} else // 如果txdata不为空表示多签交易已经创建好了,将交易反序列化出来
{
byte[] txByte = Hex.decode(txdata);
tx.parse(new NulsByteBuffer(txByte));
transactionSignature.parse(new NulsByteBuffer(tx.getTransactionSignature()));
p2PHKSignatures = transactionSignature.getP2PHKSignatures();
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按规则排序
Collections.sort(p2PHKSignatures, 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()) {
if (KernelErrorCode.DATA_SIZE_ERROR.getCode().equals(saveResult.getErrorCode().getCode())) {
// 重新算一次交易(不超出最大交易数据大小下)的最大金额
Result rs = getMaxAmountOfOnce(fromAddr, 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;
}
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());
}
}
use of io.nuls.kernel.exception.NulsException in project nuls by nuls-io.
the class AccountLedgerServiceImpl method createP2shTransfer.
/**
* A transfers NULS to B 多签交易
*
* @param fromAddr 输入地址
* @param signAddr 签名地址
* @param outputs 输出地址
* @param password password of A
* @param remark remarks of transaction
* @return Result
*/
@Override
public Result createP2shTransfer(String fromAddr, String signAddr, List<MultipleAddressTransferModel> outputs, String password, String remark) {
try {
Result<Account> accountResult = accountService.getAccount(signAddr);
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);
}
}
TransferTransaction tx = new TransferTransaction();
TransactionSignature transactionSignature = new TransactionSignature();
List<Script> scripts = new ArrayList<>();
Result<MultiSigAccount> result = accountService.getMultiSigAccount(fromAddr);
MultiSigAccount multiSigAccount = result.getData();
// 验证签名账户是否属于多签账户,如果不是多签账户下的地址则提示错误
if (!AddressTool.validSignAddress(multiSigAccount.getPubKeyList(), account.getPubKey())) {
return Result.getFailed(AccountErrorCode.SIGN_ADDRESS_NOT_MATCH);
}
Script redeemScript = getRedeemScript(multiSigAccount);
if (redeemScript == null) {
return Result.getFailed(AccountErrorCode.ACCOUNT_NOT_EXIST);
}
tx.setTime(TimeService.currentTimeMillis());
if (StringUtils.isNotBlank(remark)) {
try {
tx.setRemark(remark.getBytes(NulsConfig.DEFAULT_ENCODING));
} catch (UnsupportedEncodingException e) {
Log.error(e);
}
}
CoinData coinData = new CoinData();
Na values = Na.ZERO;
for (MultipleAddressTransferModel to : outputs) {
// 如果为多签地址
Coin toCoin = null;
values = values.add(Na.valueOf(to.getAmount()));
if (to.getAddress()[2] == NulsContext.P2SH_ADDRESS_TYPE) {
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);
}
// 交易签名的长度为m*单个签名长度+赎回脚本长度
int scriptSignLenth = redeemScript.getProgram().length + ((int) multiSigAccount.getM()) * 72;
CoinDataResult coinDataResult = getMutilCoinData(AddressTool.getAddress(fromAddr), values, tx.size() + coinData.size() + scriptSignLenth, TransactionFeeCalculator.MIN_PRICE_PRE_1024_BYTES);
if (!coinDataResult.isEnough()) {
return Result.getFailed(AccountLedgerErrorCode.INSUFFICIENT_BALANCE);
}
coinData.setFrom(coinDataResult.getCoinList());
if (coinDataResult.getChange() != null) {
coinData.getTo().add(coinDataResult.getChange());
}
tx.setCoinData(coinData);
tx.setHash(NulsDigestData.calcDigestData(tx.serializeForHash()));
// 将赎回脚本先存储在签名脚本中
scripts.add(redeemScript);
transactionSignature.setScripts(scripts);
return txMultiProcess(tx, transactionSignature, account, password);
} catch (IOException e) {
Log.error(e);
return Result.getFailed(KernelErrorCode.IO_ERROR);
} catch (NulsException e) {
Log.error(e);
return Result.getFailed(e.getErrorCode());
} catch (Exception e) {
Log.error(e);
return Result.getFailed(AccountErrorCode.ACCOUNT_NOT_EXIST);
}
}
use of io.nuls.kernel.exception.NulsException in project nuls by nuls-io.
the class AccountLedgerResource method getTxInfoList.
@GET
@Path("/tx/list/{address}")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "账户地址查询交易列表", notes = "result.data: balanceJson 返回账户相关的交易列表")
@ApiResponses(value = { @ApiResponse(code = 200, message = "success", response = Page.class) })
public RpcClientResult getTxInfoList(@ApiParam(name = "address", value = "账户地址", required = true) @PathParam("address") String address, @ApiParam(name = "assetType", value = "资产类型") @QueryParam("assetType") String assetType, @ApiParam(name = "type", value = "类型") @QueryParam("type") Integer type, @ApiParam(name = "pageNumber", value = "页码") @QueryParam("pageNumber") Integer pageNumber, @ApiParam(name = "pageSize", value = "每页条数") @QueryParam("pageSize") Integer pageSize) {
if (null == pageNumber || pageNumber == 0) {
pageNumber = 1;
}
if (null == pageSize || pageSize == 0) {
pageSize = 10;
}
if (pageNumber < 0 || pageSize < 0 || pageSize > 100) {
return Result.getFailed(KernelErrorCode.PARAMETER_ERROR).toRpcClientResult();
}
if (type == null || type <= 0) {
type = -1;
}
byte[] addressBytes = null;
Result dtoResult = Result.getSuccess();
try {
addressBytes = AddressTool.getAddress(address.trim());
} catch (Exception e) {
return Result.getFailed(AccountLedgerErrorCode.PARAMETER_ERROR).toRpcClientResult();
}
List<TransactionInfo> result = new ArrayList<TransactionInfo>();
boolean isEmptyAssetType = StringUtils.isBlank(assetType);
boolean isNeedQueryToken = StringUtils.isBlank(assetType) || AddressTool.validAddress(assetType);
boolean isNeedQueryNuls = StringUtils.isBlank(assetType) || "NULS".equals(assetType);
Set<String> hashCheckSet = MapUtil.createHashSet(8);
// 合约token代币转账交易记录
if (isNeedQueryToken) {
Result<List<ContractTokenTransferInfoPo>> listResult = contractService.getTokenTransferInfoList(address);
List<ContractTokenTransferInfoPo> list = listResult.getData();
if (list != null && list.size() > 0) {
List<ContractTokenTransferInfoPo> tokenInfoList = null;
if (!isEmptyAssetType) {
String contractAddress = assetType;
if (AddressTool.validAddress(contractAddress)) {
tokenInfoList = list.stream().filter(po -> contractAddress.equals(po.getContractAddress())).collect(Collectors.toList());
}
} else {
tokenInfoList = list;
}
TransactionInfo info = null;
if (null != tokenInfoList) {
for (ContractTokenTransferInfoPo po : tokenInfoList) {
info = new TransactionInfo();
// 临时type,用于区分资产类型 - 基础币和代币
info.setTxType(1000);
NulsDigestData hashData = new NulsDigestData();
try {
hashData.parse(po.getTxHash(), 0);
} catch (NulsException e) {
Log.error(e);
// skip it
}
info.setTxHash(hashData);
info.setContractAddress(AddressTool.getAddress(po.getContractAddress()));
info.setTime(po.getTime());
info.setBlockHeight(po.getBlockHeight());
info.setStatus(po.getStatus());
info.setInfo(po.getInfo(addressBytes));
info.setSymbol(po.getSymbol());
result.add(info);
hashCheckSet.add(hashData.getDigestHex());
}
}
}
}
// 基础币交易记录
if (isNeedQueryNuls) {
Result<List<TransactionInfo>> rawResult = accountLedgerService.getTxInfoList(addressBytes);
if (rawResult.isFailed()) {
dtoResult.setSuccess(false);
dtoResult.setErrorCode(rawResult.getErrorCode());
return dtoResult.toRpcClientResult();
}
List<TransactionInfo> infoList = rawResult.getData();
if (infoList != null && infoList.size() > 0) {
// 过滤掉既是调用合约的交易类型,又是代币转账类型的交易
List<TransactionInfo> baseList = infoList.stream().filter(info -> hashCheckSet.add(info.getTxHash().getDigestHex())).collect(Collectors.toList());
if (type == -1) {
result.addAll(baseList);
} else {
for (TransactionInfo txInfo : baseList) {
if (txInfo.getTxType() == type) {
result.add(txInfo);
}
}
}
}
}
result.sort(new Comparator<TransactionInfo>() {
@Override
public int compare(TransactionInfo o1, TransactionInfo o2) {
return o1.compareTo(o2.getTime());
}
});
Page<TransactionInfoDto> page = new Page<>(pageNumber, pageSize, result.size());
int start = pageNumber * pageSize - pageSize;
if (start >= page.getTotal()) {
dtoResult.setData(page);
return dtoResult.toRpcClientResult();
}
int end = start + pageSize;
if (end > page.getTotal()) {
end = (int) page.getTotal();
}
List<TransactionInfoDto> infoDtoList = new ArrayList<>();
for (int i = start; i < end; i++) {
TransactionInfo info = result.get(i);
Transaction tx = ledgerService.getTx(info.getTxHash());
if (tx == null) {
tx = accountLedgerService.getUnconfirmedTransaction(info.getTxHash()).getData();
}
if (tx == null) {
continue;
}
info.setBlockHeight(tx.getBlockHeight());
// 非合约Token转账
if (info.getTxType() != 1000) {
info.setInfo(tx.getInfo(addressBytes));
}
infoDtoList.add(new TransactionInfoDto(info));
}
page.setList(infoDtoList);
dtoResult.setSuccess(true);
dtoResult.setData(page);
return dtoResult.toRpcClientResult();
}
use of io.nuls.kernel.exception.NulsException in project nuls by nuls-io.
the class AccountLedgerServiceImpl method getChangeWholeTxInfoList.
private TransferTransaction getChangeWholeTxInfoList(byte[] address, Account account, String password, Na price) {
// 这里要重新获取可用余额
List<Coin> coinList = balanceManager.getCoinListByAddress(address);
TransferTransaction tx = new TransferTransaction();
changeWholeLock.lock();
try {
tx.setTime(TimeService.currentTimeMillis());
// 默认coindata中to暂定38字节(一条tocoin)
int size = tx.size() + 38;
// 计算目标size,coindata中from的总大小
int targetSize = TxMaxSizeValidator.MAX_TX_SIZE - size;
if (coinList.isEmpty()) {
return null;
}
// 从大到小
Collections.sort(coinList, CoinComparatorDesc.getInstance());
Na max = Na.ZERO;
List<Coin> coins = new ArrayList<>();
byte signType = 0;
for (int i = 0; i < coinList.size(); i++) {
Coin coin = coinList.get(i);
if (!coin.usable()) {
continue;
}
if (coin.getNa().equals(Na.ZERO)) {
continue;
}
size += coin.size();
if (i == 127) {
size += 1;
}
if (size > targetSize) {
break;
}
coins.add(coin);
/**
* 判断是否是脚本验证UTXO
*/
if (signType != 3) {
if ((signType & 0x01) == 0 && coin.getTempOwner().length == 23) {
signType = (byte) (signType | 0x01);
size += P2PHKSignature.SERIALIZE_LENGTH;
} else if ((signType & 0x02) == 0 && coin.getTempOwner().length != 23) {
signType = (byte) (signType | 0x02);
size += P2PHKSignature.SERIALIZE_LENGTH;
}
}
max = max.add(coin.getNa());
}
Na fee = TransactionFeeCalculator.getFee(size, price);
max = max.subtract(fee);
CoinData coinData = new CoinData();
Coin toCoin = new Coin(address, max);
coinData.getTo().add(toCoin);
coinData.setFrom(coins);
tx.setCoinData(coinData);
// 一定要交易组装完才能setHash
tx.setHash(NulsDigestData.calcDigestData(tx.serializeForHash()));
// 生成签名
List<ECKey> signEckeys = new ArrayList<>();
List<ECKey> scriptEckeys = new ArrayList<>();
ECKey eckey = account.getEcKey(password);
// 如果最后一位为1则表示该交易包含普通签名
if ((signType & 0x01) == 0x01) {
signEckeys.add(eckey);
}
// 如果倒数第二位位为1则表示该交易包含脚本签名
if ((signType & 0x02) == 0x02) {
scriptEckeys.add(eckey);
}
SignatureUtil.createTransactionSignture(tx, scriptEckeys, signEckeys);
return tx;
} catch (RuntimeException | IOException | NulsException e) {
Log.error(e);
return null;
} finally {
changeWholeLock.unlock();
}
}
use of io.nuls.kernel.exception.NulsException in project nuls by nuls-io.
the class AccountLedgerServiceImpl method dapp.
@Override
public Result dapp(byte[] from, String password, byte[] data, byte[] remark) {
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);
}
}
DataTransaction tx = new DataTransaction();
tx.setRemark(remark);
tx.setTime(TimeService.currentTimeMillis());
LogicData logicData = new LogicData(data);
tx.setTxData(logicData);
CoinData coinData = new CoinData();
try {
CoinDataResult coinDataResult = getCoinData(from, Na.ZERO, tx.size() + coinData.size(), TransactionFeeCalculator.MIN_PRICE_PRE_1024_BYTES);
if (!coinDataResult.isEnough()) {
return Result.getFailed(AccountLedgerErrorCode.INSUFFICIENT_BALANCE);
}
coinData.setFrom(coinDataResult.getCoinList());
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 = verifyAndSaveUnconfirmedTransaction(tx);
if (saveResult.isFailed()) {
if (KernelErrorCode.DATA_SIZE_ERROR.getCode().equals(saveResult.getErrorCode().getCode())) {
// 重新算一次交易(不超出最大交易数据大小下)的最大金额
Result rs = getMaxAmountOfOnce(from, 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()) {
this.deleteTransaction(tx);
return sendResult;
}
return Result.getSuccess().setData(tx.getHash().getDigestHex());
} catch (NulsException e) {
Log.error(e);
return Result.getFailed(e.getErrorCode());
} catch (IOException e) {
Log.error(e);
return Result.getFailed(KernelErrorCode.IO_ERROR);
}
}
Aggregations