use of io.nuls.kernel.script.Script 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.kernel.script.Script in project nuls by nuls-io.
the class AccountLedgerResource method createMultipleTx.
@POST
@Path("/createMultipleTx")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "创建多账户转账交易", notes = "result.data: resultJson 返回交易Hex")
@ApiResponses(value = { @ApiResponse(code = 200, message = "success") })
public RpcClientResult createMultipleTx(@ApiParam(name = "form", value = "交易输入输出", required = true) TransactionForm form) {
if (form.getInputs() == null || form.getInputs().isEmpty()) {
return RpcClientResult.getFailed("inputs error");
}
if (form.getOutputs() == null || form.getOutputs().isEmpty()) {
return RpcClientResult.getFailed("outputs error");
}
byte[] remark = null;
if (!StringUtils.isBlank(form.getRemark())) {
try {
remark = form.getRemark().getBytes(NulsConfig.DEFAULT_ENCODING);
} catch (UnsupportedEncodingException e) {
return RpcClientResult.getFailed("remark error");
}
}
List<Coin> outputList = new ArrayList<>();
for (int i = 0; i < form.getOutputs().size(); i++) {
OutputDto outputDto = form.getOutputs().get(i);
Coin to = new Coin();
try {
if (!AddressTool.validAddress(outputDto.getAddress())) {
return Result.getFailed(AccountErrorCode.ADDRESS_ERROR).toRpcClientResult();
}
byte[] owner = AddressTool.getAddress(outputDto.getAddress());
if (owner[2] == 3) {
Script scriptPubkey = SignatureUtil.createOutputScript(to.getAddress());
to.setOwner(scriptPubkey.getProgram());
} else {
to.setOwner(AddressTool.getAddress(outputDto.getAddress()));
}
} catch (Exception e) {
return Result.getFailed(AccountErrorCode.ADDRESS_ERROR).toRpcClientResult();
}
try {
to.setNa(Na.valueOf(outputDto.getValue()));
} catch (Exception e) {
return Result.getFailed(AccountErrorCode.DATA_PARSE_ERROR).toRpcClientResult();
}
if (outputDto.getLockTime() < 0) {
return RpcClientResult.getFailed("lockTime error");
}
to.setLockTime(outputDto.getLockTime());
outputList.add(to);
}
List<Coin> inputsList = new ArrayList<>();
Set<String> addressSet = new HashSet<>();
for (int i = 0; i < form.getInputs().size(); i++) {
InputDto inputDto = form.getInputs().get(i);
if (inputDto.getAddress() == null) {
return Result.getFailed(AccountErrorCode.ADDRESS_ERROR).toRpcClientResult();
}
addressSet.add(inputDto.getAddress());
byte[] key = Arrays.concatenate(Hex.decode(inputDto.getFromHash()), new VarInt(inputDto.getFromIndex()).encode());
Coin coin = new Coin();
coin.setOwner(key);
coin.setNa(Na.valueOf(inputDto.getValue()));
coin.setLockTime(inputDto.getLockTime());
inputsList.add(coin);
}
TransferTransaction tx = new TransferTransaction();
CoinData coinData = new CoinData();
coinData.setFrom(inputsList);
coinData.setTo(outputList);
tx.setCoinData(coinData);
tx.setTime(TimeService.currentTimeMillis());
tx.setRemark(remark);
if (!isFeeEnough(tx, P2PHKSignature.SERIALIZE_LENGTH * addressSet.size())) {
return Result.getFailed(TransactionErrorCode.FEE_NOT_RIGHT).toRpcClientResult();
}
try {
String txHex = Hex.encode(tx.serialize());
Map<String, String> map = new HashMap<>();
map.put("value", txHex);
return Result.getSuccess().setData(map).toRpcClientResult();
} catch (IOException e) {
Log.error(e);
return RpcClientResult.getFailed(e.getMessage());
}
}
use of io.nuls.kernel.script.Script in project nuls by nuls-io.
the class UtxoLedgerServiceImpl method verifyCoinData.
/**
* 此txList是待打包的块中的交易,所以toList是下一步的UTXO,应该校验它
* coinData的交易和txList同处一个块中,txList中的to可能是coinData的from,
* 也就是可能存在,在同一个块中,下一笔输入就是上一笔的输出,所以需要校验它
* bestHeight is used when switch chain.
*
* @return ValidateResult
*/
@Override
public ValidateResult verifyCoinData(Transaction transaction, Map<String, Coin> temporaryToMap, Set<String> temporaryFromSet, Long bestHeight) {
if (transaction == null || transaction.getCoinData() == null) {
return ValidateResult.getFailedResult(CLASS_NAME, LedgerErrorCode.NULL_PARAMETER);
}
try {
/*
校验开始
*/
CoinData coinData = transaction.getCoinData();
List<Coin> froms = coinData.getFrom();
int fromSize = froms.size();
TransactionSignature transactionSignature = new TransactionSignature();
// 交易签名反序列化
if (transaction.needVerifySignature() && fromSize > 0) {
try {
transactionSignature.parse(transaction.getTransactionSignature(), 0);
} catch (NulsException e) {
return ValidateResult.getFailedResult(CLASS_NAME, LedgerErrorCode.LEDGER_P2PKH_SCRIPT_ERROR);
}
}
// 保存Set用于验证自身双花
if (temporaryFromSet == null) {
temporaryFromSet = new HashSet<>();
}
Na fromTotal = Na.ZERO;
byte[] fromBytes;
// 保存在数据库中或者txList中的utxo数据
Coin fromOfFromCoin = null;
byte[] fromAddressBytes = null;
/**
* 存放真实地址(如果为脚本验证的情况fromAddressBytes存的是脚本信息)
*/
byte[] realAddressBytes = null;
for (int i = 0; i < froms.size(); i++) {
Coin from = froms.get(i);
fromBytes = from.getOwner();
// 验证是否可花费, 校验的coinData的fromUTXO,检查数据库中是否存在此UTXO
fromOfFromCoin = utxoLedgerUtxoStorageService.getUtxo(fromBytes);
// 检查txList中是否存在此UTXO
if (temporaryToMap != null && fromOfFromCoin == null) {
fromOfFromCoin = temporaryToMap.get(asString(fromBytes));
}
if (null == fromOfFromCoin) {
// 如果既不存在于txList的to中(如果txList不为空),又不存在于数据库中,那么这是一笔问题数据,进一步检查是否存在这笔交易,交易有就是双花,没有就是孤儿交易,则返回失败
if (null != utxoLedgerTransactionStorageService.getTxBytes(LedgerUtil.getTxHashBytes(fromBytes))) {
return ValidateResult.getFailedResult(CLASS_NAME, TransactionErrorCode.TRANSACTION_REPEATED);
} else {
return ValidateResult.getFailedResult(CLASS_NAME, LedgerErrorCode.ORPHAN_TX);
}
} else {
fromAddressBytes = fromOfFromCoin.getOwner();
realAddressBytes = fromOfFromCoin.getAddress();
// pierre add 非合约转账(从合约转出)交易,验证fromAdress是否是合约地址,如果是,则返回失败,非合约转账(从合约转出)交易不能转出合约地址资产
if (transaction.getType() != ContractConstant.TX_TYPE_CONTRACT_TRANSFER) {
boolean isContractAddress = contractService.isContractAddress(realAddressBytes);
if (isContractAddress) {
return ValidateResult.getFailedResult(CLASS_NAME, LedgerErrorCode.DATA_ERROR);
}
}
// 验证地址中的公钥hash160和交易中的公钥hash160是否相等,不相等则说明这笔utxo不属于交易发出者
boolean signtureValidFlag = false;
if (transaction.needVerifySignature()) {
if (transactionSignature != null) {
if (fromAddressBytes != null && transactionSignature.getScripts() != null && transactionSignature.getScripts().size() > 0) {
if (fromAddressBytes.length != Address.ADDRESS_LENGTH) {
Script scriptPubkey = new Script(fromAddressBytes);
for (Script scriptSig : transactionSignature.getScripts()) {
signtureValidFlag = scriptSig.correctlyNulsSpends(transaction, 0, scriptPubkey);
if (signtureValidFlag) {
break;
}
}
} else {
for (Script scriptSig : transactionSignature.getScripts()) {
Script redeemScript = new Script(scriptSig.getChunks().get(scriptSig.getChunks().size() - 1).data);
Address address = new Address(NulsContext.getInstance().getDefaultChainId(), NulsContext.P2SH_ADDRESS_TYPE, SerializeUtils.sha256hash160(redeemScript.getProgram()));
Script publicScript = SignatureUtil.createOutputScript(address.getAddressBytes());
signtureValidFlag = scriptSig.correctlyNulsSpends(transaction, 0, publicScript);
if (signtureValidFlag) {
break;
}
}
}
} else {
if (transactionSignature.getP2PHKSignatures() != null && transactionSignature.getP2PHKSignatures().size() != 0) {
for (P2PHKSignature signature : transactionSignature.getP2PHKSignatures()) {
signtureValidFlag = AddressTool.checkPublicKeyHash(realAddressBytes, signature.getSignerHash160());
if (signtureValidFlag) {
break;
}
}
}
}
}
if (!signtureValidFlag) {
Log.warn("public key hash160 check error.");
return ValidateResult.getFailedResult(CLASS_NAME, LedgerErrorCode.INVALID_INPUT);
}
}
if (java.util.Arrays.equals(realAddressBytes, NulsConstant.BLACK_HOLE_ADDRESS) || java.util.Arrays.equals(realAddressBytes, NulsConstant.BLACK_HOLE_ADDRESS_TEST_NET)) {
return ValidateResult.getFailedResult(CLASS_NAME, KernelErrorCode.ADDRESS_IS_BLOCK_HOLE);
}
if (NulsContext.getInstance().getDefaultChainId() != SerializeUtils.bytes2Short(realAddressBytes)) {
return ValidateResult.getFailedResult(CLASS_NAME, KernelErrorCode.ADDRESS_IS_NOT_BELONGS_TO_CHAIN);
}
}
// 验证非解锁类型的交易及解锁类型的交易
if (!transaction.isUnlockTx()) {
// 验证非解锁类型的交易,验证是否可用,检查是否还在锁定时间内
if (bestHeight == null) {
if (!fromOfFromCoin.usable()) {
return ValidateResult.getFailedResult(CLASS_NAME, LedgerErrorCode.UTXO_UNUSABLE);
}
} else {
if (!fromOfFromCoin.usable(bestHeight)) {
return ValidateResult.getFailedResult(CLASS_NAME, LedgerErrorCode.UTXO_UNUSABLE);
}
}
} else {
// 验证解锁类型的交易
if (fromOfFromCoin.getLockTime() != -1) {
return ValidateResult.getFailedResult(CLASS_NAME, LedgerErrorCode.UTXO_STATUS_CHANGE);
}
}
// 验证与待确认交易列表中是否有双花,既是待校验交易的fromUtxo是否和txList中的fromUtxo重复,有重复则是双花
if (temporaryFromSet != null && !temporaryFromSet.add(asString(fromBytes))) {
if (i > 0) {
for (int x = 0; x < i; x++) {
Coin theFrom = froms.get(i);
temporaryFromSet.remove(asString(theFrom.getOwner()));
}
}
return ValidateResult.getFailedResult(CLASS_NAME, TransactionErrorCode.TRANSACTION_REPEATED);
}
// 验证from的锁定时间和金额
if (!(fromOfFromCoin.getNa().equals(from.getNa()) && fromOfFromCoin.getLockTime() == from.getLockTime())) {
return ValidateResult.getFailedResult(CLASS_NAME, LedgerErrorCode.DATA_ERROR);
}
fromTotal = fromTotal.add(fromOfFromCoin.getNa());
from.setFrom(fromOfFromCoin);
}
List<Coin> tos = coinData.getTo();
Na toTotal = Na.ZERO;
byte[] txBytes = transaction.getHash().serialize();
for (int i = 0; i < tos.size(); i++) {
Coin to = tos.get(i);
// 如果不是调用合约的类型,并且合约地址作为nuls接收者,则返回错误,非合约交易不能转入nuls(CoinBase交易不过此验证)
if (ContractConstant.TX_TYPE_CALL_CONTRACT != transaction.getType() && AddressTool.validContractAddress(to.getOwner())) {
Log.error("Ledger verify error: {}.", ContractErrorCode.NON_CONTRACTUAL_TRANSACTION_NO_TRANSFER.getEnMsg());
return ValidateResult.getFailedResult(CLASS_NAME, ContractErrorCode.NON_CONTRACTUAL_TRANSACTION_NO_TRANSFER);
}
toTotal = toTotal.add(to.getNa());
if (temporaryToMap != null) {
temporaryToMap.put(asString(ArraysTool.concatenate(txBytes, new VarInt(i).encode())), to);
}
}
// 验证输出不能大于输入
if (fromTotal.compareTo(toTotal) < 0) {
return ValidateResult.getFailedResult(CLASS_NAME, LedgerErrorCode.INVALID_AMOUNT);
}
} catch (Exception e) {
Log.error(e);
return ValidateResult.getFailedResult(CLASS_NAME, KernelErrorCode.SYS_UNKOWN_EXCEPTION);
}
return ValidateResult.getSuccessResult();
}
use of io.nuls.kernel.script.Script in project nuls by nuls-io.
the class AccountServiceImpl method saveMultiSigAccount.
/**
* 导入一个跟本地地址相关的多签账户
*
* @param addressStr 多签地址
* @param pubkeys 多签组成公钥列表
* @param m 最小签名数
* @return 是否成功
*/
@Override
public Result<Boolean> saveMultiSigAccount(String addressStr, List<String> pubkeys, int m) {
Script redeemScript = ScriptBuilder.createNulsRedeemScript(m, pubkeys);
Address address = new Address(NulsContext.getInstance().getDefaultChainId(), NulsContext.P2SH_ADDRESS_TYPE, SerializeUtils.sha256hash160(redeemScript.getProgram()));
if (!AddressTool.getStringAddressByBytes(address.getAddressBytes()).equals(addressStr)) {
return Result.getFailed(AccountErrorCode.ADDRESS_ERROR);
}
MultiSigAccount account = new MultiSigAccount();
account.setAddress(address);
account.setM(m);
account.addPubkeys(pubkeys);
Result result = null;
try {
result = this.multiSigAccountStorageService.saveAccount(account.getAddress(), account.serialize());
} catch (IOException e) {
Log.error(e);
return Result.getFailed(KernelErrorCode.SERIALIZE_ERROR);
}
if (result.isFailed()) {
return result;
}
return result.setData(addressStr);
}
use of io.nuls.kernel.script.Script in project nuls by nuls-io.
the class AccountServiceImpl method createMultiAccount.
@Override
public Result<Address> createMultiAccount(List<String> pubkeys, int m) {
locker.lock();
try {
Script redeemScript = ScriptBuilder.createNulsRedeemScript(m, pubkeys);
Address address = new Address(NulsContext.getInstance().getDefaultChainId(), NulsContext.P2SH_ADDRESS_TYPE, SerializeUtils.sha256hash160(redeemScript.getProgram()));
MultiSigAccount account = new MultiSigAccount();
account.setAddress(address);
account.setM(m);
account.addPubkeys(pubkeys);
Result result = this.multiSigAccountStorageService.saveAccount(account.getAddress(), account.serialize());
if (result.isFailed()) {
return result;
}
return result.setData(account);
} catch (Exception e) {
Log.error(e);
throw new NulsRuntimeException(KernelErrorCode.FAILED);
} finally {
locker.unlock();
}
}
Aggregations