Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(withdraw): support withdrawing rewards to specified address #5729

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package org.tron.core.actuator;

import static org.tron.core.actuator.ActuatorConstant.ACCOUNT_EXCEPTION_STR;
import static org.tron.core.actuator.ActuatorConstant.NOT_EXIST_STR;
import static org.tron.core.config.Parameter.ChainConstant.FROZEN_PERIOD;

import com.google.common.math.LongMath;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import java.util.Arrays;
import java.util.Objects;
import lombok.extern.slf4j.Slf4j;
import org.tron.common.parameter.CommonParameter;
import org.tron.common.utils.DecodeUtil;
import org.tron.common.utils.StringUtil;
import org.tron.core.capsule.AccountCapsule;
import org.tron.core.capsule.TransactionResultCapsule;
import org.tron.core.exception.ContractExeException;
import org.tron.core.exception.ContractValidateException;
import org.tron.core.service.MortgageService;
import org.tron.core.store.AccountStore;
import org.tron.core.store.DynamicPropertiesStore;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.Protocol.Transaction.Result.code;
import org.tron.protos.contract.BalanceContract.WithdrawBalanceToContract;

@Slf4j(topic = "actuator")
public class WithdrawBalanceToActuator extends AbstractActuator {

public WithdrawBalanceToActuator() {
super(ContractType.WithdrawBalanceToContract, WithdrawBalanceToContract.class);
}

@Override
public boolean execute(Object result) throws ContractExeException {
TransactionResultCapsule ret = (TransactionResultCapsule) result;
if (Objects.isNull(ret)) {
throw new RuntimeException(ActuatorConstant.TX_RESULT_NULL);
}

long fee = calcFee();
final WithdrawBalanceToContract withdrawBalanceToContract;
AccountStore accountStore = chainBaseManager.getAccountStore();
DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
MortgageService mortgageService = chainBaseManager.getMortgageService();
try {
withdrawBalanceToContract = any.unpack(WithdrawBalanceToContract.class);
} catch (InvalidProtocolBufferException e) {
logger.debug(e.getMessage(), e);
ret.setStatus(fee, code.FAILED);
throw new ContractExeException(e.getMessage());
}

mortgageService.withdrawReward(withdrawBalanceToContract.getOwnerAddress()
.toByteArray());

AccountCapsule ownerCapsule = accountStore.
get(withdrawBalanceToContract.getOwnerAddress().toByteArray());

AccountCapsule receiverCapsule = accountStore.
get(withdrawBalanceToContract.getReceiverAddress().toByteArray());
long receiverBalance = receiverCapsule.getBalance();
long allowance = ownerCapsule.getAllowance();

long now = dynamicStore.getLatestBlockHeaderTimestamp();
ownerCapsule.setInstance(ownerCapsule.getInstance().toBuilder()
.setAllowance(0L)
.setLatestWithdrawTime(now)
.build());
receiverCapsule.setInstance(receiverCapsule.getInstance().toBuilder()
.setBalance(receiverBalance + allowance)
.setLatestWithdrawToTime(now)
.build());
accountStore.put(ownerCapsule.createDbKey(), ownerCapsule);
accountStore.put(receiverCapsule.createDbKey(), receiverCapsule);
ret.setWithdrawAmount(allowance);
ret.setStatus(fee, code.SUCESS);

return true;
}

@Override
public boolean validate() throws ContractValidateException {
if (this.any == null) {
throw new ContractValidateException(ActuatorConstant.CONTRACT_NOT_EXIST);
}
if (chainBaseManager == null) {
throw new ContractValidateException(ActuatorConstant.STORE_NOT_EXIST);
}
AccountStore accountStore = chainBaseManager.getAccountStore();
DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
MortgageService mortgageService = chainBaseManager.getMortgageService();
if (!this.any.is(WithdrawBalanceToContract.class)) {
throw new ContractValidateException(
"contract type error, expected type [WithdrawBalanceToContract], real type[" + any
.getClass() + "]");
}
final WithdrawBalanceToContract withdrawBalanceToContract;
try {
withdrawBalanceToContract = this.any.unpack(WithdrawBalanceToContract.class);
} catch (InvalidProtocolBufferException e) {
logger.debug(e.getMessage(), e);
throw new ContractValidateException(e.getMessage());
}
byte[] ownerAddress = withdrawBalanceToContract.getOwnerAddress().toByteArray();
if (!DecodeUtil.addressValid(ownerAddress)) {
throw new ContractValidateException("Invalid owner address");
}

AccountCapsule accountCapsule = accountStore.get(ownerAddress);
String readableOwnerAddress = StringUtil.createReadableString(ownerAddress);
if (accountCapsule == null) {
throw new ContractValidateException(
ACCOUNT_EXCEPTION_STR + readableOwnerAddress + NOT_EXIST_STR);
}

byte[] receiverAddress = withdrawBalanceToContract.getReceiverAddress().toByteArray();
if (!DecodeUtil.addressValid(receiverAddress)) {
lxcmyf marked this conversation as resolved.
Show resolved Hide resolved
throw new ContractValidateException("Invalid receiver address");
}

AccountCapsule receiverCapsule = accountStore.get(receiverAddress);
if (receiverCapsule == null) {
String readableReceiverAddress = StringUtil.createReadableString(receiverAddress);
throw new ContractValidateException(
ACCOUNT_EXCEPTION_STR + readableReceiverAddress + NOT_EXIST_STR);
}

boolean isGP = CommonParameter.getInstance()
.getGenesisBlock().getWitnesses().stream().anyMatch(witness ->
Arrays.equals(ownerAddress, witness.getAddress()));
if (isGP) {
throw new ContractValidateException(
ACCOUNT_EXCEPTION_STR + readableOwnerAddress
+ "] is a guard representative and is not allowed to withdraw Balance");
}

long latestWithdrawTime = accountCapsule.getLatestWithdrawTime();
long now = dynamicStore.getLatestBlockHeaderTimestamp();
long witnessAllowanceFrozenTime = dynamicStore.getWitnessAllowanceFrozenTime() * FROZEN_PERIOD;

if (now - latestWithdrawTime < witnessAllowanceFrozenTime) {
lxcmyf marked this conversation as resolved.
Show resolved Hide resolved
throw new ContractValidateException("The last withdraw time is "
+ latestWithdrawTime + ", less than 24 hours");
}

if (accountCapsule.getAllowance() <= 0 &&
mortgageService.queryReward(ownerAddress) <= 0) {
throw new ContractValidateException("witnessAccount does not have any reward");
}
try {
LongMath.checkedAdd(accountCapsule.getBalance(), accountCapsule.getAllowance());
} catch (ArithmeticException e) {
logger.debug(e.getMessage(), e);
throw new ContractValidateException(e.getMessage());
}

return true;
}

@Override
public ByteString getOwnerAddress() throws InvalidProtocolBufferException {
return any.unpack(WithdrawBalanceToContract.class).getOwnerAddress();
}

@Override
public long calcFee() {
return 0;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
import org.tron.protos.contract.BalanceContract.UnDelegateResourceContract;
import org.tron.protos.contract.BalanceContract.UnfreezeBalanceContract;
import org.tron.protos.contract.BalanceContract.WithdrawBalanceContract;
import org.tron.protos.contract.BalanceContract.WithdrawBalanceToContract;
import org.tron.protos.contract.BalanceContract.WithdrawExpireUnfreezeContract;
import org.tron.protos.contract.ExchangeContract.ExchangeCreateContract;
import org.tron.protos.contract.ExchangeContract.ExchangeInjectContract;
Expand Down Expand Up @@ -1443,6 +1444,12 @@ public void withdrawBalance2(WithdrawBalanceContract request,
createTransactionExtention(request, ContractType.WithdrawBalanceContract, responseObserver);
}

@Override
public void withdrawBalanceTo(WithdrawBalanceToContract request,
StreamObserver<TransactionExtention> responseObserver) {
createTransactionExtention(request, ContractType.WithdrawBalanceToContract, responseObserver);
}

@Override
public void withdrawExpireUnfreeze(WithdrawExpireUnfreezeContract request,
StreamObserver<TransactionExtention> responseObserver) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public class FullNodeHttpApiService extends HttpService {
@Autowired
private WithdrawBalanceServlet withdrawBalanceServlet;
@Autowired
private WithdrawBalanceToServlet withdrawBalanceToServlet;
@Autowired
private UpdateAssetServlet updateAssetServlet;
@Autowired
private ListNodesServlet listNodesServlet;
Expand Down Expand Up @@ -328,6 +330,7 @@ public void start() {
context.addServlet(new ServletHolder(unFreezeBalanceServlet), "/wallet/unfreezebalance");
context.addServlet(new ServletHolder(unFreezeAssetServlet), "/wallet/unfreezeasset");
context.addServlet(new ServletHolder(withdrawBalanceServlet), "/wallet/withdrawbalance");
context.addServlet(new ServletHolder(withdrawBalanceToServlet), "/wallet/withdrawbalanceTo");
context.addServlet(new ServletHolder(updateAssetServlet), "/wallet/updateasset");
context.addServlet(new ServletHolder(listNodesServlet), "/wallet/listnodes");
context.addServlet(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ public class HttpSelfFormatFieldName {
AddressFieldNameMap.put("protocol.UnfreezeAssetContract.owner_address", 1);
//WithdrawBalanceContract
AddressFieldNameMap.put("protocol.WithdrawBalanceContract.owner_address", 1);
//WithdrawBalanceToContract
AddressFieldNameMap.put("protocol.WithdrawBalanceToContract.owner_address", 1);
AddressFieldNameMap.put("protocol.WithdrawBalanceToContract.receiver_address", 1);
//UpdateAssetContract
AddressFieldNameMap.put("protocol.UpdateAssetContract.owner_address", 1);
//ProposalCreateContract
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.tron.core.services.http;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tron.core.Wallet;
import org.tron.protos.Protocol.Transaction;
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
import org.tron.protos.contract.BalanceContract.WithdrawBalanceToContract;


@Component
@Slf4j(topic = "API")
public class WithdrawBalanceToServlet extends RateLimiterServlet {

@Autowired
private Wallet wallet;

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) {

}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
try {
String contract = request.getReader().lines()
.collect(Collectors.joining(System.lineSeparator()));
Util.checkBodySize(contract);
boolean visible = Util.getVisiblePost(contract);
WithdrawBalanceToContract.Builder build = WithdrawBalanceToContract.newBuilder();
JsonFormat.merge(contract, build, visible);
Transaction tx = wallet
.createTransactionCapsule(build.build(), ContractType.WithdrawBalanceToContract)
.getInstance();
JSONObject jsonObject = JSON.parseObject(contract);
tx = Util.setTransactionPermissionId(jsonObject, tx);
response.getWriter().println(Util.printCreateTransaction(tx, visible));
} catch (Exception e) {
Util.processError(e, response);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ public static long getTransactionAmount(Transaction.Contract contract, String ha
switch (contract.getType()) {
case UnfreezeBalanceContract:
case WithdrawBalanceContract:
case WithdrawBalanceToContract:
case WithdrawExpireUnfreezeContract:
case UnfreezeBalanceV2Contract:
case CancelAllUnfreezeV2Contract:
Expand Down Expand Up @@ -291,6 +292,7 @@ public static long getTransactionAmount(Transaction.Contract contract, String ha
break;
case UnfreezeBalanceContract:
case WithdrawBalanceContract:
case WithdrawBalanceToContract:
case WithdrawExpireUnfreezeContract:
case UnfreezeBalanceV2Contract:
case CancelAllUnfreezeV2Contract:
Expand Down Expand Up @@ -322,6 +324,7 @@ public static long getAmountFromTransactionInfo(String hash, ContractType contra
amount = transactionInfo.getUnfreezeAmount();
break;
case WithdrawBalanceContract:
case WithdrawBalanceToContract:
amount = transactionInfo.getWithdrawAmount();
break;
case ExchangeInjectContract:
Expand Down
Loading
Loading