use of io.nuls.account.ledger.model.CoinDataResult in project nuls by nuls-io.
the class ContractSdkResource method createContractTx.
@POST
@Path("/sdk/create")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "create contact transaction")
@ApiResponses(value = { @ApiResponse(code = 200, message = "success") })
public RpcClientResult createContractTx(@ApiParam(name = "createForm", value = "create contact transaction", required = true) CreateContractTx createContractTx) {
if (!AddressTool.validAddress(createContractTx.getSender())) {
return Result.getFailed(AccountErrorCode.ADDRESS_ERROR).toRpcClientResult();
}
if (createContractTx.getGasLimit() < 0 || createContractTx.getPrice() < 0) {
return Result.getFailed(ContractErrorCode.PARAMETER_ERROR).toRpcClientResult();
}
if (createContractTx.getUtxos().isEmpty()) {
// utxo not null and size not zero.
return Result.getFailed(AccountErrorCode.DATA_PARSE_ERROR).toRpcClientResult();
}
Long gasLimit = createContractTx.getGasLimit();
Long price = createContractTx.getPrice();
Na value = Na.ZERO;
long totalGas = LongUtils.mul(gasLimit, price);
Na totalNa = Na.valueOf(totalGas);
byte[] senderBytes = AddressTool.getAddress(createContractTx.getSender());
/**
* first create a new contract address.
*/
Address contractAddress = null;
try {
contractAddress = AccountTool.createContractAddress();
} catch (NulsException e) {
Log.error(e);
return Result.getFailed(AccountErrorCode.ADDRESS_ERROR).toRpcClientResult();
}
byte[] contractAddressBytes = contractAddress.getAddressBytes();
// 组装txData
String contractCode = createContractTx.getContractCode();
byte[] contractCodeBytes = Hex.decode(contractCode);
CreateContractData txData = new CreateContractData();
txData.setSender(senderBytes);
txData.setContractAddress(contractAddressBytes);
txData.setValue(value.getValue());
txData.setGasLimit(gasLimit);
txData.setPrice(price);
txData.setCodeLen(contractCodeBytes.length);
txData.setCode(contractCodeBytes);
Object[] args = createContractTx.getArgs();
if (args != null) {
txData.setArgsCount((byte) args.length);
if (args.length > 0) {
txData.setArgs(ContractUtil.twoDimensionalArray(args));
}
}
/**
* create create contract tx data
*/
CreateContractTransaction tx = new CreateContractTransaction();
String remark = createContractTx.getRemark();
if (StringUtils.isNotBlank(remark)) {
try {
tx.setRemark(remark.getBytes(NulsConfig.DEFAULT_ENCODING));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
tx.setTime(TimeService.currentTimeMillis());
tx.setTxData(txData);
List<Coin> coinList = ConvertCoinTool.convertCoinList(createContractTx.getUtxos());
CoinDataResult coinDataResult = CoinDataTool.getCoinData(senderBytes, totalNa, tx.size(), TransactionFeeCalculator.MIN_PRICE_PRE_1024_BYTES, coinList);
if (!coinDataResult.isEnough()) {
return RpcClientResult.getFailed(TransactionErrorCode.INSUFFICIENT_BALANCE);
}
/**
* build coin data
*/
CoinData coinData = new CoinData();
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, AddressTool.getStringAddressByBytes(contractAddressBytes));
}
use of io.nuls.account.ledger.model.CoinDataResult 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.account.ledger.model.CoinDataResult in project nuls by nuls-io.
the class ContractSdkResource method deleteContractTx.
@POST
@Path("/sdk/delete")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "delete smart contract transaction")
@ApiResponses(value = { @ApiResponse(code = 200, message = "success") })
public RpcClientResult deleteContractTx(@ApiParam(name = "deleteForm", value = "delete smart contract transaction", required = true) DeleteContractTx deleteContractTx) {
if (!AddressTool.validAddress(deleteContractTx.getSender())) {
return Result.getFailed(AccountErrorCode.ADDRESS_ERROR).toRpcClientResult();
}
String contractAddress = deleteContractTx.getContractAddress();
if (!AddressTool.validAddress(contractAddress)) {
return Result.getFailed(AccountErrorCode.ADDRESS_ERROR).toRpcClientResult();
}
if (deleteContractTx.getUtxos().isEmpty()) {
// utxo not null and size not zero.
return Result.getFailed(AccountErrorCode.DATA_PARSE_ERROR).toRpcClientResult();
}
DeleteContractTransaction tx = new DeleteContractTransaction();
if (StringUtils.isNotBlank(deleteContractTx.getRemark())) {
try {
tx.setRemark(deleteContractTx.getRemark().getBytes(NulsConfig.DEFAULT_ENCODING));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
tx.setTime(TimeService.currentTimeMillis());
byte[] senderBytes = AddressTool.getAddress(deleteContractTx.getSender());
byte[] contractAddressBytes = AddressTool.getAddress(deleteContractTx.getContractAddress());
/**
* build tx data
*/
DeleteContractData deleteContractData = new DeleteContractData();
deleteContractData.setContractAddress(contractAddressBytes);
deleteContractData.setSender(senderBytes);
tx.setTxData(deleteContractData);
List<Coin> coinList = ConvertCoinTool.convertCoinList(deleteContractTx.getUtxos());
CoinDataResult coinDataResult = CoinDataTool.getCoinData(senderBytes, Na.ZERO, tx.size(), TransactionFeeCalculator.MIN_PRICE_PRE_1024_BYTES, coinList);
if (!coinDataResult.isEnough()) {
return RpcClientResult.getFailed(TransactionErrorCode.INSUFFICIENT_BALANCE);
}
/**
* build coin data
*/
CoinData coinData = new CoinData();
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.account.ledger.model.CoinDataResult in project nuls by nuls-io.
the class ContractServiceImpl method transfer.
/**
* @param from
* @param to
* @param values
* @param fee
* @param isSendBack
* @param orginHash
* @param blockTime
* @param toMaps
* @param contractUsedCoinMap 组装时不操作toMaps, 用 contractUsedCoinMap 来检查UTXO是否已经被使用,如果已使用则跳过
* (合约转账(从合约转出)不同于普通转账,普通转账有本地账本来维护UTXO,并且在打包前已经组装好UTXO,
* 而合约转账(从合约转出)是在打包时才组装,此时从合约账本[DB]及打包交易[toMaps]中拿到所有的utxo来组装,
* 用 contractUsedCoinMap 来代表已使用的UTXO)
* @param bestHeight
* @return
*/
private Result<ContractTransferTransaction> transfer(byte[] from, byte[] to, Na values, Na fee, boolean isSendBack, NulsDigestData orginHash, long blockTime, Map<String, Coin> toMaps, Map<String, Coin> contractUsedCoinMap, Long bestHeight) {
try {
if (!ContractLedgerUtil.isExistContractAddress(from)) {
return Result.getFailed(ContractErrorCode.CONTRACT_ADDRESS_NOT_EXIST);
}
ContractTransferTransaction tx = new ContractTransferTransaction();
tx.setTime(blockTime);
CoinData coinData = new CoinData();
List<Coin> tos = coinData.getTo();
Coin toCoin = new Coin(to, values.subtract(fee));
tos.add(toCoin);
// 加入toMaps、contractUsedCoinMap组装UTXO
// 组装时不操作toMaps, 用 contractUsedCoinMap 来检查UTXO是否已经被使用,如果已使用则跳过
CoinDataResult coinDataResult = getContractSpecialTransferCoinData(from, values, toMaps, contractUsedCoinMap, bestHeight);
if (!coinDataResult.isEnough()) {
return Result.getFailed(TransactionErrorCode.INSUFFICIENT_BALANCE);
}
coinData.setFrom(coinDataResult.getCoinList());
if (coinDataResult.getChange() != null) {
tos.add(coinDataResult.getChange());
}
tx.setCoinData(coinData);
byte successByte;
byte[] remark = null;
if (isSendBack) {
successByte = 0;
remark = ContractConstant.SEND_BACK_REMARK.getBytes(NulsConfig.DEFAULT_ENCODING);
} else {
successByte = 1;
}
tx.setRemark(remark);
tx.setTxData(new ContractTransferData(orginHash, from, successByte));
tx.setHash(NulsDigestData.calcDigestData(tx.serializeForHash()));
// 维护合约地址连续转出交易的未花费UTXO
byte[] txBytes = tx.getHash().serialize();
for (int i = 0, size = tos.size(); i < size; i++) {
if (toMaps != null) {
toMaps.put(LedgerUtil.asString(ArraysTool.concatenate(txBytes, new VarInt(i).encode())), tos.get(i));
}
}
// 合约转账(从合约转出)交易不需要签名
return Result.getSuccess().setData(tx);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
use of io.nuls.account.ledger.model.CoinDataResult in project nuls by nuls-io.
the class ContractServiceImpl method getContractSpecialTransferCoinData.
/**
* @param address
* @param amount
* @param bestHeight
* @return
* @throws NulsException
*/
public CoinDataResult getContractSpecialTransferCoinData(byte[] address, Na amount, Map<String, Coin> toMaps, Map<String, Coin> contractUsedCoinMap, Long bestHeight) {
lock.lock();
try {
CoinDataResult coinDataResult = new CoinDataResult();
List<Coin> coinList = contractBalanceManager.getCoinListByAddress(address);
// pierre add contract coin key
Set<Map.Entry<String, Coin>> toMapsEntries = toMaps.entrySet();
Coin toCoin;
Coin cloneCoin;
String key;
for (Map.Entry<String, Coin> toMapsEntry : toMapsEntries) {
key = toMapsEntry.getKey();
toCoin = toMapsEntry.getValue();
if (Arrays.equals(toCoin.getAddress(), address) && !contractUsedCoinMap.containsKey(key)) {
cloneCoin = new Coin(asBytes(key), toCoin.getNa(), toCoin.getLockTime());
cloneCoin.setFrom(toCoin);
cloneCoin.setKey(key);
coinList.add(cloneCoin);
}
}
if (coinList.isEmpty()) {
coinDataResult.setEnough(false);
return coinDataResult;
}
// 将所有余额从小到大排序
Collections.sort(coinList, ContractCoinComparator.getInstance());
boolean enough = false;
List<Coin> coins = new ArrayList<>();
Na values = Na.ZERO;
Coin coin;
// 将所有余额从小到大排序后,累计未花费的余额
for (int i = 0, length = coinList.size(); i < length; i++) {
coin = coinList.get(i);
if (bestHeight != null) {
if (!coin.usable(bestHeight)) {
continue;
}
} else {
if (!coin.usable()) {
continue;
}
}
if (coin.getNa().equals(Na.ZERO)) {
continue;
}
// for contract, 使用过的UTXO不能再使用
if (StringUtils.isNotBlank(coin.getKey())) {
if (contractUsedCoinMap.containsKey(coin.getKey())) {
continue;
}
contractUsedCoinMap.put(coin.getKey(), coin);
}
coins.add(coin);
// 每次累加一条未花费余额
values = values.add(coin.getNa());
if (values.isGreaterOrEquals(amount)) {
// 余额足够后,需要判断是否找零
Na change = values.subtract(amount);
if (change.isGreaterThan(Na.ZERO)) {
Coin changeCoin = new Coin();
changeCoin.setOwner(address);
changeCoin.setNa(change);
coinDataResult.setChange(changeCoin);
}
enough = true;
coinDataResult.setEnough(true);
coinDataResult.setFee(Na.ZERO);
coinDataResult.setCoinList(coins);
break;
}
}
if (!enough) {
coinDataResult.setEnough(false);
return coinDataResult;
}
return coinDataResult;
} finally {
lock.unlock();
}
}
Aggregations